Yesterday I was doing a little exploratory surgery on some code when something completely unrelated caught my eye and further exploration uncovered a bug in perl. The essence of the bug is that Perl's local() function doesn't properly localize the attached magic that holds the pos() information, or rather, it fails to restore the magic properly (it resets it instead) after the scope of the local() call.

A simple test case is:$_ = 'n';while(/./g){{local $_}} which gives an infinite loop in 5.00503 and 5.6.0 (and according to chipmunk, in a recent development release as well).

The following 2 snippets bring the problem and its cause to light -- in the first snippet we can see the effect of the bug if we remove

the 'last' statement from the loop (infinite loop results). In the second, we see that perl's implicit localization attaches new magic to the variable and properly restores the old magic after the local() call. These snippets utilize Devel::Peek (standard with 5.6.0) to Dump() internal information about a variable. Both tested with 5.6.0 and 5.00503.

use Devel::Peek; $_ = 'blah'; while(/./g){ Dump $_; foo(); Dump $_; last; } sub foo { local $_ = 'foo'; pos($_) = 2; Dump $_; } __END__ output is: SV = PVMG(0x8102ff8) at 0x80e72ac REFCNT = 1 FLAGS = (SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x80f3400 "blah"\0 CUR = 4 LEN = 5 MAGIC = 0x80f3660 MG_VIRTUAL = &PL_vtbl_mglob MG_TYPE = 'g' MG_LEN = 1 SV = PVMG(0x8103018) at 0x80e7300 REFCNT = 2 FLAGS = (SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x80f2008 "foo"\0 CUR = 3 LEN = 4 MAGIC = 0x80f3660 MG_VIRTUAL = &PL_vtbl_mglob MG_TYPE = 'g' MG_LEN = 2 SV = PVMG(0x8102ff8) at 0x80e72ac REFCNT = 1 FLAGS = (SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x80f3400 "blah"\0 CUR = 4 LEN = 5 MAGIC = 0x80f3660 MG_VIRTUAL = &PL_vtbl_mglob MG_TYPE = 'g' MG_LEN = -1

Above we see that the first Dump() shows that the current pos() (which is indicated by MG_LEN) is 1 (which is where we are in the string at this point). The second Dump() shows that we now have a new SV (the address differs), though the 'magic' address remains the same -- we manually set pos() to 2 here and it shows up fine in the dump. The third Dump() shows the problem, upon restoring $_, the 'magic' is reset to -1 rather than restored to what it was prior to the local() call in foo(). The second snippet utilizes perl's implicit localization:

use Devel::Peek; $_ = 'blah'; while(/./g){ Dump $_; for ('foo'){ # implicit localization of $_ pos($_) = 2; Dump $_; } Dump $_; last; } __END__ output: SV = PVMG(0x8102ff8) at 0x80e72ac REFCNT = 1 FLAGS = (SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x80f4d78 "blah"\0 CUR = 4 LEN = 5 MAGIC = 0x80f35d0 MG_VIRTUAL = &PL_vtbl_mglob MG_TYPE = 'g' MG_LEN = 1 SV = PVMG(0x8103018) at 0x80f90f0 REFCNT = 3 FLAGS = (SMG,POK,READONLY,pPOK) IV = 0 NV = 0 PV = 0x80f38e8 "foo"\0 CUR = 3 LEN = 4 MAGIC = 0x80f1cb0 MG_VIRTUAL = &PL_vtbl_mglob MG_TYPE = 'g' MG_LEN = 2 SV = PVMG(0x8102ff8) at 0x80e72ac REFCNT = 1 FLAGS = (SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x80f4d78 "blah"\0 CUR = 4 LEN = 5 MAGIC = 0x80f35d0 MG_VIRTUAL = &PL_vtbl_mglob MG_TYPE = 'g' MG_LEN = 1

Here we note that in the second Dump() we not only get a new SV but new attached magic address as well. And after the inner loop, the final Dump() shows that we revert to the original $_ SV and its current attached magic is intact (and thus the loops finishes even without the 'last', which I only kept here to minimize the output). Note, in 5.00502 this snippet also fails to restore MG_LEN, but it was fixed in 5.00503.

That pretty much sums up the bug issue. I posted it here so that others could double check the findings and someone active on p5p could submit a bug report (leaving a note here so p5p isn't flooded with a dozen similar reports). I also thought an example of using Devel::Peek might have some value for the monastery.

Something else I'll point out, merely for the curious (like myself) is that local() produces a new SV. I guess it makes sense now that I think about it, but I hadn't really given it any thought before because I don't find myself taking references to localized globals. The perlsub page has this to say about how local saves away the current value:

This operator works by saving the current values of those variables in its argument list on a hidden stack and restoring them upon exiting the block,

I guess mentally I just thought it saved away the data, not the SV. But we can see from the above that it saves away the whole SV and points the localized variable to a new SV in its place, meaning that if we take a reference to a localized global, our reference points to something other than the package global when we leave the local scope:

$main::var = 12; { local $main::var = 10; $main::bar = \$main::var; } $$main::bar = 42; # doesn't affect $main::var print $main::var; # prints: 12

Now, $bar does *not* hold a reference to $main::var, merely to the SV that stood in its place under the local() call. I just thought I'd point this out because it 'looks' like $bar is a reference to the global $var. On the other hand, if you alias $var with a typeglob, the alias mirrors the real variable in and out of local() scopes.


In reply to bug: local() fails to restore pos() magic by danger

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.