Skip to content

Commit 0549c8e

Browse files
committed
ext/filter: fix use-after-free in parse_str() with filter.default
php_sapi_filter() re-exported PARSE_STRING results through Z_STRLEN/Z_STRVAL without checking the type php_zval_filter() left behind. A validation failure frees the string and stores IS_FALSE, so the macros read the freed zend_string; a successful FILTER_VALIDATE_INT stores IS_LONG, so they dereference the integer as a pointer. Convert with zval_get_tmp_string() before writing back. Closes GH-22765
1 parent 5fd38ba commit 0549c8e

2 files changed

Lines changed: 25 additions & 3 deletions

File tree

ext/filter/filter.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,15 +349,18 @@ static unsigned int php_sapi_filter(int arg, const char *var, char **val, size_t
349349
}
350350

351351
if (retval) {
352+
zend_string *tmp_str;
353+
zend_string *str = zval_get_tmp_string(&new_var, &tmp_str);
352354
if (new_val_len) {
353-
*new_val_len = Z_STRLEN(new_var);
355+
*new_val_len = ZSTR_LEN(str);
354356
}
355357
efree(*val);
356-
if (Z_STRLEN(new_var)) {
357-
*val = estrndup(Z_STRVAL(new_var), Z_STRLEN(new_var));
358+
if (ZSTR_LEN(str)) {
359+
*val = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
358360
} else {
359361
*val = estrdup("");
360362
}
363+
zend_tmp_string_release(tmp_str);
361364
zval_ptr_dtor(&new_var);
362365
}
363366

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
filter.default with parse_str must not crash on non-string filter results
3+
--EXTENSIONS--
4+
filter
5+
--INI--
6+
filter.default=int
7+
--FILE--
8+
<?php
9+
parse_str('a=1&b=notint', $out);
10+
var_dump($out);
11+
?>
12+
--EXPECTF--
13+
Deprecated: The filter.default ini setting is deprecated in %s on line %d
14+
array(2) {
15+
["a"]=>
16+
string(1) "1"
17+
["b"]=>
18+
string(0) ""
19+
}

0 commit comments

Comments
 (0)