develooper Front page | perl.perl5.porters | Postings from December 2016

Re: [perl #130318] segfault in Perl_mg_magical (mg.c:144)

Thread Previous | Thread Next
From:
Zefram
Date:
December 27, 2016 11:24
Subject:
Re: [perl #130318] segfault in Perl_mg_magical (mg.c:144)
Message ID:
20161227112430.GR6507@fysh.org
demerphq wrote:
>                                                            My
>understanding is it is rare to unheard of for real code to tickle
>stack-not-refcounted bugs, it only happens when people do crazy stuff
>that nobody would do in real code anyway.

On the contrary, stack refcounting bugs have been encountered in real,
organic code on several occasions.  Look at the tickets in RT under the
stack refcounting meta ticket.

>                   but why do we see 'D', 'P', 'F', 'Q', 'P', 'Q'. I
>would expect 'D', UNKNOWN, 'F', UNKNOWN, 'P', 'Q'.

The memory used by the prematurely-freed 'E' and 'G' was reused to store
the 'P' and 'Q', as you can see from the Dump output.  They had been
UNKNOWN for a brief intermediate period, from the premature free to
the reuse.  This failure mode is normal and expected: it's an inherent
possibility for free-too-soon bugs.

>Also why is it when we change it to
...
>Why do we get:

More memory reuse, transforming the prematurely-freed 'E' into $^H{M},
and by chance not reusing the prematurely-freed 'G'.  Unsurprising.

>And why is it that when I remove the Dump we end up with a segfault
>which we dont see with the Dump call?

The segv arises from sv_clear() getting called on an UNKNOWN, which
should never happen.  An assertion fails in a debugging build, causing
an abort.  In a non-debugging build (not checking the assertions),
the SV type range checks have the effect that sv_clear() believes the
SV must have an SvMAGIC() field.  Attempting to read that yields a wild
pointer, because the field was never allocated, so the code walking the
magic chain quickly gets to unmapped memory.

This too is an unsurprising failure mode.  Generally, premature freeing
has effects that amount to memory corruption, and all the resulting
failure modes are in operation.  There's no effective way to constrain
the process once corruption has set in.

As for why removing the Dump() specifically invokes this failure mode,
I don't care to investigate in such detail.  It suffices to chalk it up
to chance: once the memory is corrupted, different op sequences result
in different erroneous outcomes, in a manner that is difficult and
unrewarding to predict.

>How does the return of the the map end up polluting the stack that we
>are processing?

It doesn't.  The corruption initially results from the premature freeing
of SV structures referenced by the stack, while the stack content is being
established before the map iteration is invoked.  It's really early on,
and from that point onwards the perl runtime is doomed.  The exact effects
depend on what operations are performed on the corrupted SVs and what the
SV structures have been overwritten with by the time of those operations.
Both of these are influenced by the code within the map block: that code
performs operations on whatever's referenced by the stack, and it can
by chance reallocate the freed memory, with the effect of overwriting
the corrupted SV structures repeatedly.

-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