Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions src/pcre2_match.c
Original file line number Diff line number Diff line change
Expand Up @@ -932,28 +932,32 @@ match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket, PCRE2
where we know the starting frame is at the top of the chained frames, in
this case we have to search back for the relevant frame in case other types
of group that use chained frames have intervened. Multiple OP_CLOSEs always
come innermost first, which matches the chain order. We can ignore this in
a recursion, because captures are not passed out of recursions. */
come innermost first, which matches the chain order.

In a recursion, the closed groups may be returned to the caller by a
capture list. Only groups that were opened within the recursion are closed,
so the search stops at the recursion's frame. */

case OP_CLOSE:
if (Fcurrent_recurse == RECURSE_UNSET)
number = GET2(Fecode, 1);
offset = Flast_group_offset;
for (;;)
{
number = GET2(Fecode, 1);
offset = Flast_group_offset;
for (;;)
{
/* Corrupted heapframes?. Trigger an assert and return an error */
PCRE2_ASSERT(offset != PCRE2_UNSET);
if (offset == PCRE2_UNSET)
return PCRE2_ERROR_INTERNAL;
/* Corrupted heapframes?. Trigger an assert and return an error */
PCRE2_ASSERT(offset != PCRE2_UNSET);
if (offset == PCRE2_UNSET)
return PCRE2_ERROR_INTERNAL;

N = (heapframe *)((char *)match_data->heapframes + offset);
P = (heapframe *)((char *)N - frame_size);
if (N->group_frame_type == (GF_CAPTURE | number))
break;
offset = P->last_group_offset;
}
N = (heapframe *)((char *)match_data->heapframes + offset);
P = (heapframe *)((char *)N - frame_size);
if (N->group_frame_type == (GF_CAPTURE | number) ||
GF_IDMASK(N->group_frame_type) == GF_RECURSE)
break;
offset = P->last_group_offset;
}

if (N->group_frame_type == (GF_CAPTURE | number))
{
offset = (number << 1) - 2;
Fcapture_last = number;
Fovector[offset] = P->eptr - mb->start_subject;
Expand Down Expand Up @@ -1005,13 +1009,27 @@ match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket, PCRE2
/* N is now the frame of the recursion; the previous frame is at the
OP_RECURSE position. Go back there, copying the current subject position
and mark, and the start_match position (\K might have changed it), and
then move on past the OP_RECURSE. */
then move on past the OP_RECURSE.

If the OP_RECURSE is followed by a list of capture groups to return,
build the caller's captures in the current frame, as is done when the
end of the recursion is reached, and copy them into the previous frame.
This also moves past the OP_CREF list. */

P->eptr = Feptr;
P->mark = Fmark;
P->start_match = Fstart_match;

Fecode = P->ecode + 1 + LINK_SIZE;
if (*Fecode == OP_CREF)
{
recurse_update_offsets(F, P);
memcpy(P->ovector, F->ovector, Foffset_top * sizeof(PCRE2_SIZE));
P->offset_top = Foffset_top;
}
P->ecode = Fecode;

F = P;
Fecode += 1 + LINK_SIZE;
continue;
}

Expand Down
51 changes: 51 additions & 0 deletions testdata/testinput2
Original file line number Diff line number Diff line change
Expand Up @@ -8275,6 +8275,57 @@ a)"xI
/(?(DEFINE)((Sat)(urday)))(?1(2,3)),\2,\3/
Saturday,Sat,urday

# (*ACCEPT) in a called group must return the listed captures to the caller

/(?&r(2))b(?(DEFINE)(?<r>(a)(*ACCEPT)x))/
ab

/(a)(?2(3))\1\3(?(DEFINE)(b(c)(*ACCEPT)x))/
abcac
\= Expect no match
abcab

/^(?1(2,3))(?(DEFINE)((a)(*ACCEPT)(b)))/
ab

/(?1(2))(?1(3))(?(DEFINE)((a)(*ACCEPT)|(b)))!/
ab!

/(?(R)(a)(*ACCEPT)x|(?R(1))b\1)/
aba
\= Expect no match
abx

/(?1(2))c(?(DEFINE)((a)(*ACCEPT)|(ab)))/
ac
\= Expect no match
abc

# Groups that are still open at (*ACCEPT) in a called group are closed before
# the listed captures are returned, but groups opened outside the called group
# are not. JIT does not yet close the open groups.

/(*NO_JIT)^(?&r(2))b\2$(?(DEFINE)(?<r>(a(*ACCEPT)x)))/
aba
\= Expect no match
abb

/(*NO_JIT)^(?&r(2,3))\2\3$(?(DEFINE)(?<r>(a(b(*ACCEPT)c))))/
ababb

/(*NO_JIT)^(?&r(3))\3(?(2)x|y)$(?(DEFINE)(?<r>(a(b(*ACCEPT)c))))/
abby
\= Expect no match
abbx

/(*NO_JIT)(?(R)(a(*ACCEPT)x)|(?R(1))b\1)/
aba

/^(x(?<r>a(*ACCEPT)b){0}(?&r(1))(?(1)y|z))/
xaz
\= Expect no match
xay

# --------------

# Test that a memory leak has been fixed
Expand Down
91 changes: 91 additions & 0 deletions testdata/testoutput2
Original file line number Diff line number Diff line change
Expand Up @@ -23617,6 +23617,97 @@ Failed: error 217 at offset 27: expected capture group number or name
2: Sat
3: urday

# (*ACCEPT) in a called group must return the listed captures to the caller

/(?&r(2))b(?(DEFINE)(?<r>(a)(*ACCEPT)x))/
ab
0: ab
1: <unset>
2: a

/(a)(?2(3))\1\3(?(DEFINE)(b(c)(*ACCEPT)x))/
abcac
0: abcac
1: a
2: <unset>
3: c
\= Expect no match
abcab
No match

/^(?1(2,3))(?(DEFINE)((a)(*ACCEPT)(b)))/
ab
0: a
1: <unset>
2: a

/(?1(2))(?1(3))(?(DEFINE)((a)(*ACCEPT)|(b)))!/
ab!
0: ab!
1: <unset>
2: a
3: b

/(?(R)(a)(*ACCEPT)x|(?R(1))b\1)/
aba
0: aba
1: a
\= Expect no match
abx
No match

/(?1(2))c(?(DEFINE)((a)(*ACCEPT)|(ab)))/
ac
0: ac
1: <unset>
2: a
\= Expect no match
abc
No match

# Groups that are still open at (*ACCEPT) in a called group are closed before
# the listed captures are returned, but groups opened outside the called group
# are not. JIT does not yet close the open groups.

/(*NO_JIT)^(?&r(2))b\2$(?(DEFINE)(?<r>(a(*ACCEPT)x)))/
aba
0: aba
1: <unset>
2: a
\= Expect no match
abb
No match

/(*NO_JIT)^(?&r(2,3))\2\3$(?(DEFINE)(?<r>(a(b(*ACCEPT)c))))/
ababb
0: ababb
1: <unset>
2: ab
3: b

/(*NO_JIT)^(?&r(3))\3(?(2)x|y)$(?(DEFINE)(?<r>(a(b(*ACCEPT)c))))/
abby
0: abby
1: <unset>
2: <unset>
3: b
\= Expect no match
abbx
No match

/(*NO_JIT)(?(R)(a(*ACCEPT)x)|(?R(1))b\1)/
aba
0: aba
1: a

/^(x(?<r>a(*ACCEPT)b){0}(?&r(1))(?(1)y|z))/
xaz
0: xaz
1: xaz
\= Expect no match
xay
No match

# --------------

# Test that a memory leak has been fixed
Expand Down