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
sprintf()/pack()/bin2hex() _exit(137) at hard memory limit where 8.5.11 raised a catchable OOM (incomplete fallible-alloc conversion, 8.5.12/8.4.27) #24492
Since commit 31c0ea7d ("Avoid state corruption via bailout", CVE-2026-109618/109619), a non-fallible allocation that would cross the new hard memory limit (default 200% of memory_limit) terminates the process via _exit(137) in zend_mm_hard_limit_error() (Zend/zend_alloc.c). Most allocation sites were moved to the new fallible API, but several core functions still allocate non-fallibly from a small, caller-supplied size, so they kill the whole process instead of raising the catchable "Allowed memory size exhausted" fatal that 8.5.11 produced for the same input.
The following code:
<?phpsprintf("%400000000s", "");
Run with: php -n -d memory_limit=128M repro.php
Resulted in this output (8.5.12):
Hard memory limit of 268435456 bytes exhausted (tried to allocate 505413632 bytes) in Command line code on line 1
Exit code 137 — process terminated; the allocation is never actually performed (the hard-limit check rejects first).
But I expected this output instead (the 8.5.11 behavior):
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 503316512 bytes) in Command line code on line 1
Exit code 255 — catchable fatal, the process/worker survives.
The same process kill occurs from other small integer parameters — pack("x300000000"), number_format(1.0, 100000000), bcadd("1","1",100000000) — and from large in-heap inputs to bin2hex / base64_encode / urlencode / var_export / convert_uuencode / utf8_encode / quoted_printable_encode. A crafted phar:// manifest length triggers it from a ~100-byte file.
Sites still using the non-fallible allocator in 8.5.12:
smart_string_alloc() in PAD_CHAR — main/spprintf.c
zend_string_safe_alloc(..., 0) in php_bin2hex() / php_utf8_encode() — ext/standard/string.c
Mechanism: zend_mm_check_limit() calls zend_mm_hard_limit_error() → _exit(137) when !heap->fallible and the allocation would cross hard_limit. Routing these sites through the fallible API (femalloc / zend_string_finit / smart_str_ferealloc) would keep the OOM catchable, consistent with the rest of the patch.
Reproduces identically on 8.4.27 (8.4.26 behaves like 8.5.11). The path is in Zend/zend_alloc.c, so it is platform-independent; tested here on official Windows x64 NTS builds.
PHP Version
PHP 8.5.12 (cli) (built: Oct 20 2026 04:19:56) (NTS Visual C++ 2022 x64)
Copyright (c) The PHP Group
Built by The PHP Group
Zend Engine v4.5.12, Copyright (c) Zend Technologies
Operating System
Windows 11 (code path is OS-independent — Zend/zend_alloc.c)
Another site with the same problem: ext/xml built against Expat (--with-expat). The memory handling suite passed to XML_ParserCreate_MM() still uses the non-fallible allocator:
So any document that makes Expat allocate past memory_limit kills the process at the hard limit. With libxml2 the same script still produces the catchable fatal error. No CI job builds ext/xml with Expat, which is probably why make test there doesn't show it. In an Expat build these tests from the 8.5.12 tarball fail:
Hard memory limit of 16777216 bytes exhausted (tried to allocate 2097152 bytes) in /repro.php on line 11
Same sources with libxml2 2.13.9, exit code 255:
Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 2097152 bytes) in /repro.php on line 11
--enable-debug build, test diffs:
oom_xml_parse_ns.phpt
+ Hard memory limit of 16777216 bytes exhausted at .../ext/xml/xml.c:209 (tried to allocate 2097152 bytes) in .../oom_xml_parse_ns.php on line 12
+ Termsig=9
gh14637.phpt
+ Hard memory limit of 41943040 bytes exhausted at .../ext/xml/xml.c:214 (tried to allocate 4194304 bytes) in .../gh14637.php on line 15
+ Termsig=9
Expat handles a failed allocation by reporting XML_ERROR_NO_MEMORY, so I switched the two wrappers to the fallible allocator (femalloc/ferealloc), as was done for libmagic's cdf.c: rmetzler@7833d0ab63 (branch fix/xml-expat-fallible-alloc, based on PHP-8.2). With it, all ext/xml tests pass in a ZTS + Expat build of PHP-8.2 and PHP-8.5, including the two above. On PHP-8.5 the only exception is xml003.phpt, which fails with Expat with or without the patch. Happy to open a PR if that's the right direction.
Environment: PHP 8.5.12 ZTS (release and --enable-debug), official php-8.5.12.tar.xz built with Alpine's community/php85 APKBUILD and --with-expat, Alpine 3.23.6 (musl), aarch64 locally; the same tests also fail in CI on x86_64. NTS not tested.
Activity
rmetzler commentedon Oct 21, 2026
Another site with the same problem:
ext/xmlbuilt against Expat (--with-expat). The memory handling suite passed toXML_ParserCreate_MM()still uses the non-fallible allocator:php_xml_malloc_wrapper()→emalloc()(ext/xml/xml.c:209)php_xml_realloc_wrapper()→erealloc()(ext/xml/xml.c:214)So any document that makes Expat allocate past
memory_limitkills the process at the hard limit. With libxml2 the same script still produces the catchable fatal error. No CI job buildsext/xmlwith Expat, which is probably whymake testthere doesn't show it. In an Expat build these tests from the 8.5.12 tarball fail:ext/xml/tests/oom_xml_parse_ns.phptext/xml/tests/gh14637.phptphp -n -d extension=xml -d memory_limit=8M repro.phpExpat 2.9.0 build, exit code 137:
Same sources with libxml2 2.13.9, exit code 255:
--enable-debugbuild, test diffs:Expat handles a failed allocation by reporting
XML_ERROR_NO_MEMORY, so I switched the two wrappers to the fallible allocator (femalloc/ferealloc), as was done for libmagic'scdf.c: rmetzler@7833d0ab63 (branchfix/xml-expat-fallible-alloc, based on PHP-8.2). With it, allext/xmltests pass in a ZTS + Expat build of PHP-8.2 and PHP-8.5, including the two above. On PHP-8.5 the only exception isxml003.phpt, which fails with Expat with or without the patch. Happy to open a PR if that's the right direction.Environment: PHP 8.5.12 ZTS (release and
--enable-debug), official php-8.5.12.tar.xz built with Alpine's community/php85 APKBUILD and--with-expat, Alpine 3.23.6 (musl), aarch64 locally; the same tests also fail in CI on x86_64. NTS not tested.Patch ext/xml instead of disabling the expat OOM tests
Fix PHP 8.5.12 ZTS build: patch ext/xml for Expat, skip rfc1867 OOM t…
Fix PHP 8.5.12 ZTS build: patch ext/xml for Expat, skip rfc1867 OOM t…
rmetzler commentedon Oct 21, 2026
Opened #24516 for the ext/xml part (against PHP-8.4).
Fix PHP 8.5.12 ZTS build: patch ext/xml for Expat, skip rfc1867 OOM t…
Update PHP to 8.4.27 (ZTS)
Update PHP to 8.3.36 (ZTS)
Update PHP to 8.2.35 (ZTS)