diff options
author | David Mitchell <davem@iabyn.com> | 2016-06-30 10:56:28 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2016-07-01 09:35:14 +0100 |
commit | 214949f5cdc4164f25e32c1a6ce989286456c205 (patch) | |
tree | 2ddcb962ab13d2b62b4a0dd6a47666093fef5858 /pp_ctl.c | |
parent | 8c86f0238ecb5f32c2e7fba36e3edfdb54069068 (diff) | |
download | perl-214949f5cdc4164f25e32c1a6ce989286456c205.tar.gz |
FREETMPS when leaving eval, even when void/dying
When a scope is exited normally (e.g. pp_leavetry, pp_leavesub),
we do a FREETMPS only in scalar or list context; in void context
we don't bother for efficiency reasons. Similarly, when there's an
exception and we unwind to (and then pop) an EVAL context, we haven't
been bothering to FREETMPS.
The problem with this in try/eval (exiting normally or via an exception)
is that it can delay some SVs getting freed until *after* $@ has been
set. If that freeing calls a destructor which happens to set $@,
then that overwrites the "real" value of $@.
For example
sub DESTROY { eval { die "died in DESTROY"; } }
eval { bless []; };
is ($@, "");
Before this commit, that test would fail because $@ is "died in DESTROY".
This commit ensures that leaving an eval/try by whatever means always
clears the tmps stack before setting $@.
See http://nntp.perl.org/group/perl.perl5.porters/237380.
For now, I haven't added a FREETMPS to the other pp_leavefoo()
void context cases, since I can't think of a case where it would
matter.
Diffstat (limited to 'pp_ctl.c')
-rw-r--r-- | pp_ctl.c | 38 |
1 files changed, 34 insertions, 4 deletions
@@ -1598,7 +1598,7 @@ Perl_qerror(pTHX_ SV *err) static void S_pop_eval_context_maybe_croak(pTHX_ PERL_CONTEXT *cx, SV *errsv, int action) { - SV *namesv; + SV *namesv = NULL; /* just to silence compiler warnings */ bool do_croak; CX_LEAVE_SCOPE(cx); @@ -1654,7 +1654,13 @@ Perl_die_unwind(pTHX_ SV *msv) if (in_eval) { I32 cxix; - exceptsv = sv_2mortal(SvREFCNT_inc_simple_NN(exceptsv)); + /* We need to keep this SV alive through all the stack unwinding + * and FREETMPSing below, while ensuing that it doesn't leak + * if we call out to something which then dies (e.g. sub STORE{die} + * when unlocalising a tied var). So we do a dance with + * mortalising and SAVEFREEing. + */ + sv_2mortal(SvREFCNT_inc_simple_NN(exceptsv)); /* * Historically, perl used to set ERRSV ($@) early in the die @@ -1723,6 +1729,24 @@ Perl_die_unwind(pTHX_ SV *msv) restartjmpenv = cx->blk_eval.cur_top_env; restartop = cx->blk_eval.retop; + + /* We need a FREETMPS here to avoid late-called destructors + * clobbering $@ *after* we set it below, e.g. + * sub DESTROY { eval { die "X" } } + * eval { my $x = bless []; die $x = 0, "Y" }; + * is($@, "Y") + * Here the clearing of the $x ref mortalises the anon array, + * which needs to be freed *before* $& is set to "Y", + * otherwise it gets overwritten with "X". + * + * However, the FREETMPS will clobber exceptsv, so preserve it + * on the savestack for now. + */ + SAVEFREESV(SvREFCNT_inc_simple_NN(exceptsv)); + FREETMPS; + /* now we're about to pop the savestack, so re-mortalise it */ + sv_2mortal(SvREFCNT_inc_simple_NN(exceptsv)); + /* Note that unlike pp_entereval, pp_require isn't supposed to * trap errors. So if we're a require, after we pop the * CXt_EVAL that pp_require pushed, rethrow the error with @@ -4305,8 +4329,11 @@ PP(pp_leaveeval) ? SvTRUE(*PL_stack_sp) : PL_stack_sp > oldsp); - if (gimme == G_VOID) + if (gimme == G_VOID) { PL_stack_sp = oldsp; + /* free now to avoid late-called destructors clobbering $@ */ + FREETMPS; + } else leave_adjust_stacks(oldsp, oldsp, gimme, 0); @@ -4395,8 +4422,11 @@ PP(pp_leavetry) oldsp = PL_stack_base + cx->blk_oldsp; gimme = cx->blk_gimme; - if (gimme == G_VOID) + if (gimme == G_VOID) { PL_stack_sp = oldsp; + /* free now to avoid late-called destructors clobbering $@ */ + FREETMPS; + } else leave_adjust_stacks(oldsp, oldsp, gimme, 1); CX_LEAVE_SCOPE(cx); |