[prev in list] [next in list] [prev in thread] [next in thread]
List: perl5-porters
Subject: Re: [perl #75436] Segfault with lexical $_ and reverse() since e1f795dc
From: Vincent Pit <perl () profvince ! com>
Date: 2010-05-30 23:11:19
Message-ID: 4C02F097.4020003 () profvince ! com
[Download RAW message or body]
> On 29 May 2010 20:19, Ævar Arnfjörð Bjarmason <perlbug-followup@perl.org> wrote:
>
>> This segfaults in everything from 5.10 to blead:
>>
>> perl -e 'sub eek { my $_ = $_[0]; reverse } eek(1)'
>>
> Thanks for tracing this down.
>
> Some background :
> reverse() without arguments is special, because it defaults to $_ only
> when called in scalar context. That means that, unlike all other
> builtins that default to $_, you can't know at compile time whether it
> will read $_ or not, and you can't put this info in the optree. So you
> have to figure it out later, at runtime.
>
> find_rundefsvoffset() locates a lexical $_ in the pad at runtime;
> however we don't know if it's a "my" or an "our", and in this case the
> segfault is produced in the part of the statement:
> PAD_COMPNAME_FLAGS_isOUR(padoff_du), which doesn't work, because
> apparently it's intended for compile-time, not run-time.
>
That was also my analysis.
I've attached two patches that fix the issue. Both fetch the name sv
associated with $_ from the current run-time pad instead of the comppad.
The former inlines all the new required code in pp_reverse, while the
second one introduces a new find_rundefsv() function that abstracts this
logic. Since I suspect that the UNDERBAR macro is also wrong (it's
deemed to be called at run-time), my preferred solution would be to
apply the second patch, change UNDERBAR to use find_rundefsv(), and
deprecate find_rundefsvoffset() as I don't think it's really useful in
the end.
Vincent.
["reverse_implicit.patch" (text/plain)]
diff --git a/pp.c b/pp.c
index 937fdfd..4d1ff3c 100644
--- a/pp.c
+++ b/pp.c
@@ -5489,19 +5489,27 @@ PP(pp_reverse)
register I32 tmp;
dTARGET;
STRLEN len;
- PADOFFSET padoff_du;
SvUTF8_off(TARG); /* decontaminate */
if (SP - MARK > 1)
do_join(TARG, &PL_sv_no, MARK, SP);
else {
- sv_setsv(TARG, (SP > MARK)
- ? *SP
- : (padoff_du = find_rundefsvoffset(),
- (padoff_du == NOT_IN_PAD
- || PAD_COMPNAME_FLAGS_isOUR(padoff_du))
- ? DEFSV : PAD_SVl(padoff_du)));
-
+ SV *src;
+ if (SP > MARK)
+ src = *SP;
+ else {
+ PADOFFSET padoff_du = find_rundefsvoffset();
+ if (padoff_du == NOT_IN_PAD)
+ src = DEFSV;
+ else {
+ const CV * const cv = find_runcv(NULL);
+ const AV * const padlist = CvPADLIST(cv);
+ const AV * const nameav = MUTABLE_AV(AvARRAY(padlist)[0]);
+ const SV * const namesv = AvARRAY(nameav)[padoff_du];
+ src = ((SvFLAGS(namesv) & (SVpad_NAME|SVpad_OUR)) == (SVpad_NAME|SVpad_OUR)) ? \
DEFSV : PAD_SVl(padoff_du); + }
+ }
+ sv_setsv(TARG, src);
if (! SvOK(TARG) && ckWARN(WARN_UNINITIALIZED))
report_uninit(TARG);
}
diff --git a/t/op/reverse.t b/t/op/reverse.t
index 2fa0877..08521ad 100644
--- a/t/op/reverse.t
+++ b/t/op/reverse.t
@@ -6,7 +6,7 @@ BEGIN {
require './test.pl';
}
-plan tests => 23;
+plan tests => 25;
is(reverse("abc"), "cba");
@@ -91,3 +91,11 @@ use Tie::Array;
my $c = scalar reverse($b);
is($a, $c);
}
+
+{
+ # Lexical $_.
+ sub blurp { my $_ = shift; reverse }
+
+ is(blurp("foo"), "oof");
+ is(sub { my $_ = shift; reverse }->("bar"), "rab");
+}
["reverse_implicit2.patch" (text/plain)]
diff --git a/embed.fnc b/embed.fnc
index 8e463c1..6400f3e 100644
--- a/embed.fnc
+++ b/embed.fnc
@@ -856,6 +856,8 @@ p |PADOFFSET|allocmy |NN const char *const name|const STRLEN len\
: Used in op.c and toke.c
AMpdR |PADOFFSET|pad_findmy |NN const char* name|STRLEN len|U32 flags
Ap |PADOFFSET|find_rundefsvoffset |
+: Used in pp.c
+Ap |SV* |find_rundefsv |
: Used in perly.y
pR |OP* |oopsAV |NN OP* o
: Used in perly.y
diff --git a/embed.h b/embed.h
index 90e8045..588c50a 100644
--- a/embed.h
+++ b/embed.h
@@ -673,6 +673,7 @@
#endif
#define pad_findmy Perl_pad_findmy
#define find_rundefsvoffset Perl_find_rundefsvoffset
+#define find_rundefsv Perl_find_rundefsv
#ifdef PERL_CORE
#define oopsAV Perl_oopsAV
#define oopsHV Perl_oopsHV
@@ -3106,6 +3107,7 @@
#endif
#define pad_findmy(a,b,c) Perl_pad_findmy(aTHX_ a,b,c)
#define find_rundefsvoffset() Perl_find_rundefsvoffset(aTHX)
+#define find_rundefsv() Perl_find_rundefsv(aTHX)
#ifdef PERL_CORE
#define oopsAV(a) Perl_oopsAV(aTHX_ a)
#define oopsHV(a) Perl_oopsHV(aTHX_ a)
diff --git a/global.sym b/global.sym
index 8861fca..5ab0090 100644
--- a/global.sym
+++ b/global.sym
@@ -396,6 +396,7 @@ Perl_ninstr
Perl_op_free
Perl_pad_findmy
Perl_find_rundefsvoffset
+Perl_find_rundefsv
Perl_pad_sv
Perl_reentrant_size
Perl_reentrant_init
diff --git a/pad.c b/pad.c
index 8015154..9078fba 100644
--- a/pad.c
+++ b/pad.c
@@ -705,6 +705,27 @@ Perl_find_rundefsvoffset(pTHX)
}
/*
+ * Returns a lexical $_, if there is one, at run time ; or the global one
+ * otherwise.
+ */
+
+SV *
+Perl_find_rundefsv(pTHX)
+{
+ SV *namesv;
+ int flags;
+ PADOFFSET po;
+
+ po = pad_findlex("$_", find_runcv(NULL), PL_curcop->cop_seq, 1,
+ NULL, &namesv, &flags);
+ if (po == NOT_IN_PAD
+ || (SvFLAGS(namesv) & (SVpad_NAME|SVpad_OUR)) == (SVpad_NAME|SVpad_OUR))
+ return DEFSV;
+
+ return PAD_SVl(po);
+}
+
+/*
=for apidoc pad_findlex
Find a named lexical anywhere in a chain of nested pads. Add fake entries
diff --git a/pp.c b/pp.c
index 937fdfd..2649c7e 100644
--- a/pp.c
+++ b/pp.c
@@ -5489,19 +5489,12 @@ PP(pp_reverse)
register I32 tmp;
dTARGET;
STRLEN len;
- PADOFFSET padoff_du;
SvUTF8_off(TARG); /* decontaminate */
if (SP - MARK > 1)
do_join(TARG, &PL_sv_no, MARK, SP);
else {
- sv_setsv(TARG, (SP > MARK)
- ? *SP
- : (padoff_du = find_rundefsvoffset(),
- (padoff_du == NOT_IN_PAD
- || PAD_COMPNAME_FLAGS_isOUR(padoff_du))
- ? DEFSV : PAD_SVl(padoff_du)));
-
+ sv_setsv(TARG, SP > MARK ? *SP : find_rundefsv());
if (! SvOK(TARG) && ckWARN(WARN_UNINITIALIZED))
report_uninit(TARG);
}
diff --git a/proto.h b/proto.h
index 6ccf19c..c27313c 100644
--- a/proto.h
+++ b/proto.h
@@ -2510,6 +2510,7 @@ PERL_CALLCONV PADOFFSET Perl_pad_findmy(pTHX_ const char* name, STRLEN len, U32
assert(name)
PERL_CALLCONV PADOFFSET Perl_find_rundefsvoffset(pTHX);
+PERL_CALLCONV SV* Perl_find_rundefsv(pTHX);
PERL_CALLCONV OP* Perl_oopsAV(pTHX_ OP* o)
__attribute__warn_unused_result__
__attribute__nonnull__(pTHX_1);
diff --git a/t/op/reverse.t b/t/op/reverse.t
index 2fa0877..08521ad 100644
--- a/t/op/reverse.t
+++ b/t/op/reverse.t
@@ -6,7 +6,7 @@ BEGIN {
require './test.pl';
}
-plan tests => 23;
+plan tests => 25;
is(reverse("abc"), "cba");
@@ -91,3 +91,11 @@ use Tie::Array;
my $c = scalar reverse($b);
is($a, $c);
}
+
+{
+ # Lexical $_.
+ sub blurp { my $_ = shift; reverse }
+
+ is(blurp("foo"), "oof");
+ is(sub { my $_ = shift; reverse }->("bar"), "rab");
+}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic