Skip to content

Fix uint64 to float bound on 32-bit (1L<<52 is UB) - #194

Open
andypost wants to merge 1 commit into
msgpack:masterfrom
andypost:fix-uint64-double-32bit
Open

andypost wants to merge 1 commit into
msgpack:masterfrom
andypost:fix-uint64-double-32bit

Conversation

@andypost

@andypost andypost commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

msgpack_unserialize_uint64() is meant to return a float for uint64 values above ZEND_LONG_MAX that a double still represents exactly (up to 2^52), and a decimal string beyond that (#67, fb746e0):

} else if (data <= 1L<<52) {
    ZVAL_DOUBLE(*obj, data);

On 32-bit, long is 32 bits, so 1L<<52 is undefined behaviour. Every 32-bit build warns about it:

msgpack_unpack.c:440:26: warning: left shift count >= width of type [-Wshift-count-overflow]

In practice the float branch is never taken on 32-bit. Every uint64 above 2^31 comes back as a string. On i386 with PHP 8.5 and current master:

2^31 -> string(10) "2147483648"
2^32 -> string(10) "4294967296"
2^40 -> string(13) "1099511627776"
2^52 -> string(16) "4503599627370496"

On 64-bit the branch can't be reached anyway, since 2^52 < ZEND_LONG_MAX, so 32-bit is the only platform where the float range exists.

The fix computes the bound as (uint64_t) 1 << 52. The new test issue067_32bit_double.phpt (32-bit only) covers 2^31, 2^32 and 2^52 → float, and 2^52+1 → string. The existing issue067_32bit.phpt (values ≥ 2^63 → string) is unchanged.

Behaviour change on 32-bit: values from 2^31 to 2^52 now unpack as float, as the code intended, instead of string. Values above 2^52 are unchanged.

Verification

i386 (PHP 8.5, Alpine edge) x86_64
master shift warning; new test FAILs; 141 passed / 1 failed 141 passed / 0 failed (new test skipped)
this PR no warning; 142 passed / 0 failed 141 passed / 0 failed (new test skipped)

Found while fixing 32-bit issues for Alpine (alpine/aports!111139, see also #192 and #193).

🤖 Generated with Claude Code

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 68.65%. Comparing base (26c8c70) to head (f3a78df).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
msgpack_unpack.c 0.00% 0 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@           Coverage Diff           @@
##           master     #194   +/-   ##
=======================================
  Coverage   68.65%   68.65%           
=======================================
  Files           7        7           
  Lines        1356     1356           
  Branches      248      248           
=======================================
  Hits          931      931           
  Misses        310      310           
  Partials      115      115           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants