mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-10-22 03:36:19 +00:00
Fix optimization specific SREM edge case (#527)
Existing test coverage didn't catch a signed remainder overflow semantics edge case behavior where at least one the operators is being constant folded by LLVM instead of solc. This PR fixes the SREM translation bug and adds missing test coverage for division ops with constants. Closes #524 --------- Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
1 parent
62a62770e3
commit
40e97d11eb
23 files changed
+1336
-21
No files matched your search
@@ -0,0 +1,41 @@
|
||||
/// Dedicated fixture for the `smod(INT_MIN, -1)` LLVM-UB bug reported in
|
||||
/// paritytech/revive#524.
|
||||
///
|
||||
/// The bug is that LLVM constant-folds `srem(INT_MIN, -1)` to poison (signed
|
||||
/// overflow UB), which then propagates through subsequent operations and
|
||||
/// often collapses to 0. EVM SMOD defines this case as 0, so a bare
|
||||
/// `smod(INT_MIN, -1)` would coincidentally agree; we XOR the result with a
|
||||
/// runtime calldata word so any divergence between the (defined) EVM value
|
||||
/// and the (poison-derived) PVM value becomes observable.
|
||||
///
|
||||
/// **Important compilation details that make the bug actually surface here:**
|
||||
///
|
||||
/// 1. Operands are written as Yul expressions (`shl(255, 1)`, `sar(58, shl(58,
|
||||
/// sub(0,1)))`) rather than raw `0x8000...` / `0xfff...` literals. revive's
|
||||
/// pipeline constant-evaluates raw literals before LLVM ever sees them, so
|
||||
/// no `srem` instruction with both literal operands reaches LLVM and the
|
||||
/// UB-fold opportunity vanishes. Yul expression operands force a real
|
||||
/// `srem` to appear in LLVM IR.
|
||||
///
|
||||
/// 2. The function has a single execution path (no `switch` over many cases).
|
||||
/// Multi-case dispatchers exceed LLVM's inlining budget for the runtime
|
||||
/// `__revive_signed_remainder` function, so the CALL is preserved and
|
||||
/// evaluated at runtime via RISC-V `rem` (which is defined as 0 for the
|
||||
/// INT_MIN/-1 pair) — hiding the bug. With a single case the inliner
|
||||
/// inlines aggressively, exposing the bug.
|
||||
object "SmodIntMinNegOneBug" {
|
||||
code {
|
||||
let size := datasize("SmodIntMinNegOneBug_deployed")
|
||||
codecopy(0, dataoffset("SmodIntMinNegOneBug_deployed"), size)
|
||||
return(0, size)
|
||||
}
|
||||
object "SmodIntMinNegOneBug_deployed" {
|
||||
code {
|
||||
let tag := calldataload(0)
|
||||
let int_min := shl(255, 1)
|
||||
let neg_one := sar(58, shl(58, sub(0, 1)))
|
||||
mstore(0, xor(smod(int_min, neg_one), tag))
|
||||
return(0, 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user