Skip to content

Commit 5bd1bbd

Browse files
authored
ext/pcre: Fix preg_grep() returning partial array instead of false on error (#21260)
When a PCRE execution error occurs (e.g. malformed UTF-8 with /u modifier), preg_grep() was returning a partial result array containing only the entries processed before the error. All other preg_* functions return false on execution errors. After the match loop, check PCRE_G(error_code) and if an error occurred, destroy the partial array and return false instead. Fixes GH-11936
1 parent 031b4c6 commit 5bd1bbd

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ PHP 8.6 UPGRADE NOTES
1919
1. Backward Incompatible Changes
2020
========================================
2121

22+
- PCRE:
23+
. preg_grep() now returns false instead of a partial array when a PCRE
24+
execution error occurs (e.g. malformed UTF-8 input with the /u modifier).
25+
This is consistent with other preg_* functions.
26+
2227
- Phar:
2328
. Invalid values now throw in Phar::mungServer() instead of being silently
2429
ignored.

ext/pcre/php_pcre.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2987,6 +2987,11 @@ PHPAPI void php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return
29872987
if (match_data != mdata) {
29882988
pcre2_match_data_free(match_data);
29892989
}
2990+
2991+
if (PCRE_G(error_code) != PHP_PCRE_NO_ERROR) {
2992+
zend_array_destroy(Z_ARR_P(return_value));
2993+
RETURN_FALSE;
2994+
}
29902995
}
29912996
/* }}} */
29922997

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
preg_grep() returns false on match execution error (e.g. malformed UTF-8)
3+
--FILE--
4+
<?php
5+
// preg_grep should return false when a match execution error occurs,
6+
// consistent with preg_match behavior. See GH-11936.
7+
8+
// Test 1: preg_match returns false on malformed UTF-8 with /u modifier
9+
var_dump(preg_match('/.*/u', hex2bin('ff')));
10+
var_dump(preg_last_error() === PREG_BAD_UTF8_ERROR);
11+
12+
// Test 2: preg_grep should also return false (not an empty/partial array)
13+
var_dump(preg_grep('/.*/u', [hex2bin('ff')]));
14+
var_dump(preg_last_error() === PREG_BAD_UTF8_ERROR);
15+
16+
// Test 3: preg_grep with valid entries before the invalid one should
17+
// return false, not a partial array
18+
var_dump(preg_grep('/.*/u', ['foo', hex2bin('ff'), 'bar']));
19+
var_dump(preg_last_error() === PREG_BAD_UTF8_ERROR);
20+
21+
// Test 4: preg_grep with PREG_GREP_INVERT should also return false on error
22+
var_dump(preg_grep('/.*/u', [hex2bin('ff')], PREG_GREP_INVERT));
23+
var_dump(preg_last_error() === PREG_BAD_UTF8_ERROR);
24+
25+
// Test 5: preg_grep without error still returns an array
26+
var_dump(preg_grep('/.*/u', ['foo', 'bar']));
27+
var_dump(preg_last_error() === PREG_NO_ERROR);
28+
?>
29+
--EXPECTF--
30+
bool(false)
31+
bool(true)
32+
bool(false)
33+
bool(true)
34+
bool(false)
35+
bool(true)
36+
bool(false)
37+
bool(true)
38+
array(2) {
39+
[0]=>
40+
string(3) "foo"
41+
[1]=>
42+
string(3) "bar"
43+
}
44+
bool(true)

0 commit comments

Comments
 (0)