Skip to content

Commit a7eeb84

Browse files
authored
Merge pull request #1317 from crytic/upgrade-hevm
Upgrade `hevm`
2 parents 460a071 + 2831aec commit a7eeb84

File tree

9 files changed

+103
-11
lines changed

9 files changed

+103
-11
lines changed

flake.nix

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
# TODO: maybe only override it for echidna-redistributable?
2929
pkgsStatic = if pkgs.stdenv.hostPlatform.isLinux then pkgs.pkgsStatic else pkgs;
3030
# this is not perfect for development as it hardcodes solc to 0.5.7, test suite runs fine though
31-
solc = solc-pkgs.mkDefault pkgs pkgs.solc_0_5_7;
31+
# 0.5.7 is not available on aarch64 darwin so alternatively pick 0.8.5
32+
solc = solc-pkgs.mkDefault pkgs (pkgs.solc_0_5_7 or pkgs.solc_0_8_5);
3233

3334
secp256k1-static = pkgsStatic.secp256k1.overrideAttrs (attrs: {
3435
configureFlags = attrs.configureFlags ++ [ "--enable-static" ];
@@ -51,8 +52,8 @@
5152
hevm = pkgs: pkgs.lib.pipe ((hsPkgs pkgs).callCabal2nix "hevm" (pkgs.fetchFromGitHub {
5253
owner = "ethereum";
5354
repo = "hevm";
54-
rev = "c779777d18c8ff60867f009d434b44ce08188e01";
55-
sha256 = "sha256-JnJUZ9AxhxTP+TBMThksh0D4R6KFdzjgu1+fBeBERws=";
55+
rev = "f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc";
56+
sha256 = "sha256-3zEUwcZm4uZZLecvFTgVTV5CAm4qMfKPbLdwO88LnrY=";
5657
}) { secp256k1 = pkgs.secp256k1; })
5758
([
5859
pkgs.haskell.lib.compose.dontCheck

lib/Echidna/Exec.hs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import Data.Maybe (fromMaybe, fromJust)
2020
import Data.Text qualified as T
2121
import Data.Vector qualified as V
2222
import Data.Vector.Unboxed.Mutable qualified as VMut
23-
import System.Process (readProcessWithExitCode)
23+
import System.Environment (lookupEnv, getEnvironment)
24+
import System.Process qualified as P
2425

2526
import EVM (bytecode, replaceCodeOfSelf, loadContract, exec1, vmOpIx, clearTStorages)
2627
import EVM.ABI
@@ -178,13 +179,21 @@ execTxWith executeTx tx = do
178179
runFully -- resume execution
179180

180181
-- Execute a FFI call
181-
Just (PleaseDoFFI (cmd : args) continuation) -> do
182-
(_, stdout, _) <- liftIO $ readProcessWithExitCode cmd args ""
182+
Just (PleaseDoFFI (cmd : args) envs continuation) -> do
183+
existingEnv <- liftIO getEnvironment
184+
let mergedEnv = Map.toList $ Map.union envs $ Map.fromList existingEnv
185+
let process = (P.proc cmd args) { P.env = Just mergedEnv }
186+
(_, stdout, _) <- liftIO $ P.readCreateProcessWithExitCode process ""
183187
let encodedResponse = encodeAbiValue $
184188
AbiTuple (V.fromList [AbiBytesDynamic . hexText . T.pack $ stdout])
185189
fromEVM (continuation encodedResponse)
186190
runFully
187191

192+
Just (PleaseReadEnv var continuation) -> do
193+
value <- liftIO $ lookupEnv var
194+
fromEVM (continuation $ fromMaybe "" value)
195+
runFully -- resume execution
196+
188197
-- No queries to answer, the tx is fully executed and the result is final
189198
_ -> pure vmResult
190199

lib/Echidna/SymExec.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ vmMakeSymbolic vm
136136
, forks = vm.forks
137137
, currentFork = vm.currentFork
138138
, labels = vm.labels
139+
, osEnv = vm.osEnv
139140
}
140141

141142
frameStateMakeSymbolic :: FrameState Concrete s -> FrameState Symbolic s

lib/Echidna/Test.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,12 @@ checkAssertionEvent = any (T.isPrefixOf "AssertionFailed(")
271271

272272
checkSelfDestructedTarget :: Addr -> DappInfo -> VM Concrete RealWorld -> TestValue
273273
checkSelfDestructedTarget addr _ vm =
274-
let selfdestructs' = vm.tx.substate.selfdestructs
274+
let selfdestructs' = vm.tx.subState.selfdestructs
275275
in BoolValue $ LitAddr addr `notElem` selfdestructs'
276276

277277
checkAnySelfDestructed :: DappInfo -> VM Concrete RealWorld -> TestValue
278278
checkAnySelfDestructed _ vm =
279-
BoolValue $ null vm.tx.substate.selfdestructs
279+
BoolValue $ null vm.tx.subState.selfdestructs
280280

281281
checkPanicEvent :: T.Text -> Events -> Bool
282282
checkPanicEvent n = any (T.isPrefixOf ("Panic(" <> n <> ")"))

lib/Echidna/Transaction.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import Echidna.Types.World (World(..))
3636
import Echidna.Types.Campaign
3737

3838
hasSelfdestructed :: VM Concrete s -> Addr -> Bool
39-
hasSelfdestructed vm addr = LitAddr addr `elem` vm.tx.substate.selfdestructs
39+
hasSelfdestructed vm addr = LitAddr addr `elem` vm.tx.subState.selfdestructs
4040

4141
-- | If half a tuple is zero, make both halves zero. Useful for generating
4242
-- delays, since block number only goes up with timestamp

src/test/Tests/Cheat.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import Echidna.Types.Campaign (WorkerType(..))
99
cheatTests :: TestTree
1010
cheatTests =
1111
testGroup "Cheatcodes Tests"
12-
[ testContract' "cheat/ffi.sol" (Just "TestFFI") (Just (> solcV (0,6,0))) (Just "cheat/ffi.yaml") False FuzzWorker
12+
[ testContract' "cheat/ffi.sol" (Just "TestFFI") (Just (> solcV (0,5,0))) (Just "cheat/ffi.yaml") False FuzzWorker
1313
[ ("echidna_ffi passed", solved "echidna_ffi") ]
14+
, testContract' "cheat/ffi2.sol" (Just "TestFFI") (Just (> solcV (0,5,0))) (Just "cheat/ffi.yaml") False FuzzWorker
15+
[ ("echidna_ffi passed", solved "echidna_ffi") ]
16+
, testContract' "cheat/gas.sol" (Just "TestCheatGas") (Just (> solcV (0,5,0))) (Just "cheat/ffi.yaml") False FuzzWorker
17+
[ ("echidna_gas_zero passed", solved "echidna_gas_zero") ]
1418
]

stack.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ packages:
55

66
extra-deps:
77
- git: https://github.com/ethereum/hevm.git
8-
commit: c779777d18c8ff60867f009d434b44ce08188e01
8+
commit: f1f45d3c0d9767a38df04f398d1eab8b66dbe7fc
99

1010
- smt2-parser-0.1.0.1@sha256:1e1a4565915ed851c13d1e6b8bb5185cf5d454da3b43170825d53e221f753d77,1421
1111
- spawn-0.3@sha256:b91e01d8f2b076841410ae284b32046f91471943dc799c1af77d666c72101f02,1162

tests/solidity/cheat/ffi2.sol

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
pragma experimental ABIEncoderV2;
2+
3+
interface Hevm {
4+
function setEnv(string calldata, string calldata) external;
5+
function ffi(string[] calldata) external returns (bytes memory);
6+
}
7+
8+
contract TestFFI {
9+
address constant HEVM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
10+
11+
bytes32 hehe;
12+
13+
function foo(int x) external {
14+
// ABI encoded "gm", as a string
15+
Hevm(HEVM_ADDRESS).setEnv("ECHIDNA_FOO_BAR", "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000");
16+
17+
string[] memory inputs = new string[](3);
18+
inputs[0] = "sh";
19+
inputs[1] = "-c";
20+
inputs[2] = "printf '%s' \"$ECHIDNA_FOO_BAR\"";
21+
22+
bytes memory res = Hevm(HEVM_ADDRESS).ffi(inputs);
23+
24+
(string memory output) = abi.decode(res, (string));
25+
hehe = keccak256(bytes(output));
26+
}
27+
28+
function echidna_ffi() public returns (bool){
29+
return hehe != keccak256("gm");
30+
}
31+
}

tests/solidity/cheat/gas.sol

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
pragma experimental ABIEncoderV2;
2+
3+
interface Hevm {
4+
function assume(bool) external;
5+
}
6+
7+
contract C {
8+
address public calledContract;
9+
10+
constructor() public {
11+
calledContract = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
12+
}
13+
14+
function foo() public returns (uint256) {
15+
assembly {
16+
mstore(0x80, 0x4c63e562)
17+
mstore(0xa0, 1)
18+
19+
let addr := sload(0 /* calledContract.slot */)
20+
let beforegas := gas()
21+
let success := callcode(gas(), addr, 0, 0x9c, 0x24, 0, 0)
22+
let aftergas := gas()
23+
mstore(0x80, sub(beforegas, aftergas))
24+
return(0x80, 0x20)
25+
}
26+
}
27+
}
28+
29+
contract TestCheatGas {
30+
uint256 spent = 123456;
31+
C foo;
32+
33+
constructor() public {
34+
foo = new C();
35+
}
36+
37+
function bar() public {
38+
spent = foo.foo();
39+
}
40+
41+
function echidna_gas_zero() public returns (bool){
42+
// 0x14 as measured from opcodes, but let's leave some leeway in case solc changes
43+
// GAS PUSH1 0x0 DUP1 PUSH1 0x24 PUSH1 0x9C PUSH1 0x0 DUP7 GAS CALLCODE GAS
44+
return spent > 0x20;
45+
}
46+
}

0 commit comments

Comments
 (0)