summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2022-05-12 10:43:35 +0100
committerSteve Hay <steve.m.hay@googlemail.com>2023-04-10 11:56:08 +0100
commit3017a3a26efb5701ab447d5df791efbdb20e2772 (patch)
treebe096dd891d9fff67e2b7da3076ff65381c771f3
parent087438c31af9a09867cf323916ec25db60089561 (diff)
downloadperl-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.c7
-rw-r--r--t/re/pat_re_eval.t11
2 files changed, 15 insertions, 3 deletions
diff --git a/pp_ctl.c b/pp_ctl.c
index 86f5f2c343..3e657e44b9 100644
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -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;