You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
var_export(), print_r() and unserialize() segfault on deep nesting — the stack-limit guard from #22871 is incomplete #24605
#22871 added ZEND_CHECK_STACK_LIMIT to var_dump()/debug_zval_dump() so deep
nesting throws a catchable Error instead of segfaulting, and its description notes var_export() "overflows the same way but needs a throw-based guard, left for a
separate change." That guard was never added. print_r()'s array path and unserialize() have the same unguarded C recursion. serialize() (GH-15169) and DOMNode::normalize() (#23127) are already guarded.
Reproduce (needs a real stack limit; a container makes it one line)
docker run --rm --ulimit stack=524288:524288 -v "$PWD:/w" php:8.5.12-cli php /w/repro.php
# or locally: bash -c 'ulimit -s 512; php repro.php'
Expected: a catchable Error ("Maximum call stack size reached"), as serialize()
produces on the identical structure in the same process. Actual: segmentation fault (exit 139).
Stack cost is ~233 B/level, so crash depth tracks the stack (~35,000 at 8 MB, ~2,200 at
512 KB). var_export() and print_r() have no depth backstop, so they crash at ~35,000
on a stock 8 MB stack too; unserialize() throws at its default max_depth of 4096 first,
so it only reaches the segfault when the stack is small enough that 4096 levels don't fit
(threaded SAPIs, musl/Alpine, fibers, lowered ulimit).
Fix: add ZEND_CHECK_STACK_LIMIT to the var_export, print_r and unserialize recursion,
as the already-guarded paths do.
Confirming the reopen, with file/line detail on why the patch could not cover two of the three paths.
On PHP-8.5, the six ZEND_CHECK_STACK_LIMIT sites in ext/standard/var.c are at lines 47, 68, 267, 288 (dump paths), 545 (php_var_export_ex), and 1123 (php_var_serialize_class). PR #24671's diff touches only var.c and tests/general_functions/gh24605_var_export_stack_guard.phpt.
print_r is implemented in ext/standard/basic_functions.c (around line 2113), not var.c, and contains no stack check — so a var.c-only change cannot reach it.
unserialize's parser is ext/standard/var_unserializer.re, which currently contains zero occurrences of ZEND_CHECK_STACK_LIMIT (and no stack token at all). It needs the check added in the .re and regenerated.
I'd separate the fixes: basic_functions.c for print_r, var_unserializer.re for unserialize.
Activity
Guard var_export() against stack overflow (phpGH-24605)
Guard print_r() recursion against native stack overflow (phpGH-24605)
Guard unserialize() parser recursion against native stack overflow (p…
tests: Cover phpGH-24605 native stack overflow guard
tests: Cover phpGH-24605 native stack overflow guard
tests: Make phpGH-24605 stack guard coverage more robust
ndossche commentedon Oct 27, 2026
Only partially done
Fix phpGH-24605: Guard var_export() against native stack overflow (ph…
Merge branch 'PHP-8.4' into PHP-8.5
Merge branch 'PHP-8.5' into PHP-8.6
Merge branch 'PHP-8.6'
Merge branch 'PHP-8.4' into prateek/fix-gh24577-ce-cache-race
Volkopat commentedon Oct 27, 2026
Confirming the reopen, with file/line detail on why the patch could not cover two of the three paths.
On PHP-8.5, the six ZEND_CHECK_STACK_LIMIT sites in ext/standard/var.c are at lines 47, 68, 267, 288 (dump paths), 545 (php_var_export_ex), and 1123 (php_var_serialize_class). PR #24671's diff touches only var.c and tests/general_functions/gh24605_var_export_stack_guard.phpt.
print_r is implemented in ext/standard/basic_functions.c (around line 2113), not var.c, and contains no stack check — so a var.c-only change cannot reach it.
unserialize's parser is ext/standard/var_unserializer.re, which currently contains zero occurrences of ZEND_CHECK_STACK_LIMIT (and no stack token at all). It needs the check added in the .re and regenerated.
I'd separate the fixes: basic_functions.c for print_r, var_unserializer.re for unserialize.