Skip to content

ext/filter: fix use-after-free in parse_str() with filter.default - #22765

Closed
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/filter-parse-str-uaf
Closed

ext/filter: fix use-after-free in parse_str() with filter.default#22765
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/filter-parse-str-uaf

Conversation

@iliaal

Copy link
Copy Markdown
Contributor

php_sapi_filter() re-exports parse_str() results through Z_STRLEN/Z_STRVAL without checking what type php_zval_filter() left behind. A failed validation 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.

Reproducer, with filter.default set:

php -d filter.default=int -r 'parse_str("a=1&b=notint", $out); var_dump($out);'

ASAN on 8.4, unpatched:

ERROR: AddressSanitizer: heap-use-after-free
READ of size 8 at ... in php_sapi_filter ext/filter/filter.c:353
freed by php_zval_filter ext/filter/filter.c:275
allocated at php_sapi_filter ext/filter/filter.c:338
ERROR: AddressSanitizer: SEGV on unknown address 0x000000000011
in php_sapi_filter ext/filter/filter.c:353

Convert with zval_get_string() before writing back. Present on 8.3 through master, targeting 8.4 as the lowest actively supported branch.

iliaal added a commit to iliaal/php-src that referenced this pull request Jul 16, 2026
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_string() before
writing back.
ClosesphpGH-22765
@iliaal
iliaalforce-pushed the fix/filter-parse-str-uaf branch from 5dd2de9 to efae27aCompareJuly 16, 2026 15:08
@iliaal
iliaal requested a review from ndosscheJuly 16, 2026 15:11
Comment threadext/filter/filter.c Outdated
}

if (retval) {
zend_string *str = zval_get_string(&new_var);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. Could use the _tmp_ version to avoid refcounting, but not critical

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.
ClosesphpGH-22765
@iliaal
iliaalforce-pushed the fix/filter-parse-str-uaf branch from efae27a to 7d43f66CompareJuly 16, 2026 15:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@iliaal@ndossche