11module Echidna.SymExec.Common where
22
33import Control.Monad.IO.Unlift (MonadUnliftIO , liftIO )
4- import Data.ByteString.Lazy qualified as BS
54import Data.Function ((&) )
65import Data.Map qualified as Map
76import Data.Maybe (fromMaybe , mapMaybe )
87import Data.Set (Set )
98import Data.Set qualified as Set
109import Data.Text qualified as T
11- import Data.Vector (toList , fromList )
1210import GHC.IORef (IORef , readIORef )
1311import Optics.Core ((.~) , (%) , (%~) )
14- import EVM.ABI (abiKind , AbiKind (Dynamic ), AbiValue (.. ), AbiType ( .. ), Sig (.. ), decodeAbiValue )
12+ import EVM.ABI (abiKind , AbiKind (Dynamic ), Sig (.. ), decodeBuf , AbiVals (.. ))
1513import EVM.Fetch qualified as Fetch
16- import EVM (loadContract , resetState , forceLit )
14+ import EVM (loadContract , resetState , symbolify )
1715import EVM.Effects (TTY , ReadConfig )
18- import EVM.Solidity (SolcContract (.. ), Method (.. ))
16+ import EVM.Solidity (SolcContract (.. ), SourceCache ( .. ), Method ( .. ), WarningData (.. ))
1917import EVM.Solvers (SolverGroup )
20- import EVM.SymExec (abstractVM , mkCalldata , verifyInputs , VeriOpts (.. ), checkAssertions )
21- import EVM.Types (Addr , Frame (.. ), FrameState (.. ), VMType (.. ), EType (.. ), Expr (.. ), word256Bytes , Block (.. ), W256 , SMTCex (.. ), ProofResult (.. ), Prop (.. ), Query (.. ))
22- import qualified EVM.Types (VM (.. ), Env (.. ))
23- import EVM.Format (formatPartial )
24- import Control.Monad.ST (stToIO , RealWorld )
18+ import EVM.SymExec (mkCalldata , verifyInputs , VeriOpts (.. ), checkAssertions , subModel , defaultSymbolicValues )
19+ import EVM.Expr qualified
20+ import EVM.Types (Addr , VMType (.. ), EType (.. ), Expr (.. ), Block (.. ), W256 , SMTCex (.. ), ProofResult (.. ), Prop (.. ), Query (.. ), forceLit )
21+ import qualified EVM.Types (VM (.. ))
22+ import EVM.Format (formatPartialDetailed )
23+ import Control.Monad.ST (RealWorld )
2524import Control.Monad.State.Strict (execState , runStateT )
2625
2726import Echidna.Types (fromEVM )
@@ -50,31 +49,6 @@ suitableForSymExec m = not $ null m.inputs
5049 && null (filter (\ (_, t) -> abiKind t == Dynamic ) m. inputs)
5150 && not (T. isInfixOf " _no_symexec" m. name)
5251
53- -- | Sets result to Nothing, and sets gas to ()
54- vmMakeSymbolic :: W256 -> W256 -> EVM.Types. VM Concrete s -> EVM.Types. VM Symbolic s
55- vmMakeSymbolic maxTimestampDiff maxNumberDiff vm
56- = EVM.Types. VM
57- { result = Nothing
58- , state = frameStateMakeSymbolic vm. state
59- , frames = map frameMakeSymbolic vm. frames
60- , env = vm. env
61- , block = blockMakeSymbolic vm. block
62- , tx = vm. tx
63- , logs = vm. logs
64- , traces = vm. traces
65- , cache = vm. cache
66- , burned = ()
67- , iterations = vm. iterations
68- , constraints = addBlockConstraints maxTimestampDiff maxNumberDiff vm. block vm. constraints
69- , config = vm. config
70- , forks = vm. forks
71- , currentFork = vm. currentFork
72- , labels = vm. labels
73- , osEnv = vm. osEnv
74- , freshVar = vm. freshVar
75- , exploreDepth = 0
76- , keccakPreImgs = vm. keccakPreImgs
77- }
7852
7953blockMakeSymbolic :: Block -> Block
8054blockMakeSymbolic b
@@ -93,72 +67,21 @@ addBlockConstraints maxTimestampDiff maxNumberDiff block cs =
9367senderConstraints :: Set Addr -> [Prop ]
9468senderConstraints as = [foldr (\ a b -> POr b (PEq (SymAddr " caller" ) (LitAddr a))) (PBool False ) $ Set. toList as]
9569
96- frameStateMakeSymbolic :: FrameState Concrete s -> FrameState Symbolic s
97- frameStateMakeSymbolic fs
98- = FrameState
99- { contract = fs. contract
100- , codeContract = fs. codeContract
101- , code = fs. code
102- , pc = fs. pc
103- , stack = fs. stack
104- , memory = fs. memory
105- , memorySize = fs. memorySize
106- , calldata = fs. calldata
107- , callvalue = fs. callvalue
108- , caller = fs. caller
109- , gas = ()
110- , returndata = fs. returndata
111- , static = fs. static
112- , overrideCaller = fs. overrideCaller
113- , resetCaller = fs. resetCaller
114- }
115-
116- frameMakeSymbolic :: Frame Concrete s -> Frame Symbolic s
117- frameMakeSymbolic fr = Frame { context = fr. context, state = frameStateMakeSymbolic fr. state }
118-
119- -- | Convert a n-bit unsigned integer to a n-bit signed integer.
120- uintToInt :: W256 -> Integer
121- uintToInt w = fromIntegral (w - 2 ^ (256 :: Int ))
122-
123- modelToTx :: Addr -> Expr EWord -> Expr EWord -> Method -> Set Addr -> Addr -> ProofResult SMTCex String -> TxOrError
124- modelToTx dst oldTimestamp oldNumber method senders fallbackSender result =
70+ modelToTx :: Addr -> Expr EWord -> Expr EWord -> Method -> Set Addr -> Addr -> Expr Buf -> ProofResult SMTCex String -> TxOrError
71+ modelToTx dst oldTimestamp oldNumber method senders fallbackSender calldata result =
12572 case result of
12673 Cex cex ->
12774 let
128- args = zipWith grabArg (snd <$> method. inputs) [" arg" <> T. pack (show n) | n <- [1 .. ] :: [Int ]]
129-
130- grabArg t
131- = case t of
132- AbiUIntType _ -> grabNormalArg t
133- AbiIntType _ -> grabNormalArgInt t
134- AbiBoolType -> grabNormalArg t
135- AbiBytesType _ -> grabNormalArg t
136- AbiAddressType -> grabAddressArg
137- AbiArrayType n mt -> grabArrayArg n mt
138- AbiTupleType mt -> grabTupleArg mt
139- _ -> error " Unexpected ABI type in `modelToTx`"
140-
141- grabNormalArg argType name
142- = case Map. lookup (Var name) cex. vars of
143- Just w ->
144- decodeAbiValue argType (BS. fromStrict (word256Bytes w))
145- Nothing -> -- put a placeholder
146- decodeAbiValue argType (BS. repeat 0 )
147-
148- grabNormalArgInt argType name
149- = case Map. lookup (Var name) cex. vars of
150- Just w ->
151- case argType of
152- AbiIntType n -> AbiInt n (fromIntegral (uintToInt w))
153- _ -> error " Expected AbiIntType"
154- Nothing -> -- put a placeholder
155- decodeAbiValue argType (BS. repeat 0 )
156-
157- grabAddressArg name = AbiAddress $ fromMaybe 0 $ Map. lookup (SymAddr name) cex. addrs
158-
159- grabArrayArg nElem memberType name = AbiArray nElem memberType $ fromList [grabArg memberType $ name <> " -a-" <> T. pack (show n) | n <- [0 .. nElem - 1 ] :: [Int ]]
160-
161- grabTupleArg memberTypes name = AbiTuple $ fromList [grabArg t $ name <> " -t-" <> T. pack (show n) | (n, t) <- zip ([0 .. ] :: [Int ]) (toList memberTypes)]
75+ cd = defaultSymbolicValues $ subModel cex calldata
76+ types = snd <$> method. inputs
77+ argdata = case cd of
78+ Right cd' -> Right $ EVM.Expr. drop 4 (EVM.Expr. simplify cd')
79+ Left e -> Left e
80+ args = case argdata of
81+ Right argdata' -> case decodeBuf types argdata' of
82+ CAbi v -> v
83+ _ -> []
84+ Left _ -> []
16285
16386 src_ = fromMaybe 0 $ Map. lookup (SymAddr " caller" ) cex. addrs
16487 src = if Set. member src_ senders then src_ else fallbackSender
@@ -222,28 +145,32 @@ getUnknownLogs = mapMaybe (\case
222145 _ -> Nothing )
223146
224147exploreMethod :: (MonadUnliftIO m , ReadConfig m , TTY m ) =>
225- Method -> SolcContract -> EVM.Types. VM Concrete RealWorld -> Addr -> EConfig -> VeriOpts -> SolverGroup -> Fetch. RpcInfo -> IORef ContractCache -> IORef SlotCache -> m ([TxOrError ], PartialsLogs )
148+ Method -> SolcContract -> SourceCache -> EVM.Types. VM Concrete RealWorld -> Addr -> EConfig -> VeriOpts -> SolverGroup -> Fetch. RpcInfo -> IORef ContractCache -> IORef SlotCache -> m ([TxOrError ], PartialsLogs )
226149
227- exploreMethod method contract vm defaultSender conf veriOpts solvers rpcInfo contractCacheRef slotCacheRef = do
228- -- liftIO $ putStrLn ("Exploring: " ++ T.unpack method.methodSignature)
229- -- pushWorkerEvent undefined
230- calldataSym @ (cd, constraints) <- mkCalldata ( Just ( Sig method . methodSignature ( snd <$> method . inputs))) []
150+ exploreMethod method contract sources vm defaultSender conf veriOpts solvers rpcInfo contractCacheRef slotCacheRef = do
151+ calldataSym @ (_, constraints) <- mkCalldata ( Just ( Sig method . methodSignature ( snd <$> method. inputs))) []
152+ let
153+ cd = fst calldataSym
231154 let
232155 fetcher = cachedOracle contractCacheRef slotCacheRef solvers rpcInfo
233156 dst = conf. solConf. contractAddr
234- vmSym = abstractVM calldataSym contract. runtimeCode Nothing False
235- vmSym' <- liftIO $ stToIO vmSym
236157 vmReset <- liftIO $ snd <$> runStateT (fromEVM resetState) vm
237- let vm' = vmReset & execState (loadContract (LitAddr dst))
238- & vmMakeSymbolic conf. txConf. maxTimeDelay conf. txConf. maxBlockDelay
239- & # constraints %~ (++ constraints ++ (senderConstraints conf. solConf. sender))
240- & # state % # callvalue .~ TxValue
241- & # state % # caller .~ SymAddr " caller"
242- & # state % # calldata .~ cd
243- & # env % # contracts .~ (Map. union vmSym'. env. contracts vm. env. contracts)
158+ let
159+ vm' = vmReset & execState (loadContract (LitAddr dst))
160+ & # tx % # isCreate .~ False
161+ & # state % # callvalue .~ TxValue
162+ & # state % # caller .~ SymAddr " caller"
163+ & # state % # calldata .~ cd
164+
165+ vm'' = symbolify vm'
166+ & # block %~ blockMakeSymbolic
167+ & # constraints %~ (addBlockConstraints conf. txConf. maxTimeDelay conf. txConf. maxBlockDelay vm'. block)
168+ & # constraints %~ (++ constraints ++ senderConstraints conf. solConf. sender)
169+
244170 -- TODO we might want to switch vm's state.baseState value to to AbstractBase eventually.
245171 -- Doing so might mess up concolic execution.
246- (_, models, partials) <- verifyInputs solvers veriOpts fetcher vm' (Just $ checkAssertions [0x1 ])
172+ (_, models, partials) <- verifyInputs solvers veriOpts fetcher vm'' (Just $ checkAssertions [0x1 ])
247173 let results = map fst models
174+ let warnData = Just $ WarningData contract sources vm'
248175 -- liftIO $ mapM_ TIO.putStrLn partials
249- return (map (modelToTx dst vm. block. timestamp vm. block. number method conf. solConf. sender defaultSender) results, map (formatPartial . fst ) partials)
176+ return (map (modelToTx dst vm. block. timestamp vm. block. number method conf. solConf. sender defaultSender cd ) results, map (formatPartialDetailed warnData . fst ) partials)
0 commit comments