diff --git a/op.h b/op.h index 0440de81f0..af89b4acfb 100644 --- a/op.h +++ b/op.h @@ -298,9 +298,16 @@ struct pmop { OP * op_code_list; /* list of (?{}) code blocks */ }; +/* The PM_GETRE_raw/PM_SETRE_raw variants get/set the slot without any + * processing or asserts */ #ifdef USE_ITHREADS +#define PM_GETRE_raw(o) (REGEXP*)(PL_regex_pad[(o)->op_pmoffset]) #define PM_GETRE(o) (SvTYPE(PL_regex_pad[(o)->op_pmoffset]) == SVt_REGEXP \ ? (REGEXP*)(PL_regex_pad[(o)->op_pmoffset]) : NULL) + +#define PM_SETRE_raw(o,r) STMT_START { \ + PL_regex_pad[(o)->op_pmoffset] = MUTABLE_SV(r); \ + } STMT_END /* The assignment is just to enforce type safety (or at least get a warning). */ /* With first class regexps not via a reference one needs to assign @@ -315,7 +322,9 @@ struct pmop { PL_regex_pad[(o)->op_pmoffset] = MUTABLE_SV(_pm_setre); \ } STMT_END #else +#define PM_GETRE_raw(o) ((o)->op_pmregexp) #define PM_GETRE(o) ((o)->op_pmregexp) +#define PM_SETRE_raw(o,r) ((o)->op_pmregexp = (r)) #define PM_SETRE(o,r) ((o)->op_pmregexp = (r)) #endif diff --git a/regexec.c b/regexec.c index ab67374830..419b1ec58f 100644 --- a/regexec.c +++ b/regexec.c @@ -11237,8 +11237,9 @@ S_setup_eval_state(pTHX_ regmatch_info *const reginfo) regexp *const rex = ReANY(reginfo->prog); regmatch_info_aux_eval *eval_state = reginfo->info_aux_eval; - eval_state->rex = rex; - eval_state->sv = reginfo->sv; + eval_state->rx = reginfo->prog; + SvREFCNT_inc(eval_state->rx); + eval_state->sv = reginfo->sv; if (reginfo->sv) { /* Make $_ available to executed code. */ @@ -11278,6 +11279,26 @@ S_setup_eval_state(pTHX_ regmatch_info *const reginfo) } #endif } + + /* if we're currently executing a MATCHish op, the only ref to the + * current regex might be from that op. If recursive code called from + * (?{...}) recompiles that regex, the old regex will be lost - + * meaning that $1 etc will stuff refer to the value from the inner + * match. So if possible restore the PMOPs regex to the outer value at + * the end of the outer match */ + if ( PL_op + && (PL_opargs[PL_op->op_type] & OA_CLASS_MASK) == OA_PMOP + && PM_GETRE((PMOP*)PL_op)) + { + eval_state->old_op = (PMOP*)PL_op; + eval_state->old_op_val = PM_GETRE((PMOP*)PL_op); + SvREFCNT_inc(eval_state->old_op_val); + } + else + eval_state->old_op = NULL; + + eval_state->old_regcurpm_val = PM_GETRE_raw(PL_reg_curpm); + SvREFCNT_inc(eval_state->old_regcurpm_val); S_set_reg_curpm(aTHX_ reginfo->prog, reginfo); eval_state->curpm = PL_curpm; PL_curpm_under = PL_curpm; @@ -11320,7 +11341,7 @@ S_cleanup_regmatch_info_aux(pTHX_ void *arg) /* undo the effects of S_setup_eval_state() */ if (eval_state->subbeg) { - regexp * const rex = eval_state->rex; + regexp * const rex = ReANY(eval_state->rx); RXp_SUBBEG(rex) = eval_state->subbeg; RXp_SUBLEN(rex) = eval_state->sublen; RXp_SUBOFFSET(rex) = eval_state->suboffset; @@ -11340,6 +11361,17 @@ S_cleanup_regmatch_info_aux(pTHX_ void *arg) PL_curpm = eval_state->curpm; SvREFCNT_dec(eval_state->sv); + SvREFCNT_dec(eval_state->rx); + + REGEXP *old_rx = PM_GETRE(PL_reg_curpm); + PM_SETRE_raw(PL_reg_curpm, eval_state->old_regcurpm_val); + SvREFCNT_dec(old_rx); + + if (eval_state->old_op) { + old_rx = PM_GETRE(eval_state->old_op); + PM_SETRE(eval_state->old_op, eval_state->old_op_val); + SvREFCNT_dec(old_rx); + } } PL_regmatch_state = aux->old_regmatch_state; diff --git a/regexp.h b/regexp.h index 1f7d6e7950..78c70a1d36 100644 --- a/regexp.h +++ b/regexp.h @@ -776,7 +776,10 @@ struct regmatch_slab; * regmatch_state stack at the start of execution */ typedef struct { - regexp *rex; + REGEXP *rx; + PMOP *old_op; /* saved value of PL_op and ... */ + REGEXP *old_op_val; /* ... saved value of PM_GETRE(PL_op) if any */ + REGEXP *old_regcurpm_val; /* saved value of PM_GETRE(PL_reg_curpm) */ PMOP *curpm; /* saved PL_curpm */ #ifdef PERL_ANY_COW SV *saved_copy; /* saved saved_copy field from rex */ diff --git a/t/re/pat_re_eval.t b/t/re/pat_re_eval.t index a7351751b7..8193eea5cc 100644 --- a/t/re/pat_re_eval.t +++ b/t/re/pat_re_eval.t @@ -25,7 +25,7 @@ BEGIN { our @global; -plan tests => 527; # Update this when adding/deleting tests. +plan tests => 528; # Update this when adding/deleting tests. run_tests() unless caller; @@ -1409,6 +1409,43 @@ sub run_tests { ok("" =~ m{^ (?{eval q{$x=}})}x, "GH #19390"); + # GH #22869 "Perl crash with recursive sub and regex with code eval". + # + # A recursive call to a match op with a run-time pattern and which + # contained a code block, led to to the temporary rex stored in the + # OP_MATCH and PL_reg_curpm ops getting prematurely freed when updated + # within the inner match's OP_MATCH op. + + { + my @got; + + my $f = sub { + my ($s, $re) = @_; + $s =~ $re; + push @got, ',', $1, $2, ']'; + }; + + my $pat; + $pat = qr{^ + (.) + (?{ + push @got, '[', $1, $2; + $f->('XY', $pat) if $1 eq 'A'; + push @got, ',', $1, $2; + }) + (.) + (?{ + push @got, ',', $1, $2; + }) + $ + }x; + + $f->('AB',$pat); + + my $got = join '', map defined ? $_ : '-', @got; + is($got, "[A-[X-,X-,XY,XY],A-,AB,AB]", "GH22869"); + } + } # End of sub run_tests 1;