Skip to content

Commit 66bfa65

Browse files
committed
Fix memory leak in filter_var_array() when a nested element throws
php_filter_array_handler() returns on a pending exception without freeing nval, its copy of the input value, so an array value leaks. Free it before returning.
1 parent 3e1903c commit 66bfa65

3 files changed

Lines changed: 42 additions & 0 deletions

File tree

‎NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ PHP NEWS
2525
- Curl:
2626
. Fixed curl_reset() retaining CURLOPT_PRIVATE after reset. (lacatoire)
2727

28+
- Filter:
29+
. Fixed memory leak in filter_var_array() and filter_input_array() when
30+
filtering a nested array throws. (Eyüp Can Akman)
31+
2832
- MBString:
2933
. Fixed stack overflow in mb_check_encoding() and mb_convert_encoding() with
3034
deeply nested arrays. (Eyüp Can Akman)

‎ext/filter/filter.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ static void php_filter_array_handler(zval *input, HashTable *op_ht, zend_long op
665665
2
666666
);
667667
if (EG(exception)) {
668+
zval_ptr_dtor(&nval);
668669
RETURN_THROWS();
669670
}
670671
zend_hash_update(Z_ARRVAL_P(return_value), arg_key, &nval);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
filter_var_array() does not leak the filtered value when filtering it throws
3+
--EXTENSIONS--
4+
filter
5+
--FILE--
6+
<?php
7+
function leakcheck(callable $fn): bool {
8+
$fn();
9+
$before = memory_get_usage();
10+
for ($i = 0; $i < 2000; $i++) {
11+
$fn();
12+
}
13+
return memory_get_usage() - $before === 0;
14+
}
15+
16+
var_dump(leakcheck(function () {
17+
try {
18+
filter_var_array(['a' => ['1', 'x']], ['a' => ['filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_REQUIRE_ARRAY | FILTER_THROW_ON_FAILURE]]);
19+
} catch (Filter\FilterFailedException $e) {
20+
}
21+
}));
22+
23+
var_dump(leakcheck(function () {
24+
try {
25+
filter_var_array(['a' => ['1', 'x']], ['a' => ['filter' => FILTER_CALLBACK, 'options' => function ($value) {
26+
if ($value === 'x') {
27+
throw new Exception('callback failed');
28+
}
29+
return $value;
30+
}]]);
31+
} catch (Exception $e) {
32+
}
33+
}));
34+
?>
35+
--EXPECT--
36+
bool(true)
37+
bool(true)

0 commit comments

Comments
 (0)