From d61efdfe97a04f8d67e2f7180353795b5f101c9a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 2 Sep 2023 20:24:39 +0200 Subject: [PATCH] Fix GH-11956: PCRE regular expressions with JIT enabled gives different result The code in the attached test used to work correctly in PHP 8.0, but not in 8.1+. This is because PHP 8.1+ uses a more modern version of pcre2 than PHP 8.0, and that pcre2 versions has a regression. While upgrading pcre2lib seems to be only done for the master branch, it is possible to backport upstream fixes to stable branches. This has been already done in the past in for JIT regressions [1], so it is not unprecedented. We backport the upstream pcre2 fix [2]. [1] https://github.com/php/php-src/commit/788a701e222 [2] https://github.com/PCRE2Project/pcre2/pull/135 Closes GH-12108. --- NEWS | 4 ++++ ext/pcre/pcre2lib/pcre2_jit_compile.c | 20 ++++++++++---------- ext/pcre/tests/gh11956.phpt | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 ext/pcre/tests/gh11956.phpt diff --git a/NEWS b/NEWS index 6f0c68ad73c65..8c728b0744320 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,10 @@ PHP NEWS . Fixed bug GH-12186 (segfault copying/cloning a finalized HashContext). (MaxSem) +- PCRE: + . Fixed bug GH-11956 (Backport upstream fix, PCRE regular expressions with + JIT enabled gives different result). (nielsdos) + - SimpleXML: . Fixed bug GH-12170 (Can't use xpath with comments in SimpleXML). (nielsdos) . Fixed bug GH-12192 (SimpleXML infinite loop when getName() is called diff --git a/ext/pcre/pcre2lib/pcre2_jit_compile.c b/ext/pcre/pcre2lib/pcre2_jit_compile.c index 227714e577019..0b2d707ae51a7 100644 --- a/ext/pcre/pcre2lib/pcre2_jit_compile.c +++ b/ext/pcre/pcre2lib/pcre2_jit_compile.c @@ -11286,19 +11286,19 @@ if (exact > 1) } } else if (exact == 1) - { compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE); - if (early_fail_type == type_fail_range) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr); - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw)); - OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0); - OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0); - add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0)); +if (early_fail_type == type_fail_range) + { + /* Range end first, followed by range start. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0); + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw), STR_PTR, 0); - } + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + (int)sizeof(sljit_sw), STR_PTR, 0); } switch(opcode) diff --git a/ext/pcre/tests/gh11956.phpt b/ext/pcre/tests/gh11956.phpt new file mode 100644 index 0000000000000..91c294ffad38d --- /dev/null +++ b/ext/pcre/tests/gh11956.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-11956 (PCRE regular expressions with JIT enabled gives different result) +--INI-- +pcre.jit=1 +--FILE-- +/', '
', $matches ); +var_dump($matches); +?> +--EXPECT-- +array(2) { + [0]=> + string(20) "
" + [1]=> + string(2) "di" +}