mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-11-12 21:57:12 +00:00
Fix/ny msize dse expansion (#565)
Bug found by @jubnzv: `--newyork`: dead-store elimination could drop a store whose memory expansion an intervening `msize()` observes, so `msize()` read a stale (unexpanded) value. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
9adfd9db24
commit
e2e6514953
4 files changed
+112
No files matched your search
@@ -0,0 +1,24 @@
|
||||
/// Soundness PoC (newyork dead-store elimination vs `msize`): a store whose
|
||||
/// memory expansion an intervening `msize()` observes must not be eliminated as
|
||||
/// dead by a later overwrite.
|
||||
///
|
||||
/// `mstore(288, 1)` writes `[288, 320)`, expanding active memory to `0x140`.
|
||||
/// `msize()` between the two stores must observe that expansion (`0x140`). The
|
||||
/// dead-store pass dropped the first store because `mstore(288, 2)` overwrites
|
||||
/// the same offset — but that moved the expansion to *after* the `msize()`, so
|
||||
/// `msize()` read the smaller (unexpanded) value `0`. EVM (and the stock
|
||||
/// pipeline) return `0x140`. The fix clears the dead-store candidate set when an
|
||||
/// `msize()` is evaluated, since it observes the cumulative memory expansion of
|
||||
/// all prior stores.
|
||||
object "MsizeDseBug" {
|
||||
code { datacopy(0, dataoffset("MsizeDseBug_deployed"), datasize("MsizeDseBug_deployed")) return(0, datasize("MsizeDseBug_deployed")) }
|
||||
object "MsizeDseBug_deployed" {
|
||||
code {
|
||||
mstore(288, 1)
|
||||
let r := msize()
|
||||
mstore(288, 2)
|
||||
mstore(0, r)
|
||||
return(0, 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4769,6 +4769,28 @@ fn array_bounds_probe() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Regression (newyork dead-store elimination vs `msize`): a store whose memory
|
||||
/// expansion an intervening `msize()` observes must not be eliminated as dead.
|
||||
/// `mstore(288, 1); let r := msize(); mstore(288, 2)` — the dead-store pass
|
||||
/// dropped the first store as overwritten by the second, moving the memory
|
||||
/// expansion to after the `msize()`, so `r` read the smaller value `0` instead of
|
||||
/// `0x140`. The fix clears the dead-store candidate set at an `msize()`, which
|
||||
/// observes the cumulative expansion of all prior stores. Compared newyork-PVM
|
||||
/// vs solc-EVM.
|
||||
#[test]
|
||||
fn msize_observes_dead_store_expansion() {
|
||||
let mut actions = instantiate_yul("contracts/MsizeDseBug.yul", "MsizeDseBug");
|
||||
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 (newyork FMP range proof): a `calldatacopy` whose *dynamic*
|
||||
/// destination can land on the free-memory-pointer word `[0x40, 0x60)` corrupts
|
||||
/// the FMP, but only *static* copy destinations flagged `fmp_could_be_unbounded`.
|
||||
|
||||
Reference in new issue
Block a user