Skip to content

Commit f8def6c

Browse files
committed
avoid double-freeing regex code blocks
RT #130650 heap-use-after-free in S_free_codeblocks When compiling qr/(?{...})/, a reg_code_blocks structure is allocated and various SVs are attached to it. Initially this is set to be freed via a destructor on the savestack, in case of early dying. Later the structure is attached to the compiling regex, and a boolean flag in the structure, 'attached', is set to true to show that the destructor no longer needs to free the struct. However, it is possible to get three orders of destruction: 1) allocate, push destructor, die early 2) allocate, push destructor, attach to regex, die 2) allocate, push destructor, attach to regex, succeed In 2, the regex is freed (via the savestack) before the destructor is called. In 3, the destructor is called, then later the regex is freed. It turns out perl can't currently handle case 2: qr'(?{})\6' Fix this by turning the 'attached' boolean field into an integer refcount, then keep a count of whether the struct is referenced from the savestack and/or the regex. Since it normally has a value of 1 or 2, it's similar to a boolean flag, but crucially it no longer just indicates that the regex has a pointer to it ('attached'), but that at least one of the savestack and regex have a pointer to it. So order of freeing no longer matters. I also updated S_free_codeblocks() so that it nulls out SV pointers in the reg_code_blocks struct before freeing them. This is is generally good practice to avoid double frees, although is probably not needed at the moment.
1 parent dc0dad9 commit f8def6c

File tree

3 files changed

+21
-12
lines changed

3 files changed

+21
-12
lines changed

regcomp.c

+10-10
Original file line numberDiff line numberDiff line change
@@ -6133,10 +6133,13 @@ S_free_codeblocks(pTHX_ struct reg_code_blocks *cbs)
61336133
{
61346134
int n;
61356135

6136-
if (cbs->attached)
6136+
if (--cbs->refcnt > 0)
61376137
return;
6138-
for (n = 0; n < cbs->count; n++)
6139-
SvREFCNT_dec(cbs->cb[n].src_regex);
6138+
for (n = 0; n < cbs->count; n++) {
6139+
REGEXP *rx = cbs->cb[n].src_regex;
6140+
cbs->cb[n].src_regex = NULL;
6141+
SvREFCNT_dec(rx);
6142+
}
61406143
Safefree(cbs->cb);
61416144
Safefree(cbs);
61426145
}
@@ -6148,7 +6151,7 @@ S_alloc_code_blocks(pTHX_ int ncode)
61486151
struct reg_code_blocks *cbs;
61496152
Newx(cbs, 1, struct reg_code_blocks);
61506153
cbs->count = ncode;
6151-
cbs->attached = FALSE;
6154+
cbs->refcnt = 1;
61526155
SAVEDESTRUCTOR_X(S_free_codeblocks, cbs);
61536156
if (ncode)
61546157
Newx(cbs->cb, ncode, struct reg_code_block);
@@ -7168,8 +7171,7 @@ Perl_re_op_compile(pTHX_ SV ** const patternp, int pat_count,
71687171
if (pm_flags & PMf_IS_QR) {
71697172
ri->code_blocks = pRExC_state->code_blocks;
71707173
if (ri->code_blocks)
7171-
/* disarm earlier SAVEDESTRUCTOR_X */
7172-
ri->code_blocks->attached = TRUE;
7174+
ri->code_blocks->refcnt++;
71737175
}
71747176

71757177
{
@@ -19533,10 +19535,8 @@ Perl_regfree_internal(pTHX_ REGEXP * const rx)
1953319535
if (ri->u.offsets)
1953419536
Safefree(ri->u.offsets); /* 20010421 MJD */
1953519537
#endif
19536-
if (ri->code_blocks) {
19537-
ri->code_blocks->attached = FALSE;
19538+
if (ri->code_blocks)
1953819539
S_free_codeblocks(aTHX_ ri->code_blocks);
19539-
}
1954019540

1954119541
if (ri->data) {
1954219542
int n = ri->data->count;
@@ -19764,7 +19764,7 @@ Perl_regdupe_internal(pTHX_ REGEXP * const rx, CLONE_PARAMS *param)
1976419764
reti->code_blocks->cb[n].src_regex = (REGEXP*)
1976519765
sv_dup_inc((SV*)(ri->code_blocks->cb[n].src_regex), param);
1976619766
reti->code_blocks->count = ri->code_blocks->count;
19767-
reti->code_blocks->attached = TRUE;
19767+
reti->code_blocks->refcnt = 1;
1976819768
}
1976919769
else
1977019770
reti->code_blocks = NULL;

regexp.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ struct reg_code_block {
8888
/* array of reg_code_block's plus header info */
8989

9090
struct reg_code_blocks {
91-
bool attached; /* we're attached to a regex (don't need freeing) */
91+
int refcnt; /* we may be pointed to from a regex and from the savestack */
9292
int count; /* how many code blocks */
9393
struct reg_code_block *cb; /* array of reg_code_block's */
9494
};

t/re/pat_re_eval.t

+10-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ BEGIN {
2222
}
2323

2424

25-
plan tests => 528; # Update this when adding/deleting tests.
25+
plan tests => 529; # Update this when adding/deleting tests.
2626

2727
run_tests() unless caller;
2828

@@ -1244,6 +1244,15 @@ sub run_tests {
12441244
pass "RT #129140";
12451245
}
12461246

1247+
# RT #130650 code blocks could get double-freed during a pattern
1248+
# compilation croak
1249+
1250+
{
1251+
# this used to panic or give ASAN errors
1252+
eval 'qr/(?{})\6/';
1253+
like $@, qr/Reference to nonexistent group/, "RT #130650";
1254+
}
1255+
12471256

12481257
} # End of sub run_tests
12491258

0 commit comments

Comments
 (0)