mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-11-07 11:21:11 +00:00
Fix/ny dse overlapping load (#563)
Another bug found by @jubnzv: `--newyork`: dead-store elimination could drop a store that a later overlapping unaligned `mload` reads, returning zeroed memory instead of the stored bytes. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c18d29b861
commit
da547e9dd9
4 files changed
+128
-1
No files matched your search
@@ -0,0 +1,23 @@
|
||||
/// Soundness PoC (newyork dead-store elimination): a store read back by an
|
||||
/// intervening *unaligned overlapping* `mload` must not be eliminated as dead.
|
||||
///
|
||||
/// `mem_opt` marked a pending store read only when a later `mload` used the
|
||||
/// *exact same* static offset. Here `mstore(1, PAT)` writes `[1, 33)`,
|
||||
/// `mload(8)` reads `[8, 40)` — overlapping `[1, 33)` but at a different offset,
|
||||
/// so the store stayed a dead-store candidate — and the second `mstore(1, PAT)`
|
||||
/// eliminated it. `r := mload(8)` then read zeroed memory. EVM (and the stock
|
||||
/// pipeline) read the stored bytes: bytes 7..31 of PAT followed by 7 zeros.
|
||||
/// The fix marks every pending store whose 32-byte range the load overlaps as
|
||||
/// read.
|
||||
object "DeadStoreOverlapBug" {
|
||||
code { datacopy(0, dataoffset("DeadStoreOverlapBug_deployed"), datasize("DeadStoreOverlapBug_deployed")) return(0, datasize("DeadStoreOverlapBug_deployed")) }
|
||||
object "DeadStoreOverlapBug_deployed" {
|
||||
code {
|
||||
mstore(1, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20)
|
||||
let r := mload(8)
|
||||
mstore(1, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20)
|
||||
mstore(0, r)
|
||||
return(0, 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4769,6 +4769,28 @@ fn array_bounds_probe() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Regression (newyork dead-store elimination): a store read back by an
|
||||
/// intervening unaligned *overlapping* load must not be eliminated as dead.
|
||||
/// `mem_opt` marked a pending store read only on an exact-offset load, so
|
||||
/// `mstore(1, PAT); r := mload(8); mstore(1, PAT)` left the first store a
|
||||
/// dead-store candidate — `mload(8)` reads `[8, 40)`, overlapping the store's
|
||||
/// `[1, 33)` at a different offset — and the second store eliminated it, so `r`
|
||||
/// read zeroed memory. The fix marks every pending store whose 32-byte range a
|
||||
/// load overlaps as read. Compared newyork-PVM vs solc-EVM.
|
||||
#[test]
|
||||
fn dead_store_read_by_overlapping_load() {
|
||||
let mut actions = instantiate_yul("contracts/DeadStoreOverlapBug.yul", "DeadStoreOverlapBug");
|
||||
actions.push(Call {
|
||||
origin: TestAddress::Alice,
|
||||
dest: TestAddress::Instantiated(0),
|
||||
value: 0,
|
||||
gas_limit: Some(GAS_LIMIT),
|
||||
storage_deposit_limit: None,
|
||||
data: vec![],
|
||||
});
|
||||
run_differential(actions);
|
||||
}
|
||||
|
||||
/// Regression test for the FMP range-proof gap (PR #7 review finding): a copy
|
||||
/// with a static destination inside the free-memory-pointer word [0x40, 0x60)
|
||||
/// but a DYNAMIC length clobbers 0x40 with arbitrary bytes. `mload(0x40)` must
|
||||
|
||||
Reference in new issue
Block a user