From 9149d95ddaa4abbb1914e1690e508aa3e62e4bf7 Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 24 Sep 2020 13:56:37 -0400 Subject: [PATCH] fix issue where libraries would get loaded at extreme timestamps and block numbers, also switch to the new hevm stripBytecodeMetadata (#510) * fix issue where libraries would get loaded at extreme timestamps and block numbers, also switch to the new hevm stripBytecodeMetadata * no longer doing cbor parsing * added test Co-authored-by: ggrieco-tob --- examples/solidity/basic/library.sol | 15 ++++++++++----- lib/Echidna/ABI.hs | 16 ---------------- lib/Echidna/Exec.hs | 13 +++++++++---- lib/Echidna/RPC.hs | 10 ++++------ lib/Echidna/Solidity.hs | 17 +++++++++-------- lib/Echidna/Transaction.hs | 1 + package.yaml | 1 - src/test/Tests/Integration.hs | 2 ++ 8 files changed, 35 insertions(+), 40 deletions(-) diff --git a/examples/solidity/basic/library.sol b/examples/solidity/basic/library.sol index f7c04af7..d5f153ca 100644 --- a/examples/solidity/basic/library.sol +++ b/examples/solidity/basic/library.sol @@ -1,23 +1,28 @@ -library Test{ +library Test { struct Storage{ bool flag; } - function set(Storage storage st) public{ + function set(Storage storage st) public{ st.flag = true; } } -contract Contract{ +contract Contract { using Test for Test.Storage; Test.Storage st; - function set() public{ + function set() public{ st.set(); } - function echidna_library_call() external view returns (bool) { + function echidna_library_call() external view returns (bool) { return (!st.flag); } + + function echidna_valid_timestamp() external view returns (bool) { + require(block.timestamp >= 1524785992 && block.number >= 4370000); + return (block.timestamp <= 1524785992 + 100 weeks && block.number < 4370000 + 10000000); + } } diff --git a/lib/Echidna/ABI.hs b/lib/Echidna/ABI.hs index 929519d7..de6cf3a7 100644 --- a/lib/Echidna/ABI.hs +++ b/lib/Echidna/ABI.hs @@ -8,19 +8,14 @@ module Echidna.ABI where -import Codec.CBOR.Read (deserialiseFromBytes) -import Codec.CBOR.Term (decodeTerm) import Control.Lens import Control.Monad (join, liftM2, liftM3, foldM, replicateM) import Control.Monad.Catch (MonadThrow(..)) import Control.Monad.State.Class (MonadState, gets) import Control.Monad.State (evalStateT) import Control.Monad.Random.Strict (MonadRandom, getRandom, getRandoms, getRandomR, uniformMay) -import Data.Binary.Get (runGet, getWord16be) import Data.Bool (bool) import Data.ByteString (ByteString) -import Data.ByteString.Lazy (fromStrict) -import Data.Either (isRight) import Data.Foldable (toList) import Data.Has (Has(..)) import Data.Hashable (Hashable(..)) @@ -308,14 +303,3 @@ genAbiCallM abi = genWithDict (fmap toList . view wholeCalls) (traverse $ traver genInteractionsM :: (MonadState x m, Has GenDict x, MonadRandom m, MonadThrow m) => NE.NonEmpty SolSignature -> m SolCall genInteractionsM l = genAbiCallM =<< rElem l - --- | Given a solc bytecode strip off the metadata from the end -stripBytecodeMetadata :: ByteString -> ByteString -stripBytecodeMetadata bc - | BS.length cl /= 2 = bc - | BS.length h >= cl' && (isRight . deserialiseFromBytes decodeTerm $ fromStrict cbor) = bc' - | otherwise = bc - where l = BS.length bc - (h, cl) = BS.splitAt (l - 2) bc - cl' = fromIntegral . runGet getWord16be . fromStrict $ cl - (bc', cbor) = BS.splitAt (BS.length h - cl') h diff --git a/lib/Echidna/Exec.hs b/lib/Echidna/Exec.hs index 0a4ccda4..f8dc515a 100644 --- a/lib/Echidna/Exec.hs +++ b/lib/Echidna/Exec.hs @@ -11,20 +11,20 @@ import Control.Lens import Control.Monad.Catch (Exception, MonadThrow(..)) import Control.Monad.State.Strict (MonadState, execState) import Data.Has (Has(..)) -import Data.Map.Strict (Map) +import Data.Map.Strict (Map, fromList) import Data.Maybe (fromMaybe) import Data.Set (Set) import EVM import EVM.Op (Op(..)) -import EVM.Exec (exec) +import EVM.Exec (exec, vmForEthrunCreation) +import EVM.Solidity (stripBytecodeMetadata) import qualified Data.ByteString as BS import qualified Data.Map as M import qualified Data.Set as S -import Echidna.ABI (stripBytecodeMetadata) import Echidna.Transaction -import Echidna.Types.Tx (TxCall(..), Tx, TxResult(..), call, dst) +import Echidna.Types.Tx (TxCall(..), Tx, TxResult(..), call, dst, initialTimestamp, initialBlockNumber) -- | Broad categories of execution failures: reversions, illegal operations, and ???. data ErrorClass = RevertE | IllegalE | UnknownE @@ -123,3 +123,8 @@ traceCoverage = do v <- use hasLens let c = v ^. state . code hasLens <>= [readOp (BS.index c $ v ^. state . pc) c] + +initialVM :: VM +initialVM = vmForEthrunCreation mempty & block . timestamp .~ initialTimestamp + & block . number .~ initialBlockNumber + & env . contracts .~ fromList [] -- fixes weird nonce issues diff --git a/lib/Echidna/RPC.hs b/lib/Echidna/RPC.hs index d84e096d..a94d4e5b 100644 --- a/lib/Echidna/RPC.hs +++ b/lib/Echidna/RPC.hs @@ -16,15 +16,14 @@ import Control.Monad.Reader.Class (MonadReader(..)) import Control.Monad.State.Strict (MonadState, execStateT, runStateT, get, put) import Data.Aeson (FromJSON(..), (.:), withObject, eitherDecodeFileStrict) import Data.Binary.Get (runGetOrFail) -import Data.ByteString.Char8 (ByteString, empty) +import Data.ByteString.Char8 (ByteString) import Data.Has (Has(..)) -import Data.Map (fromList) import Data.Maybe (maybe) import Data.Text.Encoding (encodeUtf8) import EVM import EVM.ABI (AbiType(..), getAbi) import EVM.Concrete (w256) -import EVM.Exec (exec, vmForEthrunCreation) +import EVM.Exec (exec) import EVM.Types (Addr, W256) import Text.Read (readMaybe) @@ -88,10 +87,9 @@ loadEthenoBatch ts fp = do (Left e) -> throwM $ EthenoException e (Right (ethenoInit :: [Etheno])) -> do -- Execute contract creations and initial transactions, - let blank = vmForEthrunCreation empty & env . contracts .~ fromList [] - initVM = foldM (execEthenoTxs ts) Nothing ethenoInit + let initVM = foldM (execEthenoTxs ts) Nothing ethenoInit - (addr, vm') <- runStateT initVM blank + (addr, vm') <- runStateT initVM initialVM case addr of Nothing -> throwM $ EthenoException "Could not find a contract with echidna tests" Just a -> execStateT (liftSH . loadContract $ a) vm' diff --git a/lib/Echidna/Solidity.hs b/lib/Echidna/Solidity.hs index 8fa1eb97..79245304 100644 --- a/lib/Echidna/Solidity.hs +++ b/lib/Echidna/Solidity.hs @@ -32,8 +32,8 @@ import System.Process (StdStream(..), readCreateProcessWithExitCode, import System.IO (openFile, IOMode(..)) import System.Exit (ExitCode(..)) import System.Directory (findExecutable) -import Echidna.ABI (encodeSig, hashSig, stripBytecodeMetadata, fallback) -import Echidna.Exec (execTx) +import Echidna.ABI (encodeSig, hashSig, fallback) +import Echidna.Exec (execTx, initialVM) import Echidna.RPC (loadEthenoBatch) import Echidna.Types.Signature (FunctionHash, SolSignature, SignatureMap) import Echidna.Types.Tx (TxConf, TxCall(..), Tx(..), initialTimestamp, initialBlockNumber) @@ -43,8 +43,7 @@ import Echidna.Processor import EVM hiding (contracts) import qualified EVM (contracts) import EVM.ABI -import EVM.Exec (vmForEthrunCreation) -import EVM.Solidity hiding (stripBytecodeMetadata) +import EVM.Solidity import EVM.Types (Addr) import EVM.Concrete (w256) @@ -163,7 +162,7 @@ loadLibraries :: (MonadIO m, MonadThrow m, MonadReader x m, Has SolConf x) => [SolcContract] -> Addr -> Addr -> VM -> m VM loadLibraries [] _ _ vm = return vm loadLibraries (l:ls) la d vm = loadLibraries ls (la + 1) d =<< loadRest - where loadRest = execStateT (execTx $ Tx (SolCreate $ l ^. creationCode) d la 8000030 0 0 (initialTimestamp, initialBlockNumber)) vm + where loadRest = execStateT (execTx $ Tx (SolCreate $ l ^. creationCode) d la 8000030 0 0 (0, 0)) vm -- | Generate a string to use as argument in solc to link libraries starting from addrLibrary linkLibraries :: [String] -> String @@ -200,7 +199,7 @@ loadSpecified name cs = do unless q . putStrLn $ "Analyzing contract: " <> c ^. contractName . unpacked -- Local variables - (SolConf ca d ads bala balc pref _ _ libs _ fp ma ch bm fs) <- view hasLens + SolConf ca d ads bala balc pref _ _ libs _ fp ma ch bm fs <- view hasLens -- generate the complete abi mapping let bc = c ^. creationCode @@ -219,7 +218,9 @@ loadSpecified name cs = do -- Set up initial VM, either with chosen contract or Etheno initialization file -- need to use snd to add to ABI dict - blank' <- maybe (pure (vmForEthrunCreation bc)) (loadEthenoBatch (fst <$> tests)) fp + blank' <- maybe (pure initialVM) + (loadEthenoBatch $ fst <$> tests) + fp let blank = populateAddresses (NE.toList ads |> d) bala blank' & env . EVM.contracts %~ sans 0x3be95e4159a131e56a84657c4ad4d43ec7cd865d -- fixes weird nonce issues @@ -235,7 +236,7 @@ loadSpecified name cs = do Just (t,_) -> throwM $ TestArgsFound t -- Test args check Nothing -> do vm <- loadLibraries ls addrLibrary d blank - let transaction = unless (isJust fp) $ void . execTx $ Tx (SolCreate bc) d ca 8000030 0 (w256 $ fromInteger balc) (initialTimestamp, initialBlockNumber) + let transaction = unless (isJust fp) $ void . execTx $ Tx (SolCreate bc) d ca 8000030 0 (w256 $ fromInteger balc) (0, 0) vm' <- execStateT transaction vm case currentContract vm' of Just _ -> return (vm', neFuns, fst <$> tests, abiMapping) diff --git a/lib/Echidna/Transaction.hs b/lib/Echidna/Transaction.hs index 7c146835..257f70af 100644 --- a/lib/Echidna/Transaction.hs +++ b/lib/Echidna/Transaction.hs @@ -23,6 +23,7 @@ import Data.Maybe (catMaybes) import EVM hiding (value) import EVM.ABI (abiCalldata, abiValueType) import EVM.Concrete (Word(..), w256) +import EVM.Solidity (stripBytecodeMetadata) import EVM.Types (Addr) import qualified System.Directory as SD diff --git a/package.yaml b/package.yaml index 1c39a5eb..02b2fc7e 100644 --- a/package.yaml +++ b/package.yaml @@ -16,7 +16,6 @@ dependencies: - brick <= 0.46 - base16-bytestring - bytestring - - cborg - containers - data-dword - data-has diff --git a/src/test/Tests/Integration.hs b/src/test/Tests/Integration.hs index b084c527..0de8524e 100644 --- a/src/test/Tests/Integration.hs +++ b/src/test/Tests/Integration.hs @@ -80,6 +80,8 @@ integrationTests = testGroup "Solidity Integration Testing" [ ("echidna_balance failed", passed "echidna_balance") ] , testContract "basic/library.sol" (Just "basic/library.yaml") [ ("echidna_library_call failed", solved "echidna_library_call") ] + , testContract "basic/library.sol" (Just "basic/library.yaml") + [ ("echidna_valid_timestamp failed", passed "echidna_valid_timestamp") ] , testContract "basic/fallback.sol" Nothing [ ("echidna_fallback failed", solved "echidna_fallback") ] , testContract "basic/darray.sol" Nothing