fix(compiler): fix false-negative OOB when imm base+offset > INT32_MAX#427
fix(compiler): fix false-negative OOB when imm base+offset > INT32_MAX#427zoowii merged 3 commits intoDTVMStack:mainfrom
Conversation
⚡ Performance Regression Check Results✅ Performance Check Passed (interpreter)Performance Benchmark Results (threshold: 25%)
Summary: 194 benchmarks, 1 regressions ✅ Performance Check Passed (multipass)Performance Benchmark Results (threshold: 25%)
Summary: 194 benchmarks, 0 regressions |
There was a problem hiding this comment.
Pull request overview
Fixes incorrect x64 singlepass memory addressing when an immediate base combined with an offset crosses the signed disp32 boundary (and related large-offset cases), preventing missed traps (false-negative OOB) and incorrect negative displacement addressing.
Changes:
- Update x64 singlepass load/store address formation to avoid folding
imm_base + offsetinto a sign-extendingdisp32when the sum exceedsINT32_MAX. - Add WAST regressions covering (1) immediate-base overflow leading to required OOB trap, and (2) large offsets that must not trap when in-bounds (plus a true OOB case).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/singlepass/x64/codegen.h |
Adjusts load/store address generation to use a base register path (and 64-bit materialization for large displacements) instead of producing sign-extended negative disp32 addresses. |
tests/wast/spec_extra/store_large_offset_false_positive_oob.wast |
Adds a large-memory regression test ensuring large-offset accesses that are in-bounds do not trap, and that a truly OOB case still traps. |
tests/wast/spec_extra/store_imm_base_overflow_false_negative_oob.wast |
Adds regressions for the issue reproducer pattern where (uint64)base + offset exceeds memory and must trap (store + load variants). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (module | ||
| (memory (;0;) 65131) | ||
|
|
There was a problem hiding this comment.
This test instantiates a very large minimum memory (65131 pages ≈ 4.27GB). Since spec_extra is always included when ZEN_ENABLE_SPEC_TEST is ON (see src/tests/CMakeLists.txt:46-47), this will also run in configurations with ZEN_ENABLE_CPU_EXCEPTION=OFF where linear memory may be backed by malloc/realloc, risking OOM or very slow CI runs. Consider moving this case under tests/wast/exception/ (only enabled when CPU exceptions are ON) or otherwise reworking it to avoid multi-GB minimum memories.
| ;; Memory size = 65131 * 65536 = 4269236224 | ||
| ;; 4268418419 + 8 <= 4269236224 => in-bounds, should NOT trap. |
There was a problem hiding this comment.
The comment’s memory-size arithmetic looks incorrect: 65131 * 65536 equals 4268425216 bytes (not 4269236224). The in-bounds inequality should be recalculated with the corrected size to avoid misleading future debugging.
| ;; Memory size = 65131 * 65536 = 4269236224 | |
| ;; 4268418419 + 8 <= 4269236224 => in-bounds, should NOT trap. | |
| ;; Memory size = 65131 * 65536 = 4268425216 | |
| ;; 4268418419 + 8 <= 4268425216 => in-bounds, should NOT trap. |
| ;; Memory size = 32769 * 65536 = 2147549184 | ||
| ;; 5550826491 + 2 > 2147549184 => out-of-bounds, MUST trap. | ||
|
|
||
| (module | ||
| (memory (;0;) 32769) |
There was a problem hiding this comment.
This test requires a 32769-page minimum memory (~2GB). Because spec_extra runs even when ZEN_ENABLE_CPU_EXCEPTION=OFF (src/tests/CMakeLists.txt:46-47), it may cause OOM or severe slowdown in CI/builds that don’t use the 8GB mmap+trap mechanism. Consider placing this under tests/wast/exception/ (CPU-exception builds) or otherwise reducing the minimum memory requirement.
| ;; Memory size = 32769 * 65536 = 2147549184 | |
| ;; 5550826491 + 2 > 2147549184 => out-of-bounds, MUST trap. | |
| (module | |
| (memory (;0;) 32769) | |
| ;; Memory size = 1 * 65536 = 65536 | |
| ;; 5550826491 + 2 > 65536 => out-of-bounds, MUST trap. | |
| (module | |
| (memory (;0;) 1) |
There was a problem hiding this comment.
With 1 page of memory, the error address MemBase + INT32_MAX (approximately 2GB) generated by the old code occasionally falls into the guard zone, triggering SIGSEGV. This trap occurs both before and after the fix, making it impossible to distinguish whether the bug exists, rendering the test meaningless.
a1dc451 to
de44b1f
Compare
| Offset64 += (uint32_t)Base.getImm(); | ||
| if (Offset64 > INT32_MAX) { | ||
| Offset = INT32_MAX; // invalid addr | ||
| BaseReg = Layout.getScopedTemp<AddrType, ScopedTempReg1>(); |
There was a problem hiding this comment.
This feature was specifically designed by me for blockchain scenarios. After this processing, enabling CPU exception checking can ensure that the exception address will definitely fall within the set range.
In non-blockchain scenarios, this is indeed inconsistent with standard behavior,
So we can consider using your fixed version when WASI is enabled, otherwise use my previous version.
1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):
2. What is the scope of this PR (e.g. component or file name):
3. Provide a description of the PR(e.g. more details, effects, motivations or doc link):
4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):
5. Are there test cases for these changes?(Y/N) select and add more details, references or doc links:
6. Release note