diff options
author | David Mitchell <davem@iabyn.com> | 2022-05-12 10:43:35 +0100 |
---|---|---|
committer | Steve Hay <steve.m.hay@googlemail.com> | 2023-04-10 11:56:08 +0100 |
commit | 3017a3a26efb5701ab447d5df791efbdb20e2772 (patch) | |
tree | be096dd891d9fff67e2b7da3076ff65381c771f3 | |
parent | 087438c31af9a09867cf323916ec25db60089561 (diff) | |
download | perl-3017a3a26efb5701ab447d5df791efbdb20e2772.tar.gz |
fix panic from eval {} inside /(?{...})/
GH #19680
Normally in code like
eval {.... };
then even if the eval is the last statement in the file or sub, the
OP_LEAVETRY isn't the last op in the execution path: it's followed
by an OP_LEAVE or OP_LEAVESUB or whatever, which will be the op to
resume execution from after an exception is caught.
However, if the eval is the *last* thing within a regex code block:
/(?{ ...; eval {....}; })/
then the op_next pointer of the OP_LEAVETRY op is actually NULL.
This confused S_docatch(), which wrongly assumed that a NULL
PL_restartop indicated that the caught exception should be rethrown,
popping execution back to the outer perl_run() call and hence leading to
the confused panic warning:
"panic: restartop in perl_run"
The fix is to to separate out the "do we need to re-throw" test,
(PL_restartjmpenv != PL_top_env), from the "no more ops so no need to
re-enter the runops loop" test, (!PL_restartop).
(cherry picked from commit 5fd637ce8c4db075c0668d9f9c806f6712cd3c7f)
-rw-r--r-- | pp_ctl.c | 7 | ||||
-rw-r--r-- | t/re/pat_re_eval.t | 11 |
2 files changed, 15 insertions, 3 deletions
@@ -3350,8 +3350,11 @@ S_docatch(pTHX_ Perl_ppaddr_t firstpp) CALLRUNOPS(aTHX); break; case 3: - /* die caught by an inner eval - continue inner loop */ - if (PL_restartop && PL_restartjmpenv == PL_top_env) { + if (PL_restartjmpenv == PL_top_env) { + /* die caught by an inner eval - continue inner loop */ + + if (!PL_restartop) + break; PL_restartjmpenv = NULL; PL_op = PL_restartop; PL_restartop = 0; diff --git a/t/re/pat_re_eval.t b/t/re/pat_re_eval.t index 70b807b9e2..fecec2b543 100644 --- a/t/re/pat_re_eval.t +++ b/t/re/pat_re_eval.t @@ -24,7 +24,7 @@ BEGIN { our @global; -plan tests => 506; # Update this when adding/deleting tests. +plan tests => 507; # Update this when adding/deleting tests. run_tests() unless caller; @@ -1332,6 +1332,15 @@ sub run_tests { pass("SvTEMP 2"); } + # GH #19680 "panic: restartop in perl_run" + # The eval block embedded within the (?{}) - but with no more code + # following it - causes the next op after the OP_LEAVETRY to be NULL + # (not even an OP_LEAVE). This confused the exception-catching and + # rethrowing code: it was incorrectly rethrowing the exception rather + # than just stopping at that point. + + ok("test" =~ m{^ (?{eval {die "boo!"}}) test $}x, "GH #19680"); + } # End of sub run_tests 1; |