Skip to content

Commit 9d2125b

Browse files
authored
fix(invariant): call override strategy panic (foundry-rs#7469)
* fix(invariant): override call strat panic * Add test
1 parent 5ecc1bf commit 9d2125b

File tree

3 files changed

+23
-2
lines changed

3 files changed

+23
-2
lines changed

crates/evm/fuzz/src/strategies/invariants.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,17 @@ pub fn override_call_strat(
2525
.prop_flat_map(move |target_address| {
2626
let fuzz_state = fuzz_state.clone();
2727
let calldata_fuzz_config = calldata_fuzz_config.clone();
28-
let (_, abi, functions) = &contracts.lock()[&target_address];
28+
29+
let contracts = &contracts.lock();
30+
let (_, abi, functions) = contracts.get(&target_address).unwrap_or({
31+
// Choose a random contract if target selected by lazy strategy is not in fuzz run
32+
// identified contracts. This can happen when contract is created in `setUp` call
33+
// but is not included in targetContracts.
34+
let rand_index = rand::thread_rng().gen_range(0..contracts.iter().len());
35+
let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap();
36+
contract_specs
37+
});
38+
2939
let func = select_random_function(abi, functions);
3040
func.prop_flat_map(move |func| {
3141
fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, target_address, func)

crates/forge/tests/it/invariant.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ async fn test_invariant() {
153153
async fn test_invariant_override() {
154154
let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol");
155155
let mut runner = TEST_DATA_DEFAULT.runner();
156+
runner.test_options.invariant.fail_on_revert = false;
156157
runner.test_options.invariant.call_override = true;
157158
let results = runner.test_collect(&filter);
158159

testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import "ds-test/test.sol";
55

66
contract Malicious {
77
function world() public {
8-
// Does not matter, since it will get overridden.
8+
// add code so contract is accounted as valid sender
9+
// see https://github.com/foundry-rs/foundry/issues/4245
10+
payable(msg.sender).transfer(1);
911
}
1012
}
1113

@@ -39,6 +41,14 @@ contract InvariantReentrancy is DSTest {
3941
vuln = new Vulnerable(address(mal));
4042
}
4143

44+
// do not include `mal` in identified contracts
45+
// see https://github.com/foundry-rs/foundry/issues/4245
46+
function targetContracts() public view returns (address[] memory) {
47+
address[] memory targets = new address[](1);
48+
targets[0] = address(vuln);
49+
return targets;
50+
}
51+
4252
function invariantNotStolen() public {
4353
require(vuln.stolen() == false, "stolen");
4454
}

0 commit comments

Comments
 (0)