mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-10-20 18:56:17 +00:00
Bug found by @jubnzv: `--newyork`: a `calldatacopy`/`codecopy`/`returndatacopy` to a dynamic destination that could overwrite the free-memory-pointer word truncated a subsequent `mload(0x40)` to `0`. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Agent <agent@local>
24 lines
1.2 KiB
Plaintext
24 lines
1.2 KiB
Plaintext
/// Soundness PoC (newyork FMP range proof): a `calldatacopy` whose *dynamic*
|
||
/// destination can land on the free-memory-pointer word `[0x40, 0x60)` corrupts
|
||
/// the FMP, but was not detected as doing so.
|
||
///
|
||
/// `calldatacopy(and(b, 0xFF), 0, 23)` copies 23 bytes to a destination that
|
||
/// ranges over `[0, 0xFF]`; with `b`'s low byte `0x40` it overwrites the FMP
|
||
/// word. A subsequent `mload(0x40)` reads the corrupted pointer, but newyork kept
|
||
/// its `FMP < heap_size` range proof (only *static* copy destinations flagged
|
||
/// `fmp_could_be_unbounded`) and truncated the value to `0`. EVM (and the stock
|
||
/// pipeline) return the copied bytes: 23 × 0x11 then 9 × 0x00. The fix flags a
|
||
/// dynamic copy destination that is not provably free-pointer-relative
|
||
/// (`add(mload(0x40), k)`, which is `>= 0x80`).
|
||
object "CalldatacopyFmpDynBug" {
|
||
code { datacopy(0, dataoffset("CalldatacopyFmpDynBug_deployed"), datasize("CalldatacopyFmpDynBug_deployed")) return(0, datasize("CalldatacopyFmpDynBug_deployed")) }
|
||
object "CalldatacopyFmpDynBug_deployed" {
|
||
code {
|
||
let b := calldataload(32)
|
||
calldatacopy(and(b, 0xFF), 0, 23)
|
||
mstore(0, mload(64))
|
||
return(0, 32)
|
||
}
|
||
}
|
||
}
|