[php-src] PR #24590: Harden core engine against integer overflows, allocator canary recovery, and SSA shift range unsoundness

From: Date: Fri, 23 Oct 2026 06:46:49 +0000
Subject: [php-src] PR #24590: Harden core engine against integer overflows, allocator canary recovery, and SSA shift range unsoundness
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-39751@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/24590 Author: mkrishna793 ### Summary of Changes This PR resolves several boundary overflows, undefined behaviors, and logic soundness issues across the Zend Engine, Core Streams, and bundled extensions (libmagic, timelib). Key arithmetic invariants in these patches have been formally modeled and verified using the **Z3 Theorem Prover** and **Rocq / Coq** to guarantee zero false positives and zero regressions across all 64-bit and 32-bit integer ranges. --- ### Key Issues & Fixes #### 1. Streams: Integer Overflow in main/streams/memory.c * **Issue:** In php_stream_memory_write() and php_stream_memory_seek(), evaluating ms->fpos + count > data_len allowed 64-bit integer wraparound when fpos was seeked near SIZE_MAX. If fpos + count wrapped to $\le data\_len$, buffer reallocation was skipped while memcpy() proceeded to write to ZSTR_VAL(ms->data) + ms->fpos, triggering an out-of-bounds heap write. * **Fix:** Added hardware overflow checks via __builtin_add_overflow(). Any wraparound immediately fails safely with (ssize_t) -1. #### 2. Zend Optimizer: Soundness Flaw in shift_left_overflows() (Zend/Optimizer/zend_inference.c) * **Issue:** In zend_inference.c:762, shift_left_overflows(n, s) for negative numbers checked safe_shift_left(n, s) > n. However, when negative inputs shifted into ZEND_LONG_MIN (e.g., $n = \text{-7,137,322,560,452,408,418}$ shifted left by 62 bits), (n << 62) wrapped around to $-2^{63}$. Because $ZEND\_LONG\_MIN < n$, the check evaluated to false, falsely claiming that a multi-magnitude overflow did not overflow. This allowed downstream passes (dfa_pass.c) to strip type/bounds guards unsoundly. * **Fix:** Replaced with an arithmetic round-trip shift check: ```c if (UNEXPECTED((zend_ulong)s >= SIZEOF_ZEND_LONG * 8)) return true; if (UNEXPECTED(s == SIZEOF_ZEND_LONG * 8 - 1)) return n != 0 && n != -1; zend_long shifted = (zend_long)((zend_ulong)n << (zend_ulong)s); return (shifted >> s) != n;

« previous php.git-pulls (#39751) next »