Ovid has asked for the wisdom of the Perl Monks concerning the following question:
My module, MooseX::Extended is quickly becoming popular and is now being used in production at some companies. However, one person is reporting intermittent segfaults. This appears to be related to my using B::Hooks::AtRuntime to avoid the need to add __PACKAGE__->meta->make_immutable; to the end of every Moose module. There's not much XS code involved, but my XS knowledge is even worse than my C.
Paul "LeoNerd" Evans commented on IRC:
14:18 LeoNerd: #0 Perl_SvREFCNT_dec_NN (sv=0xa65636e6174736e, my_perl=0x55921df002a0) at inline.h:242 <== that looks very much like a bad sv address
14:20 LeoNerd: Not terribly clear where that comes from.. the next context frame is popeval, which suggests stack unwind. Possibly at this point some accessing of bad memory
14:20 LeoNerd: valgrind might help.
Can anyone with XS knowledge help me? As far as I can tell, the code is still solid for prod. I'm wondering if this has something to do with the effectively random order of global destruction because this is just being triggered by a compilation test. (That's just speculation and could be a red herring).
Note: If anyone else experiences this, the workaround is to simply exclude the automatic immutable behavior and add it manually to your M ooseX::Extended classes.
Re: XS Error: Segfault with B::HooksAtRuntime
by dave_the_m (Monsignor) on Aug 06, 2022 at 15:08 UTC
|
Well the crash is happening at the point where the perl interpreter has just finished executing the body of a require'd file (the require occurring as part of a 'use'). The interpreter is popping the CXt_EVAL context frame off the context stack (which was pushed on when the require'd file was about to be compiled). One step of popping the context is to decrement the recount of a temporary SV pointed to from within the context struct (which happens to hold the name of the file being compiled - or possibly the package name; can't remember which). The pointer stored in the context struct is obviously corrupt - it looks like part of the context struct has been overwritten with the text "nstance", as has been pointed out.
What the cause of this is, I don't know. Given that you're doing weird stuff with delaying execution, my initial instinct was that the context stack pointer has been decremented and then some other code has pushed a new context frame on, overwriting the eval context - all before the interpreter has actually returned from doing the require, However in this case, I would expect a new context frame (even of a different type than CXt_EVAL) to be mainly full of pointers and the like - not full of literal text. So it looks like a deeper problem with a rouge string pointer somewhere.
Dave. | [reply] |
|
use lib '.';
use Demo;
File: Demo.pm
package Demo;
use MooseX::Extended;
require Stuff;
File: Stuff.pm
package Stuff;
use MooseX::Extended;
With this code, perl -wc mx_segfault.pl segfaults, and valgrind throws a 455-line tantrum.
==12066== Memcheck, a memory error detector
==12066== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et
+al.
==12066== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyrigh
+t info
==12066== Command: perl -wc mx_segfault.pl
==12066==
==12066== Invalid read of size 2
==12066== at 0x251CCD: S_pop_eval_context_maybe_croak (in /home/haj
+/perl5/perlbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x25FFA0: Perl_pp_leaveeval (in /home/haj/perl5/perlbr
+ew/perls/perl-5.36.0/bin/perl)
==12066== by 0x213312: Perl_runops_standard (in /home/haj/perl5/per
+lbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x177DFD: Perl_call_sv (in /home/haj/perl5/perlbrew/pe
+rls/perl-5.36.0/bin/perl)
==12066== by 0x17A70D: Perl_call_list (in /home/haj/perl5/perlbrew/
+perls/perl-5.36.0/bin/perl)
==12066== by 0x15E84A: S_process_special_blocks (in /home/haj/perl5
+/perlbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x170805: Perl_newATTRSUB_x (in /home/haj/perl5/perlbr
+ew/perls/perl-5.36.0/bin/perl)
==12066== by 0x1743DA: Perl_utilize (in /home/haj/perl5/perlbrew/pe
+rls/perl-5.36.0/bin/perl)
==12066== by 0x1B000E: Perl_yyparse (in /home/haj/perl5/perlbrew/pe
+rls/perl-5.36.0/bin/perl)
==12066== by 0x17E836: perl_parse (in /home/haj/perl5/perlbrew/perl
+s/perl-5.36.0/bin/perl)
==12066== by 0x155295: main (in /home/haj/perl5/perlbrew/perls/perl
+-5.36.0/bin/perl)
==12066== Address 0x4c2528a is 314 bytes inside a block of size 1,872
+ free'd
==12066== at 0x483AD7B: realloc (vg_replace_malloc.c:834)
==12066== by 0x1F385C: Perl_safesysrealloc (in /home/haj/perl5/perl
+brew/perls/perl-5.36.0/bin/perl)
==12066== by 0x24D3BB: Perl_cxinc (in /home/haj/perl5/perlbrew/perl
+s/perl-5.36.0/bin/perl)
==12066== by 0x21CED4: Perl_pp_entersub (in /home/haj/perl5/perlbre
+w/perls/perl-5.36.0/bin/perl)
==12066== by 0x213312: Perl_runops_standard (in /home/haj/perl5/per
+lbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x25130B: S_docatch (in /home/haj/perl5/perlbrew/perls
+/perl-5.36.0/bin/perl)
==12066== by 0x213312: Perl_runops_standard (in /home/haj/perl5/per
+lbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x177C5A: Perl_call_sv (in /home/haj/perl5/perlbrew/pe
+rls/perl-5.36.0/bin/perl)
==12066== by 0x87D828C: call_after (in /home/haj/perl5/perlbrew/per
+ls/perl-5.36.0/lib/site_perl/5.36.0/x86_64-linux/auto/B/Hooks/AtRunti
+me/AtRuntime.so)
==12066== by 0x24F7A1: Perl_leave_scope (in /home/haj/perl5/perlbre
+w/perls/perl-5.36.0/bin/perl)
==12066== by 0x251CCC: S_pop_eval_context_maybe_croak (in /home/haj
+/perl5/perlbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x25FFA0: Perl_pp_leaveeval (in /home/haj/perl5/perlbr
+ew/perls/perl-5.36.0/bin/perl)
==12066== Block was alloc'd at
==12066== at 0x483877F: malloc (vg_replace_malloc.c:307)
==12066== by 0x1F3502: Perl_safesysmalloc (in /home/haj/perl5/perlb
+rew/perls/perl-5.36.0/bin/perl)
==12066== by 0x24D316: Perl_new_stackinfo (in /home/haj/perl5/perlb
+rew/perls/perl-5.36.0/bin/perl)
==12066== by 0x15E9D6: S_process_special_blocks (in /home/haj/perl5
+/perlbrew/perls/perl-5.36.0/bin/perl)
==12066== by 0x170805: Perl_newATTRSUB_x (in /home/haj/perl5/perlbr
+ew/perls/perl-5.36.0/bin/perl)
==12066== by 0x1743DA: Perl_utilize (in /home/haj/perl5/perlbrew/pe
+rls/perl-5.36.0/bin/perl)
==12066== by 0x1B000E: Perl_yyparse (in /home/haj/perl5/perlbrew/pe
+rls/perl-5.36.0/bin/perl)
==12066== by 0x17E836: perl_parse (in /home/haj/perl5/perlbrew/perl
+s/perl-5.36.0/bin/perl)
==12066== by 0x155295: main (in /home/haj/perl5/perlbrew/perls/perl
+-5.36.0/bin/perl)
| [reply] [d/l] [select] |
|
In general terms, that valgrind output shows that the calling of a sub while running code via call_after() (which requires a new CXt_SUB context sub to be pushed) grows the context stack (by reallocating it). Sometime later when exiting a use/require, the Cxt_EVAL frame is accessed using the old context stack address (the smaller stack that was freed when a larger one was allocated). So maybe something is holding on to a context stack pointer when it shouldn't - either in core or XS.
Dave.
| [reply] |
|
| [reply] |
|
-------------------------
test.pl:
use lib '.';
use Foo;
-------------------------
Foo.pm:
package Foo;
use Bar;
1;
-------------------------
Bar.pm:
package Bar;
use B::Hooks::AtRuntime 'after_runtime';
sub recurse {
my $depth = shift;
return if $depth < 0;
recurse($depth -1);
}
sub import {
after_runtime {
recurse(20);
}
}
If this is run against a perl that has been built with -DDEBUGGING, you'll see the following assertion failure, which is Perl_cx_popeval() detecting that the current cx pointer has changed underneath it.
perl: inline.h:2921: Perl_cx_popeval: Assertion `CxTYPE(cx) == CXt_EVA
+L' failed.
Dave. | [reply] [d/l] [select] |
|
Thank you, Dave. Would you be willing to open an issue on this? I would do it myself, but on my Mac, I cannot reproduce this error. I've tried several versions of Perl. perl -V information would be helpful.
| [reply] |
|
Re: XS Error: Segfault with B::HooksAtRuntime
by kikuchiyo (Hermit) on Aug 05, 2022 at 12:01 UTC
|
Not necessarily relevant, but that bad address consists of all lowercase letters ("ecnatsn") apart from the leading 0xa. Looks like gibberish, but it supports the theory of accessing bad memory. | [reply] |
|
$ perl -e'
use DDP print_escapes => 1;
p ${\ pack "J", 0xa65636e6174736e };
'
"nstance\n"
| [reply] [d/l] |
|
|