# Memory corruption in _php_math_number_format_ex()

Integer overflow in PHP number_format() thousand-separator length check on 32-bit builds leads to out-of-bounds memcpy and SIGSEGV.

- Payout: hidden
- Severity: low
- Program: Internet Bug Bounty
- Found by: fosec
- Disclosed: 2019-11-12
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/180562
- Library page: https://bugbountylibrary.com/r/h1%3A180562
- Tags: area:oss domain:oss impact:dos kind:bounty platform:hackerone sev:low tech:php technique:source-review vuln:dos vuln:memory-corruption

**Target:** PHP ext/standard/math.c _php_math_number_format_ex()
**Root cause:** The overflow guard `integral + thousand_sep_len * ((integral-1)/3) < integral` uses unsigned 32-bit arithmetic, so a large thousand_sep_len wraps the sum and passes the check, leaving reslen too small for the subsequent separator memcpy.
**Steps:**
1. On a 32-bit PHP build, set memory_limit to -1.
2. Create a thousand separator of length 0x65000000 via `str_repeat("A", 0x65000000)`.
3. Call `number_format(1234567890, 0, ".", $thousands_sep)`.
4. Observe the overflow check computes 0x12F00000a which wraps to 0x2f00000a and passes.
5. reslen becomes 0x2f00000a; `t -= thousand_sep_len` underflows to 0xed200016 and `memcpy(t+1, thousand_sep, thousand_sep_len)` writes to an invalid address, causing SIGSEGV.
**How found:** Code review of the overflow guard in _php_math_number_format_ex(), noticing both integral and thousand_sep_len are unsigned 32-bit and the multiplication can wrap; confirmed with gdb breakpoints on the check and the memcpy.
**Exploitation:** Requires attacker-controlled thousand separator string and a 32-bit PHP build; the wrapped length check allows an undersized allocation followed by a huge memcpy to an underflowed pointer, crashing the process (SIGSEGV). No code execution demonstrated.
**Insight:** Overflow guards that multiply a length by a count must check the multiplication itself for wraparound, not just the final addition against the original value; on 32-bit builds attacker-controlled string lengths near 2^31 are reachable via str_repeat.

