Skip to content

Commit 5b104f4

Browse files
committed
put dynamic buffer test in dapp-test instead of hevm
1 parent e353b86 commit 5b104f4

File tree

9 files changed

+76
-67
lines changed

9 files changed

+76
-67
lines changed

src/dapp-tests/bytes.sol

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
contract Bytes
2+
{
3+
function f(bytes memory b1, bytes memory b2) public pure {
4+
b1 = b2;
5+
assert(b1[1] == b2[1]);
6+
}
7+
}

src/dapp-tests/integration/tests.sh

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ test_hevm_symbolic() {
6666
solc --bin-runtime -o . --overwrite AB.sol
6767
hevm equivalence --code-a $(<A.bin-runtime) --code-b $(<B.bin-runtime) --solver cvc4
6868
rm -rf A.bin-runtime B.bin-runtime
69+
70+
# simple dynamic calldata example
71+
solc --bin-runtime -o . --overwrite bytes.sol
72+
timeout 2m hevm symbolic --code $(<Bytes.bin-runtime) --calldata-model DynamicCD
6973
}
7074

7175
test_hevm_symbolic

src/dapp-tests/shell.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
pkgs.mkShell {
66
name = "dapp-tests";
77

8-
buildInputs = with pkgs; [ killall cacert bashInteractive curl dapp gnumake hevm procps seth solc ];
8+
buildInputs = with pkgs; [ coreutils killall cacert bashInteractive curl dapp gnumake hevm procps seth solc ];
99
}

src/hevm/hevm-cli/hevm-cli.hs

+36-23
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
module Main where
1414

15-
import EVM (StorageModel(..))
15+
import EVM (StorageModel(..), CalldataModel(..))
1616
import qualified EVM
1717
import EVM.Concrete (createAddress, w256)
18-
import EVM.Symbolic (forceLitBytes, litBytes, litAddr, w256lit, sw256, SymWord(..), Buffer(..), len)
18+
import EVM.Symbolic (forceLitBytes, litBytes, litAddr, w256lit, sw256, SymWord(..), Buffer(..), len, Calldata(..))
1919
import qualified EVM.FeeSchedule as FeeSchedule
2020
import qualified EVM.Fetch
2121
import qualified EVM.Flatten
@@ -57,6 +57,7 @@ import Data.Text.IO (hPutStr)
5757
import Data.Maybe (fromMaybe, fromJust)
5858
import Data.Version (showVersion)
5959
import Data.SBV hiding (Word, solver, verbose, name)
60+
import qualified Data.SBV.List as SList
6061
import qualified Data.SBV as SBV
6162
import Data.SBV.Control hiding (Version, timeout, create)
6263
import System.IO (hFlush, hPrint, stdout, stderr)
@@ -110,17 +111,18 @@ data Command w
110111
, block :: w ::: Maybe W256 <?> "Block state is be fetched from"
111112

112113
-- symbolic execution opts
113-
, jsonFile :: w ::: Maybe String <?> "Filename or path to dapp build output (default: out/*.solc.json)"
114-
, dappRoot :: w ::: Maybe String <?> "Path to dapp project root directory (default: . )"
115-
, storageModel :: w ::: Maybe StorageModel <?> "Select storage model: ConcreteS, SymbolicS (default) or InitialS"
116-
, sig :: w ::: Maybe Text <?> "Signature of types to decode / encode"
117-
, arg :: w ::: [String] <?> "Values to encode"
118-
, debug :: w ::: Bool <?> "Run interactively"
119-
, getModels :: w ::: Bool <?> "Print example testcase for each execution path"
120-
, smttimeout :: w ::: Maybe Integer <?> "Timeout given to SMT solver in milliseconds (default: 20000)"
121-
, maxIterations :: w ::: Maybe Integer <?> "Number of times we may revisit a particular branching point"
122-
, solver :: w ::: Maybe Text <?> "Used SMT solver: z3 (default) or cvc4"
123-
, smtoutput :: w ::: Bool <?> "Print verbose smt output"
114+
, jsonFile :: w ::: Maybe String <?> "Filename or path to dapp build output (default: out/*.solc.json)"
115+
, dappRoot :: w ::: Maybe String <?> "Path to dapp project root directory (default: . )"
116+
, storageModel :: w ::: Maybe StorageModel <?> "Select storage model: ConcreteS, SymbolicS (default) or InitialS"
117+
, calldataModel :: w ::: Maybe CalldataModel <?> "Select calldata model: BoundedCD (default), or DynamicCD"
118+
, sig :: w ::: Maybe Text <?> "Signature of types to decode / encode"
119+
, arg :: w ::: [String] <?> "Values to encode"
120+
, debug :: w ::: Bool <?> "Run interactively"
121+
, getModels :: w ::: Bool <?> "Print example testcase for each execution path"
122+
, smttimeout :: w ::: Maybe Integer <?> "Timeout given to SMT solver in milliseconds (default: 20000)"
123+
, maxIterations :: w ::: Maybe Integer <?> "Number of times we may revisit a particular branching point"
124+
, solver :: w ::: Maybe Text <?> "Used SMT solver: z3 (default) or cvc4"
125+
, smtoutput :: w ::: Bool <?> "Print verbose smt output"
124126
}
125127
| Equivalence -- prove equivalence between two programs
126128
{ codeA :: w ::: ByteString <?> "Bytecode of the first program"
@@ -681,7 +683,7 @@ vmFromCommand cmd = do
681683
value' = word value 0
682684
caller' = addr caller 0
683685
origin' = addr origin 0
684-
calldata' = ConcreteBuffer $ bytes calldata ""
686+
calldata' = CalldataBuffer $ ConcreteBuffer $ bytes calldata ""
685687
codeType = if create cmd then EVM.InitCode else EVM.RuntimeCode
686688
address' = if create cmd
687689
then createAddress origin' (word nonce 0)
@@ -716,18 +718,29 @@ symvmFromCommand :: Command Options.Unwrapped -> Query EVM.VM
716718
symvmFromCommand cmd = do
717719
caller' <- maybe (SAddr <$> freshVar_) (return . litAddr) (caller cmd)
718720
callvalue' <- maybe (sw256 <$> freshVar_) (return . w256lit) (value cmd)
719-
calldata' <- case (calldata cmd, sig cmd) of
720-
-- static calldata (up to 256 bytes)
721-
(Nothing, Nothing) -> do
722-
StaticSymBuffer <$> sbytes256
721+
(calldata', preCond) <- case (calldata cmd, sig cmd, calldataModel cmd) of
722+
-- dynamic calldata via smt lists
723+
(Nothing, Nothing, Just DynamicCD) -> do
724+
cd <- freshVar_
725+
return (CalldataBuffer (DynamicSymBuffer cd),
726+
SList.length cd .< 1000 .&&
727+
sw256 (sFromIntegral (SList.length cd)) .< sw256 1000)
728+
729+
-- dynamic calldata via (bounded) haskell list
730+
(Nothing, Nothing, _) -> do
731+
cd <- sbytes256
732+
len <- freshVar_
733+
return (CalldataDynamic (cd, len), len .<= 256)
734+
723735
-- fully concrete calldata
724-
(Just c, Nothing) ->
725-
return $ ConcreteBuffer $ decipher c
736+
(Just c, Nothing, _) ->
737+
return (CalldataBuffer (ConcreteBuffer $ decipher c), sTrue)
726738
-- calldata according to given abi with possible specializations from the `arg` list
727-
(Nothing, Just sig') -> do
739+
(Nothing, Just sig', _) -> do
728740
method' <- io $ functionAbi sig'
729741
let typs = snd <$> view methodInputs method'
730-
StaticSymBuffer <$> staticCalldata (view methodSignature method') typs (arg cmd)
742+
cd <- staticCalldata (view methodSignature method') typs (arg cmd)
743+
return (CalldataBuffer (StaticSymBuffer cd), sTrue)
731744

732745
_ -> error "incompatible options: calldata and abi"
733746

@@ -773,7 +786,7 @@ symvmFromCommand cmd = do
773786
(_, _, Nothing) ->
774787
error $ "must provide at least (rpc + address) or code"
775788

776-
return vm
789+
return $ vm & over EVM.pathConditions (<> [preCond])
777790

778791
where
779792
decipher = hexByteString "bytes" . strip0x

src/hevm/src/EVM.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ instance ParseField StorageModel
315315

316316
-- | Calldata can either by a normal buffer, or a custom "pseudo dynamic" encoding. See EVM.Symbolic for details
317317
data CalldataModel
318-
= BufferCD
318+
= DynamicCD
319319
| BoundedCD
320320
deriving (Read, Show)
321321

@@ -2213,7 +2213,7 @@ copyCalldataToMemory (CalldataBuffer bf) size xOffset yOffset =
22132213
copyCalldataToMemory (CalldataDynamic (b, l)) size xOffset yOffset =
22142214
case (maybeLitWord size, maybeLitWord xOffset, maybeLitWord yOffset) of
22152215
(Just size', Just xOffset', Just yOffset') ->
2216-
copyBytesToMemory (StaticSymBuffer [ite (i .<= l) x 0 | (x, i) <- zip b [1..]]) size' xOffset' yOffset'
2216+
copyBytesToMemory (StaticSymBuffer [ite (i .<= l) x 0 | (x, i) <- zip b [1..]]) (litWord size') (litWord xOffset') (litWord yOffset')
22172217
_ ->
22182218
copyBytesToMemory (DynamicSymBuffer (subList (implode b) 0 (sFromIntegral l))) size xOffset yOffset
22192219

src/hevm/src/EVM/Fetch.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ checksat b = do push 1
187187
b <- getInfo Name
188188
m <- case b of
189189
-- some custom strategies for z3 which have proven to be quite useful (can still be tweaked)
190-
Resp_Name "Z3" -> checkSatUsing "(check-sat-using (then (using-params simplify :cache-all true) smt))"
190+
Resp_Name "Z3" -> checkSatUsing "(check-sat-using (then (using-params simplify :push_ite_bv true :ite_extra_rules true) smt))"
191191
_ -> checkSat
192192
pop 1
193193
return m

src/hevm/src/EVM/SymExec.hs

+3-3
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ abstractVM :: Maybe (Text, [AbiType]) -> [String] -> ByteString -> StorageModel
9999
abstractVM typesignature concreteArgs x storagemodel calldatamodel = do
100100
(cd',pathCond) <- case typesignature of
101101
Nothing -> case calldatamodel of
102-
BufferCD -> do
102+
DynamicCD -> do
103103
list <- freshVar_
104104
return (CalldataBuffer (DynamicSymBuffer list),
105105
-- due to some current z3 shenanegans (possibly related to: https://github.com/Z3Prover/z3/issues/4635)
@@ -205,8 +205,8 @@ maxIterationsReached vm (Just maxIter) =
205205
type Precondition = VM -> SBool
206206
type Postcondition = (VM, VM) -> SBool
207207

208-
checkAssertBuffer :: ByteString -> Query (Either (VM, [VM]) VM)
209-
checkAssertBuffer c = verifyContract c Nothing [] SymbolicS BufferCD (const sTrue) (Just checkAssertions)
208+
checkAssertDynamic :: ByteString -> Query (Either (VM, [VM]) VM)
209+
checkAssertDynamic c = verifyContract c Nothing [] SymbolicS DynamicCD (const sTrue) (Just checkAssertions)
210210

211211

212212
checkAssert :: ByteString -> Maybe (Text, [AbiType]) -> [String] -> Query (Either (VM, [VM]) VM)

src/hevm/src/EVM/Symbolic.hs

+6-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ forceLitBytes = BS.pack . fmap (fromSized . fromJust . unliteral)
6565

6666
forceBuffer :: Buffer -> ByteString
6767
forceBuffer (ConcreteBuffer b) = b
68-
forceBuffer (SymbolicBuffer b) = forceLitBytes b
68+
forceBuffer (StaticSymBuffer b) = forceLitBytes b
69+
forceBuffer (DynamicSymBuffer b) = case unliteral b of
70+
Just b' -> BS.pack $ fmap fromSized b'
71+
Nothing -> error "unexpected symbolic argument"
6972

7073
-- | Arithmetic operations on SymWord
7174

@@ -346,13 +349,13 @@ readByteOrZero i (StaticSymBuffer bs) = readByteOrZero' i bs
346349
readByteOrZero i (ConcreteBuffer bs) = num $ Concrete.readByteOrZero i bs
347350
readByteOrZero i (DynamicSymBuffer bs) = readByteOrZero'' (literal $ num i) bs
348351

349-
-- pad up to 10000 bytes in the dynamic case
352+
-- pad up to 1000 bytes in the dynamic case
350353
sliceWithZero :: SymWord -> SymWord -> Buffer -> Buffer
351354
sliceWithZero (S _ o) (S _ s) bf = case (unliteral o, unliteral s, bf) of
352355
(Just o', Just s', StaticSymBuffer m) -> StaticSymBuffer (sliceWithZero' (num o') (num s') m)
353356
(Just o', Just s', ConcreteBuffer m) -> ConcreteBuffer (Concrete.byteStringSliceWithDefaultZeroes (num o') (num s') m)
354357
(Just o', Just s', m) -> truncpad' (num s') (ditch (num o') m)
355-
_ -> DynamicSymBuffer $ SL.subList (dynamize bf .++ literal (replicate 10000 0)) (sFromIntegral o) (sFromIntegral s)
358+
_ -> DynamicSymBuffer $ SL.subList (dynamize bf .++ literal (replicate 1000 0)) (sFromIntegral o) (sFromIntegral s)
356359

357360
writeMemory :: Buffer -> SymWord -> SymWord -> SymWord -> Buffer -> Buffer
358361
writeMemory bs1 n src dst bs0 =

src/hevm/test/test.hs

+16-34
Original file line numberDiff line numberDiff line change
@@ -558,9 +558,9 @@ main = defaultMain $ testGroup "hevm"
558558
Right counterexample <- runSMTWith cvc4 $ query $ checkAssert c (Just ("f(uint256,uint256)", [AbiUIntType 256, AbiUIntType 256])) []
559559
putStrLn $ "found counterexample:"
560560

561+
,
561562

562-
,
563-
testCase "multiple contracts" $ do
563+
testCase "multiple contracts" $ do
564564
let code =
565565
[i|
566566
contract C {
@@ -589,46 +589,28 @@ main = defaultMain $ testGroup "hevm"
589589
set EVM.storage (Symbolic store)))
590590
verify vm Nothing Nothing (Just checkAssertions)
591591
putStrLn $ "found counterexample:"
592-
-- ,
593-
-- testCase "dynamic bytes (calldataload)" $ do
594-
-- Just c <- solcRuntime "C"
595-
-- [i|
596-
-- contract C
597-
-- {
598-
-- function f() public pure {
599-
-- uint y;
600-
-- uint z;
601-
-- assembly {
602-
-- y := calldataload(12)
603-
-- z := calldataload(31)
604-
-- }
605-
-- assert(y == z);
606-
-- }
607-
-- }
608-
-- |]
609-
-- -- should find a counterexample
610-
-- Right cex <- runSMTWith z3 $ do
611-
-- query $ checkAssert c Nothing []
612-
-- putStrLn $ "found counterexample"
613-
614-
615-
-- ,
616-
testCase "dynamic bytes (abi decoding)" $ do
592+
,
593+
testCase "dynamic bytes (calldataload)" $ do
617594
Just c <- solcRuntime "C"
618595
[i|
619596
contract C
620597
{
621-
function f(bytes memory b1, bytes memory b2) public pure {
622-
b1 = b2;
623-
assert(b1[1] == b2[1]);
598+
function f() public pure {
599+
uint y;
600+
uint z;
601+
assembly {
602+
y := calldataload(12)
603+
z := calldataload(31)
624604
}
605+
assert(y == z);
606+
}
625607
}
626608
|]
627609
-- should find a counterexample
628-
Left (_, res) <- runSMTWith z3{verbose=True} $ do
629-
-- setTimeOut 20000
630-
query $ checkAssertBuffer c
631-
putStrLn $ "successfully explored: " <> show (length res) <> " paths"
610+
Right cex <- runSMTWith z3 $ do
611+
query $ checkAssert c Nothing []
612+
putStrLn $ "found counterexample"
613+
632614
]
633615
, testGroup "Equivalence checking"
634616
[

0 commit comments

Comments
 (0)