-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Description
Description
When using an assembly block to initialize a function pointer with a value provided externally, starting from 0.8.13 the optimizer removes the sstore that initializes it.
Note: this is related to #15707, but the PoC was wrong so I think this deserves a separate issue/discussion. This new PoC shows that the initializer does not always revert.
Steps to reproduce
There was an error in the PoC, the intent was not for the uninitialized call to always revert, but for the constructor argument x to dynamically initialize foo. I know it's weird, but I got this to work on 0.8.12:
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.12;
contract A {
// events
event Success();
// storage
function() internal foo;
constructor(uint256 x) {
assembly {
sstore(foo.slot, x)
}
foo();
}
// public so that it's not eliminated by the optimizer
function bar() public {
emit Success();
}
}Manually inspecting the deployment bytecode shows that bar() is at 0x14b, so if we call it like this in a test:
import {A} from "src/A.sol";
contract ATest {
function test_dyn_fun_ptr() public {
new A(0x14b << 0x20);
}
}then it actually does set and call bar() as expected on 0.8.12:
Ran 1 test for test/A.t.sol:ATest
[PASS] test_dyn_fun_ptr() (gas: 97719)
Traces:
[97719] ATest::test_dyn_fun_ptr()
├─ [65075] → new A@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
│ ├─ emit Success()
│ └─ ← [Return] 208 bytes of code
└─ ← [Stop]
but the sstore is eliminated from 0.8.13 and up as reported previously
Environment
- Compiler version: 0.8.28
- Target EVM version (as per compiler settings): Cancun
- Framework/IDE (e.g. Truffle or Remix): foundry
- EVM execution environment / backend / blockchain client: n/a
- Operating system: Linux/macOS