This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Fix RT #120600: Variable length lookbehind is not variable
authorYves Orton <demerphq@gmail.com>
Fri, 22 Nov 2013 00:08:39 +0000 (01:08 +0100)
committerYves Orton <demerphq@gmail.com>
Fri, 22 Nov 2013 00:30:55 +0000 (01:30 +0100)
commit099ec7dcf9e085a650e6d9010c12ad9649209bf4
tree174cacab9f808e6f3b8ec67298380393dd951044
parent5f74c55e9c98631b1b9adaf27273bb10baf0b172
Fix RT #120600: Variable length lookbehind is not variable

Inside of study_chunk() we have to guard against infinite
recursion with recursive subpatterns. The existing logic
sort of worked, but didn't address all cases properly.

  qr/
    (?<W>a)
    (?<BB>
      (?=(?&W))(?<=(?&W))
    )
    (?&BB)
  /x;

The pattern in the test would fail when the optimizer
was expanding (&BB). When it recursed, it creates a bitmap
for the recursion it performs, it then jumps back to
the BB node and then eventually does the first (&W) call.
At this point the bit for (&W) would be set in the bitmask.
When the recursion for the (&W) exited (fake exit through
the study frame logic) the bit was not /unset/. When the parser
then entered the (&W) again it was treated as a nested and
potentially infinite length pattern.

The fake-recursion in study-chunk made it little less obvious
what was going on in the debug output.

By reorganizing the code and adding logic to unset the bitmap
when exiting this bug was fixed. Unfortunately this also revealed
another little issue with patterns like this:

  qr/x|(?0)/
  qr/(x|(?1))/

which forced the creation of a new bitmask for each branch.
Effectively study_chunk treats each branch as an independent
pattern, so when we are expanding (?1) via the 'x' branch
we dont want that to prevent us from detecting the infinite recursion
in the (?1) branch. If you were to think of trips through study_chunk
as paths, and [] as recursive processing you would get something like:

  BRANCH 'x' END
  BRANCH (?0) [ 'x' END ]
  BRANCH (?0) [ (?0) [ 'x' END ] ]
  ...

When we want something like:

  BRANCH 'x' END
  BRANCH (?0) [ 'x' END ]
  BRANCH (?0) [ (?0) INFINITE_RECURSION ]

So when we deal with a branch we need to make a new recursion bitmask.
regcomp.c
t/re/re_tests