develooper Front page | perl.perl5.porters | Postings from January 2017

Re: [perl #129068] SV *Perl_cv_const_sv_or_av(const CV *const):Assertion `((svtype)((cv)->sv_flags & 0xff)) == SVt_PVCV ||((svtype)((cv)->sv_flags & 0xff)) == SVt_PVFM' failed (op.c:7926)

Thread Previous | Thread Next
From:
Zefram
Date:
January 29, 2017 02:27
Subject:
Re: [perl #129068] SV *Perl_cv_const_sv_or_av(const CV *const):Assertion `((svtype)((cv)->sv_flags & 0xff)) == SVt_PVCV ||((svtype)((cv)->sv_flags & 0xff)) == SVt_PVFM' failed (op.c:7926)
Message ID:
20170129022731.GA10905@fysh.org
Brian Carpenter wrote:
>./perl -e 'my __PACKAGE__(&p0000;0;p0000'

Reduces to 'my main(&z;0;z'.  This failure mode raises wider issues that
need to be decided in order to determine how to fix it.  It's all about
how my(...) lists are parsed.

Where a single item is being lexicalised, as in "my $x", the item is
syntactically required to be a scalar, array, or hash.  Thus "my &z"
is rejected early on.  But where a parenthesised list of items is being
lexicalised, the syntax permits the parens to contain any expression
whatsoever.  The restriction on what can be lexicalised is instead
implemented by walking the optree of the completed list, checking that
it semantically only contains acceptable items.  Hence this difference
in diagnostics:

$ ./perl -le 'my &z'
syntax error at -e line 1, near "my &z"
Execution of -e aborted due to compilation errors.
$ ./perl -le 'my(&z)'
Can't declare subroutine entry in "my" at -e line 1, at EOF
Execution of -e aborted due to compilation errors.

Actually it's a little more complicated because things like "my $$p"
are syntactically valid but subject to the same sematic check.  I haven't
managed to do anything really interesting with that, so I won't consider
it further.

If the list is well behaved, the difference in the mode of checking
doesn't matter.  But it's possible for a carefully crafted list to
sneak contraband past the semantic check, where the syntactic check
would have caught it.  The danger in skipping the check is that things
that were syntactically mentioned in the list are declared as lexicals
even if they are not valid for this purpose.  They get added to the pad
optimistically, by the lexer, while in a my(...) list, with the lexer
relying on the parser's checks to reject invalid stuff before any code
can be affected by the declaration.  In particular, "&z" is good enough
to get this provisional declaration behaviour, even though it's never
valid in this kind of "my" expression.

In the case with which this ticket is concerned (and using my minimised
form of it), the partial list "(&z" is enough to get a pad entry named
"&z".  Omitting the closing paren succeeds in skipping the semantic check,
because the list is never syntactically complete.  The later instance of
"z" is then looked up in the pad, which is a problem because it's not a
fully-formed lexical sub.  The lexer wants to see whether it's a constant
sub, but it picks up the type-constraining stash instead of an actual CV,
leading to the assertion failure.  This failure mode is rather tricky,
because skipping the semantic check came at the cost of creating a syntax
error, so the details are tied up in the parser's error recovery.

A more enlightening way to skip the semantic check is to use a
conditional expression with constant condition.  The false branch of the
conditional doesn't appear in the optree, and so doesn't get checked for
acceptability, but the lexer saw it.  Putting aside the lexical-sub case
for a minute, consider what mischief we can get up to with just scalars.
What should this program output:

    sub foo {
	my (1 ? $x : $y);
	$y++;
	print $y;
    }
    $y = 5;
    foo; foo;

If the lexical declaration were merely "my ($x)" then all instances of
$y would refer to the package variable, and we'd get "67".  If it were
"my ($x, $y)" then we'd get "11", incrementing $y from undef afresh on
each call.  What we actually get is "12".  The "my" has the effect of
declaring $y as a lexical, so the later uses of $y in the sub refer to
the lexical.  But with $y having been elided from the optree of the "my"
expression, it doesn't get reset for each call.  The lexical declaration
acts as "my $x; my $y if 0", but doesn't get the deprecation warning that
"if 0" provokes.

The deparser doesn't handle this situation.  It goes by the optree, and
so the "my (1 ? $x : $y)" is emitted as "my $x", but the later references
in the sub to the lexical $y are still emitted as "$y".  Compiling the
resulting code loses the lexicalness of $y.  The same problem happens
with "my $y if 0", except that in that case the deparser emits "'???'",
at least showing that something was optimised out.

Getting back to "&z", we can use the conditional to evoke the same
failure mode without a syntax error:

$ perl -le 'my main (1 ? $x : &z); z'
perl: op.c:8062: Perl_cv_const_sv_or_av: Assertion `((svtype)((cv)->sv_flags & 0xff)) == SVt_PVCV || ((svtype)((cv)->sv_flags & 0xff)) == SVt_PVFM' failed.
Abort

We can also get a related failure by omitting the type restriction:

$ perl -le 'my (1 ? $x : &z); z'
perl: pp.c:183: Perl_pp_clonecv: Assertion `protocv' failed.
Abort

There are even failures without any subsequent reference to the broken
lexical sub:

$ perl -le 'my (1 ? $x : &z);'
perl: pp.c:183: Perl_pp_clonecv: Assertion `protocv' failed.
Abort
$ perl -le 'my main (1 ? $x : &z);'
Segmentation fault

Note that the latter SEGVs even in a debugging build, rather than
asserting.

We need to decide how to treat these conditionals in "my" lists.
Determine it for the (relatively) easy cases, and that will point to
how to handle the tricky case with which this ticket started.

-zefram

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About