Branch: refs/heads/yves/fix_20396 Home: https://github.com/Perl/perl5 Commit: 5482530f849016385781df6c3086c276cf60b813 https://github.com/Perl/perl5/commit/5482530f849016385781df6c3086c276cf60b813 Author: Yves Orton <demer...@gmail.com> Date: 2022-10-18 (Tue, 18 Oct 2022)
Changed paths: M perl.c M pp_ctl.c M t/op/eval.t Log Message: ----------- 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. Commit: 3cc030d99f9b22dcb55cb227ba6017b3f1a1ce14 https://github.com/Perl/perl5/commit/3cc030d99f9b22dcb55cb227ba6017b3f1a1ce14 Author: Yves Orton <demer...@gmail.com> Date: 2022-10-18 (Tue, 18 Oct 2022) Changed paths: M pp_ctl.c Log Message: ----------- pp_ctl.c - in try_run_unitcheck() guard against leaking PL_restartop If we die while executing a UNITCHECK inside of an eval we shouldn't leave PL_restartop set, as we will execute PL_op->op_next anyway. I couldn't get anything to SEGV with code related to this, but I could get asserts() to trip to show the leaking PL_restartop. This maintains equivalence with try_yyparse() which does need it for sure. Compare: https://github.com/Perl/perl5/compare/78bb4e5a6f20...3cc030d99f9b