diff options
author | Yves Orton <demerphq@gmail.com> | 2022-10-12 18:50:25 +0200 |
---|---|---|
committer | Yves Orton <demerphq@gmail.com> | 2022-10-24 14:33:55 +0200 |
commit | dd66b1d793c73fea9309c1d12a879369bf55bb83 (patch) | |
tree | ab0b36a53a25e4a41f5b385402fecbac9765d03a /perl.c | |
parent | c8b9c03cda0319a9e817d1723e0f641f48a3a114 (diff) | |
download | perl-dd66b1d793c73fea9309c1d12a879369bf55bb83.tar.gz |
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 c304acb49 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 c304acb49 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.
Diffstat (limited to 'perl.c')
-rw-r--r-- | perl.c | 5 |
1 files changed, 5 insertions, 0 deletions
@@ -2701,6 +2701,11 @@ perl_run(pTHXx) #ifndef MULTIPLICITY PERL_UNUSED_ARG(my_perl); #endif + /* perl_parse() may end up starting its own run loops, which might end + * up "leaking" PL_restartop from the parse phase into the run phase + * which then ends up confusing run_body(). This leakage shouldn't + * happen and if it does its a bug. */ + assert(!PL_restartop); oldscope = PL_scopestack_ix; #ifdef VMS |