fix(lift): SDIV of the most negative number by -1 kills the game on x86 #361

Open
opened 2026-11-14 04:25:22 +00:00 by madebyjordan · 0 comments
Contributor

What happens

The lifted C for SDIV traps on x86 when the guest divides the most negative number by -1, and the game process dies with 0xC0000095 (integer overflow, #DE). AArch64 returns the dividend (INT64_MIN / INT32_MIN) there and goes on.

Where

third_party/a64-kit/lift/suyu/arm64_to_c.h, the data-processing 2-source group, case 3: // SDIV (line 4098 on cf605c85):

{ int64_t _a=(int64_t)Xn,_b=(int64_t)Xm; c->x[d]=(uint64_t)(_b?_a/_b:0); }                                // 64-bit
{ int32_t _a=(int32_t)(uint32_t)Xn,_b=(int32_t)(uint32_t)Xm; c->x[d]=(uint64_t)(uint32_t)(_b?_a/_b:0); }   // 32-bit

The divide-by-zero case is handled (_b ? ... : 0), but INT_MIN / -1 is undefined in C, and x86's idiv faults on it.

Fix (suggested)

Handle -1 on its own, as the negation AArch64 gives:

c->x[d] = (uint64_t)(_b == -1 ? (int64_t)(0 - (uint64_t)_a) : _b ? _a / _b : 0);                           // 64-bit
c->x[d] = (uint64_t)(uint32_t)(_b == -1 ? (int32_t)(0u - (uint32_t)_a) : _b ? _a / _b : 0);                 // 32-bit

That's the same result for every other input, and one compare more per SDIV. It's in third_party/, so it needs the a64-kit's provenance updated, or an edit outside it on the lifted text.

How it was found

A game-free equivalence harness ran every instruction form the lift emits against Unicorn: 15,709 encodings, 477,468 random states. The lifted C's own program crashed on the SDIV INT_MIN, -1 states, so SDIV was left out of that run. No game data is involved: it's the translator's output for the instruction alone.

Whether the game ever divides like that is unknown. If it does, it's a crash with no message.

## What happens The lifted C for SDIV traps on x86 when the guest divides the most negative number by -1, and the game process dies with `0xC0000095` (integer overflow, `#DE`). AArch64 returns the dividend (`INT64_MIN` / `INT32_MIN`) there and goes on. ## Where `third_party/a64-kit/lift/suyu/arm64_to_c.h`, the data-processing 2-source group, `case 3: // SDIV` (line 4098 on cf605c85): ```c { int64_t _a=(int64_t)Xn,_b=(int64_t)Xm; c->x[d]=(uint64_t)(_b?_a/_b:0); } // 64-bit { int32_t _a=(int32_t)(uint32_t)Xn,_b=(int32_t)(uint32_t)Xm; c->x[d]=(uint64_t)(uint32_t)(_b?_a/_b:0); } // 32-bit ``` The divide-by-zero case is handled (`_b ? ... : 0`), but `INT_MIN / -1` is undefined in C, and x86's `idiv` faults on it. ## Fix (suggested) Handle -1 on its own, as the negation AArch64 gives: ```c c->x[d] = (uint64_t)(_b == -1 ? (int64_t)(0 - (uint64_t)_a) : _b ? _a / _b : 0); // 64-bit c->x[d] = (uint64_t)(uint32_t)(_b == -1 ? (int32_t)(0u - (uint32_t)_a) : _b ? _a / _b : 0); // 32-bit ``` That's the same result for every other input, and one compare more per SDIV. It's in `third_party/`, so it needs the a64-kit's provenance updated, or an edit outside it on the lifted text. ## How it was found A game-free equivalence harness ran every instruction form the lift emits against Unicorn: 15,709 encodings, 477,468 random states. The lifted C's own program crashed on the `SDIV INT_MIN, -1` states, so SDIV was left out of that run. No game data is involved: it's the translator's output for the instruction alone. Whether the game ever divides like that is unknown. If it does, it's a crash with no message.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot#361
No description provided.