diff options
author | David Mitchell <davem@iabyn.com> | 2022-07-06 11:14:22 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2022-07-06 11:23:56 +0100 |
commit | 58cf04199f69d7a775bc88024df0bbb48712ea37 (patch) | |
tree | f2bc81d1e542b17668b5b4ad6bc7493295f27378 /pp_ctl.c | |
parent | 768686e95ac39587f0b8106b607659c4c66bb3f4 (diff) | |
download | perl-58cf04199f69d7a775bc88024df0bbb48712ea37.tar.gz |
goto(&xs_sub): provide correct caller context
GH #19188
in something like
sub foo { goto &xs_sub }
if the XS subroutine tries to get its caller context using GIMME_V,
it used to always be reported as scalar, since the OP_GOTO is the
current op being executed, and that op is marked as scalar.
The correct answer should be to return the context of the caller of
foo(), but that information is lost as, just before calling the XS sub,
pp_goto pops the CXt_SUB off the context stack which holds the recorded
value of the context of the caller.
The fix in principle is for goto, just before calling the XS sub, to
grab the gimme value from the about-to-be-popped CXt_SUB frame, and set
OP_GOTO op's op_flag field with that gimme value.
However, modifying ops at runtime isn't allowed, as ops are shared
between threads. So instead set up a fake copy of the OP_GOTO and alter
the gimme value in that instead.
I'm pretty confident that on an exception, a LEAVE_SCOPE() will always
be done before the JMPENV_JUMP(), so PL_op will be restored to its
original value *before* the CPU stack is restored to it's old value
(leaving the temporary OP_GOTO as garbage in an abandoned stack frame).
Diffstat (limited to 'pp_ctl.c')
-rw-r--r-- | pp_ctl.c | 15 |
1 files changed, 15 insertions, 0 deletions
@@ -2988,6 +2988,7 @@ PP(pp_goto) const SSize_t items = arg ? AvFILL(arg) + 1 : 0; const bool m = arg ? cBOOL(SvRMAGICAL(arg)) : 0; SV** mark; + UNOP fake_goto_op; ENTER; SAVETMPS; @@ -3024,6 +3025,20 @@ PP(pp_goto) PL_comppad = cx->blk_sub.prevcomppad; PL_curpad = LIKELY(PL_comppad) ? AvARRAY(PL_comppad) : NULL; + /* Make a temporary a copy of the current GOTO op on the C + * stack, but with a modified gimme (we can't modify the + * real GOTO op as that's not thread-safe). This allows XS + * users of GIMME_V to get the correct calling context, + * even though there is no longer a CXt_SUB frame to + * provide that information. + */ + Copy(PL_op, &fake_goto_op, 1, UNOP); + fake_goto_op.op_flags = + (fake_goto_op.op_flags & ~OPf_WANT) + | (cx->blk_gimme & G_WANT); + SAVEOP(); + PL_op = (OP*)&fake_goto_op; + /* XS subs don't have a CXt_SUB, so pop it; * this is a cx_popblock(), less all the stuff we already did * for cx_topblock() earlier */ |