Skip to content

Commit a2beb71

Browse files
committed
pp_ctl.c - in try_yyparse do not leak PL_restartop from compile that dies
Fix GH Issue #20396, try_yyparse() breaks Attribute::Handlers. Reduced test case is: perl -e'CHECK { eval "]" }' which should not assert or segfault. In c304acb we made it so that when doeval_compile() is executed and it calls yyparse() inside of an eval any exceptions that occur during the parse process are trapped by try_yyparse() so that exection would return to doeval_compile(). This was done so that post eval compilation cleanup logic could be handled similarly regardless of whether Perl_croak() was called or not. However the logic to setup PL_restartop was not adjusted accordingly. The opcode that calls doeval_compile() setups an eval context data before it calls doeval_compile(). This data includes the "retop" which is used to return control to after the eval should it die and is set to the be the evaling opcodes op_next. When Perl_die_unwind() is called it sets PL_restartop to be the "retop" of the of the current eval frame, and then does a longjmp, on the assumption it will end up inside of a "run loop enabled jump enviornment", where it restarts the run loop based on the value of PL_restartop, zeroing it aftewards. After c304acb however, a die inside of try_yyparse the die_unwind returns control back to the try_yyparse, which ignores PL_restartop, and leaves it set. Code then goes through the "compilation failed" branch and execution returns to PL_restartop /anyway/, as PL_op hasn't changed and pp_entereval returns control to PL_op->op_next, which is what we pushed into the eval context anyway for the PL_restartop. The end result of this however is that PL_restartop remains set when we enter perl_run() for the first time. perl_run() is a "run loop enabled jump enviornment" which uses run_body() to do its business, such that when PL_restartop is NULL it executes the just compiled body of the program, and when PL_restartop is not null it assumes it must be in the eval handler from an eval from the main body and it should recontinue. The leaked PL_restartop is thus executed instead of the main program body and things go horribly wrong. This patch changes it so that when try_yyparse traps an exception we restore PL_restartop back to its old value. Same for its partner PL_restartjmpenv. This is fine as they have been set to the values from the beginning of the eval frame which we are part of, which is now over.
1 parent c1d415b commit a2beb71

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

perl.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2701,6 +2701,11 @@ perl_run(pTHXx)
27012701
#ifndef MULTIPLICITY
27022702
PERL_UNUSED_ARG(my_perl);
27032703
#endif
2704+
/* perl_parse() may end up starting its own run loops, which might end
2705+
* up "leaking" PL_restartop from the parse phase into the run phase
2706+
* which then ends up confusing run_body(). This leakage shouldn't
2707+
* happen and if it does its a bug. */
2708+
assert(!PL_restartop);
27042709

27052710
oldscope = PL_scopestack_ix;
27062711
#ifdef VMS

pp_ctl.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,10 +3533,17 @@ Perl_find_runcv_where(pTHX_ U8 cond, IV arg, U32 *db_seqp)
35333533
* 3: yyparse() died
35343534
*/
35353535
STATIC int
3536-
S_try_yyparse(pTHX_ int gramtype)
3536+
S_try_yyparse(pTHX_ int gramtype, OP *caller_op)
35373537
{
3538-
int ret;
3538+
/* if we die during compilation PL_restartop and PL_restartjmpenv
3539+
* will be set by Perl_die_unwind(). We need to restore their values
3540+
* if that happens as they are intended for the case where the code
3541+
* compiles and dies during execution, not where it dies during
3542+
* compilation. */
3543+
JMPENV *restartjmpenv = PL_restartjmpenv;
3544+
OP *restartop = PL_restartop;
35393545
dJMPENV;
3546+
int ret;
35403547

35413548
assert(CxTYPE(CX_CUR()) == CXt_EVAL);
35423549
JMPENV_PUSH(ret);
@@ -3545,6 +3552,11 @@ S_try_yyparse(pTHX_ int gramtype)
35453552
ret = yyparse(gramtype) ? 1 : 0;
35463553
break;
35473554
case 3:
3555+
/* yyparse() died and we trapped the error. We need to restore
3556+
* the old PL_restartjmpenv and PL_restartop values. */
3557+
assert(PL_restartop == caller_op->op_next); /* we expect these to match */
3558+
PL_restartjmpenv = restartjmpenv;
3559+
PL_restartop = restartop;
35483560
break;
35493561
default:
35503562
JMPENV_POP;
@@ -3724,19 +3736,23 @@ S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh)
37243736
* always handle that case. */
37253737
assert(!CATCH_GET);
37263738
/* compile the code */
3739+
3740+
37273741
yystatus = (!in_require)
3728-
? S_try_yyparse(aTHX_ GRAMPROG)
3742+
? S_try_yyparse(aTHX_ GRAMPROG, saveop)
37293743
: yyparse(GRAMPROG);
37303744

37313745
if (yystatus || PL_parser->error_count || !PL_eval_root) {
37323746
PERL_CONTEXT *cx;
37333747
SV *errsv;
37343748

37353749
PL_op = saveop;
3736-
/* note that if yystatus == 3, then the require/eval died during
3737-
* compilation, so the EVAL CX block has already been popped, and
3738-
* various vars restored */
37393750
if (yystatus != 3) {
3751+
/* note that if yystatus == 3, then the require/eval died during
3752+
* compilation, so the EVAL CX block has already been popped, and
3753+
* various vars restored. This block applies similar steps after
3754+
* the other "failed to compile" cases in yyparse, eg, where
3755+
* yystatus=1, "failed, but did not die". */
37403756
if (PL_eval_root) {
37413757
op_free(PL_eval_root);
37423758
PL_eval_root = NULL;

t/op/eval.t

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ BEGIN {
66
set_up_inc('../lib');
77
}
88

9-
plan(tests => 152);
9+
plan(tests => 160);
1010

1111
eval 'pass();';
1212

@@ -723,10 +723,17 @@ pass("eval in freed package does not crash");
723723
'eval "UNITCHECK { eval q(UNITCHECK { die; }); print q(A-) }";',
724724
'eval "UNITCHECK { eval q(BEGIN { die; }); print q(A-) }";',
725725
'eval "BEGIN { eval q(UNITCHECK { die; }); print q(A-) }";',
726+
'CHECK { eval "]" } print q"A-";',
727+
'INIT { eval "]" } print q"A-";',
728+
'UNITCHECK { eval "]" } print q"A-";',
729+
'BEGIN { eval "]" } print q"A-";',
726730
) {
727731
fresh_perl_is($line . ' print "ok";', "A-ok", {}, "No segfault: $line");
728-
my $sort_line= 'my @x= sort { ' . $line . ' } 1,2;';
729-
fresh_perl_is($line . ' print "ok";', "A-ok", {}, "No segfault: $line");
730732

733+
# sort blocks are somewhat special and things that work in normal blocks
734+
# can blow up in sort blocks, so test these constructs specially.
735+
my $sort_line= 'my @x= sort { ' . $line . ' } 1,2;';
736+
fresh_perl_is($sort_line . ' print "ok";', "A-ok", {},
737+
"No segfault inside sort: $sort_line");
731738
}
732739
}

0 commit comments

Comments
 (0)