an intermediate symbolic execution engine for EVM bytecode
16

Configure Feed

Select the types of activity you want to include in your feed.

symevm / src / Executor.hs
8.0 kB 223 lines
1{-# LANGUAGE LambdaCase #-} 2 3module Executor where 4 5import SymbolicExpression 6import SymbolicState 7import Opcode 8import Data.ByteString (ByteString) 9import qualified Data.ByteString as BS 10import Data.Maybe (fromMaybe) 11import Control.Monad (foldM) 12import Prelude hiding (LT, GT, EQ) 13 14data StepResult 15 = Continue ExecState 16 | Branch ExecState ExecState 17 | Halt ExecState 18 | Error String 19 deriving (Show) 20 21executeOpcode :: ByteString -> Opcode -> ExecState -> StepResult 22executeOpcode code op state = case op of 23 ADD -> binOp mkAdd state 24 MUL -> binOp mkMul state 25 SUB -> binOp mkSub state 26 DIV -> binOp mkDiv state 27 MOD -> binOp mkMod state 28 29 LT -> binOp mkLt state 30 GT -> binOp mkGt state 31 EQ -> binOp mkEq state 32 ISZERO -> unOp mkIsZero state 33 34 AND -> binOp mkAnd state 35 OR -> binOp mkOr state 36 XOR -> binOp mkXor state 37 NOT -> unOp mkNot state 38 39 POP -> case popStack (stack state) of 40 Just (_, s') -> Continue state { stack = s', pc = pc state + 1 } 41 Nothing -> Error "Stack underflow" 42 43 _ | Just n <- dupDepth op -> case dupStack n (stack state) of 44 Just s' -> Continue state { stack = s', pc = pc state + 1 } 45 Nothing -> Error "Stack underflow on DUP" 46 47 _ | Just n <- swapDepth op -> case swapStack n (stack state) of 48 Just s' -> Continue state { stack = s', pc = pc state + 1 } 49 Nothing -> Error "Stack underflow on SWAP" 50 51 MLOAD -> case popStack (stack state) of 52 Just (offset, s') -> 53 let value = case offset of 54 Concrete n -> mload n (memory state) 55 _ -> Symbolic "mload" (symbolicId state) 56 state' = state { symbolicId = symbolicId state + 1 } 57 in case pushStack value s' of 58 Just s'' -> Continue state' { stack = s'', pc = pc state' + 1 } 59 Nothing -> Error "Stack overflow" 60 Nothing -> Error "Stack underflow" 61 62 MSTORE -> case popStackN 2 (stack state) of 63 Just ([offset, value], s') -> 64 let mem' = case offset of 65 Concrete n -> mstore n value (memory state) 66 _ -> memory state 67 in Continue state { stack = s', memory = mem', pc = pc state + 1 } 68 _ -> Error "Stack underflow" 69 70 MSTORE8 -> case popStackN 2 (stack state) of 71 Just ([offset, value], s') -> 72 let mem' = case offset of 73 Concrete n -> mstore8 n value (memory state) 74 _ -> memory state 75 in Continue state { stack = s', memory = mem', pc = pc state + 1 } 76 _ -> Error "Stack underflow" 77 78 SLOAD -> case popStack (stack state) of 79 Just (key, s') -> 80 let value = sload key (storage state) 81 in case pushStack value s' of 82 Just s'' -> Continue state { stack = s'', pc = pc state + 1 } 83 Nothing -> Error "Stack overflow" 84 Nothing -> Error "Stack underflow" 85 86 SSTORE -> case popStackN 2 (stack state) of 87 Just ([key, value], s') -> 88 let stor' = sstore key value (storage state) 89 in Continue state { stack = s', storage = stor', pc = pc state + 1 } 90 _ -> Error "Stack underflow" 91 92 JUMP -> case popStack (stack state) of 93 Just (Concrete dest, s') -> 94 Continue state { stack = s', pc = fromInteger dest } 95 Just (_, _) -> Error "Symbolic jump destination not supported" 96 Nothing -> Error "Stack underflow" 97 98 JUMPI -> case popStackN 2 (stack state) of 99 Just ([Concrete dest, cond], s') -> 100 let trueBranch = addConstraint (CTrue cond) $ 101 state { stack = s', pc = fromInteger dest } 102 falseBranch = addConstraint (CFalse cond) $ 103 state { stack = s', pc = pc state + 1 } 104 in case cond of 105 Concrete 0 -> Continue falseBranch 106 Concrete _ -> Continue trueBranch 107 _ -> Branch trueBranch falseBranch 108 Just ([_, cond], s') -> Error "Symbolic jump destination not supported" 109 _ -> Error "Stack underflow" 110 111 JUMPDEST -> Continue state { pc = pc state + 1 } 112 113 PC -> case pushStack (Concrete $ fromIntegral $ pc state) (stack state) of 114 Just s' -> Continue state { stack = s', pc = pc state + 1 } 115 Nothing -> Error "Stack overflow" 116 117 CALLER -> case pushStack Caller (stack state) of 118 Just s' -> Continue state { stack = s', pc = pc state + 1 } 119 Nothing -> Error "Stack overflow" 120 121 CALLVALUE -> case pushStack CallValue (stack state) of 122 Just s' -> Continue state { stack = s', pc = pc state + 1 } 123 Nothing -> Error "Stack overflow" 124 125 CALLDATALOAD -> case popStack (stack state) of 126 Just (offset, s') -> 127 let value = case offset of 128 Concrete n -> loadCalldata (calldata state) n 129 _ -> CallDataLoad offset 130 in case pushStack value s' of 131 Just s'' -> Continue state { stack = s'', pc = pc state + 1 } 132 Nothing -> Error "Stack overflow" 133 Nothing -> Error "Stack underflow" 134 135 CALLDATASIZE -> case pushStack CallDataSize (stack state) of 136 Just s' -> Continue state { stack = s', pc = pc state + 1 } 137 Nothing -> Error "Stack overflow" 138 139 STOP -> Halt state { halted = True } 140 141 RETURN -> case popStackN 2 (stack state) of 142 Just ([offset, size], s') -> 143 let retVal = case (offset, size) of 144 (Concrete o, Concrete s) -> mload o (memory state) 145 _ -> Symbolic "return" (symbolicId state) 146 in Halt state { stack = s', returnData = Just retVal, halted = True } 147 _ -> Error "Stack underflow" 148 149 REVERT -> Halt state { reverted = True, halted = True } 150 151 INVALID -> Halt state { reverted = True, halted = True } 152 153 _ -> Error $ "Unsupported opcode: " ++ show op 154 155binOp :: (SymExpr -> SymExpr -> SymExpr) -> ExecState -> StepResult 156binOp f state = case popStackN 2 (stack state) of 157 Just ([a, b], s') -> 158 case pushStack (f a b) s' of 159 Just s'' -> Continue state { stack = s'', pc = pc state + 1 } 160 Nothing -> Error "Stack overflow" 161 _ -> Error "Stack underflow" 162 163unOp :: (SymExpr -> SymExpr) -> ExecState -> StepResult 164unOp f state = case popStack (stack state) of 165 Just (a, s') -> 166 case pushStack (f a) s' of 167 Just s'' -> Continue state { stack = s'', pc = pc state + 1 } 168 Nothing -> Error "Stack overflow" 169 Nothing -> Error "Stack underflow" 170 171loadCalldata :: ByteString -> Integer -> SymExpr 172loadCalldata cd offset 173 | offset >= 0 && offset < fromIntegral (BS.length cd) = 174 let bytes = BS.drop (fromInteger offset) cd 175 value = foldl (\acc b -> acc * 256 + fromIntegral b) 0 (BS.unpack $ BS.take 32 bytes) 176 in Concrete value 177 | otherwise = Concrete 0 178 179step :: ByteString -> ExecState -> StepResult 180step code state 181 | halted state = Halt state 182 | pc state < 0 || pc state >= BS.length code = Halt state { halted = True } 183 | otherwise = 184 let byte = BS.index code (pc state) 185 opcode = decodeOpcode byte 186 in if isPush opcode 187 then 188 let n = pushBytes opcode 189 value = readBytes code (pc state + 1) n 190 in case pushStack (Concrete value) (stack state) of 191 Just s' -> Continue state { stack = s', pc = pc state + 1 + n } 192 Nothing -> Error "Stack overflow" 193 else executeOpcode code opcode state 194 195run :: ByteString -> ExecState -> Int -> [ExecState] 196run code state maxSteps = go state 0 197 where 198 go st n 199 | n >= maxSteps = [st] 200 | halted st = [st] 201 | otherwise = case step code st of 202 Continue st' -> go st' (n + 1) 203 Halt st' -> [st'] 204 Branch _ _ -> [st] 205 Error msg -> [st { halted = True, reverted = True }] 206 207explore :: ByteString -> ExecState -> Int -> [ExecState] 208explore code initialState maxSteps = go [initialState] 0 209 where 210 go states n 211 | n >= maxSteps = states 212 | all halted states = states 213 | otherwise = 214 let states' = concatMap stepOne states 215 in go states' (n + 1) 216 217 stepOne st 218 | halted st = [st] 219 | otherwise = case step code st of 220 Continue st' -> [st'] 221 Halt st' -> [st'] 222 Branch st1 st2 -> [st1, st2] 223 Error msg -> [st { halted = True, reverted = True }]