summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2012-12-07 11:07:30 +0000
committerDavid Mitchell <davem@iabyn.com>2012-12-07 11:07:30 +0000
commiteae139f3f1da0f91ca0fb543c5f5bc3b2b94cbc9 (patch)
treebcb933901688ccab02faa890393d08c5da80d562
parentcf7631f2e53782c31bd8f9b7c65010525a4cdcbc (diff)
downloadperl-eae139f3f1da0f91ca0fb543c5f5bc3b2b94cbc9.tar.gz
[perl #115992] PL_eval_start use-after-free
PL_eval_start is used for two purposes. First, it indicates the start op of a freshly-compiled eval. It is set in newPROG(), and used by entereval etc to know where to begin executing. After execution has begun, its value is meaningless (and may well point to a freed op). Second, it's used as a temporary pointer to indicate, within an assignment to $] (which has been optimised into a const), that it's not to croak in op_lvalue() with "Can't modify constant item", but instead to set CopARYBASE. This second use temporarily sets it in Perl_newASSIGNOP(), which calls op_lvalue(), which uses and then clears it. The issue is that it can also be left set by a previous eval, so something like 'local $[' will see it set and try to use its value. The quickest fix is to just set it NULL directly after each eval where its used. This change has been applied directly to maint-5.14 rather than going via bleed, since the old $[ mechanism was ripped out for 5.15.3.
-rw-r--r--pp_ctl.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/pp_ctl.c b/pp_ctl.c
index cbeeeee137..615b82e904 100644
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -3088,6 +3088,7 @@ Perl_sv_compile_2op_is_broken(pTHX_ SV *sv, OP **startop, const char *code,
CV* runcv = NULL; /* initialise to avoid compiler warnings */
STRLEN len;
bool need_catch;
+ OP* ret;
PERL_ARGS_ASSERT_SV_COMPILE_2OP_IS_BROKEN;
@@ -3182,7 +3183,9 @@ Perl_sv_compile_2op_is_broken(pTHX_ SV *sv, OP **startop, const char *code,
PERL_UNUSED_VAR(newsp);
PERL_UNUSED_VAR(optype);
- return PL_eval_start;
+ ret = PL_eval_start;
+ PL_eval_start = NULL;
+ return ret;
}
@@ -3903,8 +3906,10 @@ PP(pp_require)
encoding = PL_encoding;
PL_encoding = NULL;
- if (doeval(gimme, NULL, NULL, PL_curcop->cop_seq))
+ if (doeval(gimme, NULL, NULL, PL_curcop->cop_seq)) {
op = DOCATCH(PL_eval_start);
+ PL_eval_start = NULL;
+ }
else
op = PL_op->op_next;
@@ -4029,6 +4034,7 @@ PP(pp_entereval)
PUTBACK;
if (doeval(gimme, NULL, runcv, seq)) {
+ OP *ret;
if (was != PL_breakable_sub_gen /* Some subs defined here. */
? (PERLDB_LINE || PERLDB_SAVESRC)
: PERLDB_SAVESRC_NOSUBS) {
@@ -4037,7 +4043,9 @@ PP(pp_entereval)
char *const safestr = savepvn(tmpbuf, len);
SAVEDELETE(PL_defstash, safestr, len);
}
- return DOCATCH(PL_eval_start);
+ ret = DOCATCH(PL_eval_start);
+ PL_eval_start = NULL;
+ return ret;
} else {
/* We have already left the scope set up earlier thanks to the LEAVE
in doeval(). */