Skip to content

Commit dde644d

Browse files
Backport PCRE2 fix for GHSA-r9hj-j2rw-4q3m
Cherry-pick of PCRE2Project/pcre2@2b40382980 (out-of-bounds write in JIT matching with large stack allocations), adapted to the bundled 10.40 which has no SSIZE_OF macro.
1 parent 1b8d390 commit dde644d

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

‎NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ PHP NEWS
2121
php_openssl_matches_wildcard_name() on crafted server certificate wildcard
2222
CN). (CVE-2026-91767) (Jakub Zelenka)
2323

24+
- PCRE:
25+
. Backported upstream fix for GHSA-r9hj-j2rw-4q3m (Out-of-bounds write in JIT
26+
matching with large stack allocations). (Alexandre Daubois)
27+
2428
- Phar:
2529
. Fixed GHSA-j3wh-g957-2m85 (Integer overflow in phar_tar_number() allowing
2630
TAR archive entry injection). (CVE-2026-6103) (Jakub Zelenka)

‎ext/pcre/pcre2lib/pcre2_jit_compile.c‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Fast, but limited size. */
9797

9898
/* Growth rate for stack allocated by the OS. Should be the multiply
9999
of page size. */
100-
#define STACK_GROWTH_RATE 8192
100+
#define STACK_GROWTH_RATE (sljit_sw)8192
101101

102102
/* Enable to check that the allocation could destroy temporaries. */
103103
#if defined SLJIT_DEBUG && SLJIT_DEBUG
@@ -438,6 +438,8 @@ typedef struct compiler_common {
438438
BOOL local_quit_available;
439439
/* Currently in a positive assertion. */
440440
BOOL in_positive_assertion;
441+
/* More than STACK_GROWTH_RATE / 2 stack memory is allocated. */
442+
BOOL large_stack_allocation;
441443
/* Newline control. */
442444
int nltype;
443445
sljit_u32 nlmax;
@@ -3144,6 +3146,8 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size)
31443146
DEFINE_COMPILER;
31453147

31463148
SLJIT_ASSERT(size > 0);
3149+
if (size > (STACK_GROWTH_RATE / ((sljit_s32)sizeof(sljit_sw) * 2)))
3150+
common->large_stack_allocation = TRUE;
31473151
OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
31483152
#ifdef DESTROY_REGISTERS
31493153
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345);
@@ -14193,7 +14197,20 @@ SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
1419314197

1419414198
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0);
1419514199
OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
14196-
OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE);
14200+
if (common->large_stack_allocation)
14201+
{
14202+
SLJIT_COMPILE_ASSERT((STACK_GROWTH_RATE & (STACK_GROWTH_RATE - 1)) == 0, stack_growth_must_be_power_of_2);
14203+
// Negative difference. The positive difference would also use the same amount
14204+
// of operations, but the last subtraction emits several instructions on x86.
14205+
OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_TOP, 0, STACK_LIMIT, 0);
14206+
// Minimum extra space after allocation.
14207+
OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, (STACK_GROWTH_RATE / 2));
14208+
// Rounds down negative numbers.
14209+
OP2(SLJIT_AND, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, ~(STACK_GROWTH_RATE - 1));
14210+
OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, STACK_LIMIT, 0);
14211+
}
14212+
else
14213+
OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE);
1419714214
OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack));
1419814215
OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0);
1419914216

0 commit comments

Comments
 (0)