Skip to content

Commit 33b41c7

Browse files
committed
Fixing equivalence checking
Should work this way, cleaner Explain why test is ignored Update changelog Fixing git merge artefact Fixing up git merge
1 parent 9940c54 commit 33b41c7

File tree

10 files changed

+300
-101
lines changed

10 files changed

+300
-101
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4040
- More symbolic overapproximation for Balance and ExtCodeHash opcodes, fixing
4141
CodeHash SMT representation
4242
- Add deployment code flag to the `equivalenceCheck` function
43-
- New simplification rule for reading a byte that is lower than destination offset in `copySlice`
4443

4544
## Fixed
4645
- We now try to simplify expressions fully before trying to cast them to a concrete value
@@ -57,7 +56,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5756
- CopySlice rewrite rule is now less strict while still being sound
5857
- Assumptions about reading from buffer after its size are now the same in all cases.
5958
Previously, they were too weak in case of reading 32 bytes.
60-
- The equivalence checker now is able to prove that an empty store is equivalent to a store with all slots initialized to 0.
59+
- The equivalence checker now is able to prove that an empty store is
60+
equivalent to a store with all slots initialized to 0.
61+
- Equivalence checking was incorrectly assuming that overapproximated values
62+
were sequentially equivalent. We now distinguish these symbolic values with
63+
`A-` and `B-`
6164

6265
## Changed
6366
- Warnings now lead printing FAIL. This way, users don't accidentally think that

cli/cli.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ data Command w
134134
, maxBranch :: w ::: Int <!> "100" <?> "Max number of branches to explore when encountering a symbolic value (default: 100)"
135135
, maxBufSize :: w ::: Int <!> "64" <?> "Maximum size of buffers such as calldata and returndata in exponents of 2 (default: 64, i.e. 2^64 bytes)"
136136
, promiseNoReent:: w ::: Bool <!> "Promise no reentrancy is possible into the contract(s) being examined"
137+
, create :: w ::: Bool <?> "Tx: creation"
137138
}
138139
| Exec -- Execute a given program with specified env & calldata
139140
{ code :: w ::: Maybe ByteString <?> "Program bytecode"
@@ -307,7 +308,7 @@ equivalence cmd = do
307308
cores <- liftIO $ unsafeInto <$> getNumProcessors
308309
let solverCount = fromMaybe cores cmd.numSolvers
309310
withSolvers solver solverCount (fromMaybe 1 cmd.solverThreads) cmd.smttimeout $ \s -> do
310-
(res, e) <- equivalenceCheck s (fromJust bytecodeA) (fromJust bytecodeB) veriOpts calldata
311+
(res, e) <- equivalenceCheck s (fromJust bytecodeA) (fromJust bytecodeB) veriOpts calldata cmd.create
311312
liftIO $ case (any isCex res, any Expr.isPartial e || any isUnknown res) of
312313
(False, False) -> putStrLn " \x1b[32m[PASS]\x1b[0m Contracts behave equivalently"
313314
(True, _) -> putStrLn " \x1b[31m[FAIL]\x1b[0m Contracts do not behave equivalently"

src/EVM.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,9 +1601,9 @@ freshBufFallback xs = do
16011601
freshVar <- use #freshVar
16021602
assign #freshVar (freshVar + 1)
16031603
let opName = pack $ show $ getOp ?op
1604-
let freshVarExpr = Var (opName <> "-result-stack-" <> (pack . show) freshVar)
1604+
let freshVarExpr = Var (opName <> "-result-stack-fresh-" <> (pack . show) freshVar)
16051605
modifying #constraints ((:) (PLEq freshVarExpr (Lit 1) ))
1606-
let freshReturndataExpr = AbstractBuf (opName <> "-result-data-" <> (pack . show) freshVar)
1606+
let freshReturndataExpr = AbstractBuf (opName <> "-result-data-fresh-" <> (pack . show) freshVar)
16071607
modifying #constraints ((:) (PLEq (bufLength freshReturndataExpr) (Lit (2 ^ ?conf.maxBufSize))))
16081608
assign (#state % #returndata) freshReturndataExpr
16091609
next >> assign (#state % #stack) (freshVarExpr:xs)
@@ -1617,7 +1617,7 @@ freshVarFallback xs _ = do
16171617
freshVar <- use #freshVar
16181618
assign #freshVar (freshVar + 1)
16191619
let opName = pack $ show $ getOp ?op
1620-
let freshVarExpr = Var (opName <> "-result-stack-" <> (pack . show) freshVar)
1620+
let freshVarExpr = Var (opName <> "-result-stack-fresh-" <> (pack . show) freshVar)
16211621
next >> assign (#state % #stack) (freshVarExpr:xs)
16221622

16231623
forceConcrete :: VMOps t => Expr EWord -> String -> (W256 -> EVM t s ()) -> EVM t s ()
@@ -2962,7 +2962,7 @@ instance VMOps Symbolic where
29622962
pushGas = do
29632963
modifying (#env % #freshGasVals) (+ 1)
29642964
n <- use (#env % #freshGasVals)
2965-
pushSym $ Expr.Gas n
2965+
pushSym $ Expr.Gas "" n
29662966
enoughGas _ _ = True
29672967
subGas _ _ = ()
29682968
toGas _ = ()

src/EVM/Format.hs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ module EVM.Format
2929
, strip0x'
3030
, hexByteString
3131
, hexText
32-
, bsToHex
3332
, showVal
3433
) where
3534

@@ -859,9 +858,6 @@ hexText t =
859858
Right x -> x
860859
_ -> internalError $ "invalid hex bytestring " ++ show t
861860

862-
bsToHex :: ByteString -> String
863-
bsToHex bs = concatMap (paddedShowHex 2) (BS.unpack bs)
864-
865861
showVal :: AbiValue -> Text
866862
showVal (AbiBytes _ bs) = formatBytes bs
867863
showVal (AbiAddress addr) = T.pack . show $ addr

src/EVM/SMT.hs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ instance Monoid CexVars where
8282
data BufModel
8383
= Comp CompressedBuf
8484
| Flat ByteString
85-
deriving (Eq, Show)
85+
deriving (Eq)
86+
instance Show BufModel where
87+
show (Comp c) = "Comp " <> show c
88+
show (Flat b) = "Flat 0x" <> bsToHex b
8689

8790
-- | This representation lets us store buffers of arbitrary length without
8891
-- exhausting the available memory, it closely matches the format used by
@@ -322,10 +325,10 @@ referencedFrameContext expr = nubOrd $ foldTerm go [] expr
322325
where
323326
go :: Expr a -> [(Builder, [Prop])]
324327
go = \case
325-
TxValue -> [(fromString "txvalue", [])]
326-
v@(Balance a) -> [(fromString "balance_" <> formatEAddr a, [PLT v (Lit $ 2 ^ (96 :: Int))])]
327-
Gas freshVar -> [(fromString ("gas_" <> show freshVar), [])]
328-
CodeHash a@(LitAddr _) -> [(fromString "codehash_" <> formatEAddr a, [])]
328+
o@TxValue -> [(fromRight' $ exprToSMT o, [])]
329+
o@(Balance _) -> [(fromRight' $ exprToSMT o, [PLT o (Lit $ 2 ^ (96 :: Int))])]
330+
o@(Gas _ _) -> [(fromRight' $ exprToSMT o, [])]
331+
CodeHash o@(LitAddr _) -> [(fromRight' $ exprToSMT o, [])]
329332
_ -> []
330333

331334
referencedBlockContext :: TraversableTerm a => a -> [(Builder, [Prop])]
@@ -443,20 +446,26 @@ declareConstrainAddrs names = SMT2 (["; concrete and symbolic addresses"] <> fma
443446
assume n = "(assert (bvugt " <> n <> " (_ bv9 160)))"
444447
cexvars = (mempty :: CexVars){ addrs = fmap toLazyText names }
445448

449+
-- The gas is a tuple of (prefix, index). Within each prefix, the gas is strictly decreasing as the
450+
-- index increases. This function gets a map of Prefix -> [Int], and for each prefix,
451+
-- enforces the order
446452
enforceGasOrder :: [Prop] -> SMT2
447-
enforceGasOrder ps = SMT2 (["; gas ordering"] <> order indices) mempty mempty
453+
enforceGasOrder ps = SMT2 (["; gas ordering"] <> (concatMap (uncurry order) indices)) mempty mempty
448454
where
449-
order :: [Int] -> [Builder]
450-
order n = consecutivePairs n >>= \(x, y)->
455+
order :: TS.Text -> [Int] -> [Builder]
456+
order prefix n = consecutivePairs (nubInt n) >>= \(x, y)->
451457
-- The GAS instruction itself costs gas, so it's strictly decreasing
452-
["(assert (bvugt gas_" <> (fromString . show $ x) <> " gas_" <> (fromString . show $ y) <> "))"]
458+
["(assert (bvugt (" <> (fromRight' (exprToSMT (Gas prefix x))) <> ") " <>
459+
"(" <> fromRight' ((exprToSMT (Gas prefix y))) <> ")))"]
453460
consecutivePairs :: [Int] -> [(Int, Int)]
454461
consecutivePairs [] = []
455462
consecutivePairs l = zip l (tail l)
456-
indices :: [Int] = nubInt $ concatMap (foldProp go mempty) ps
457-
go :: Expr a -> [Int]
463+
indices = Map.toList $ toMapOfLists $ concatMap (foldProp go mempty) ps
464+
toMapOfLists :: [(TS.Text, Int)] -> Map.Map TS.Text [Int]
465+
toMapOfLists = foldr (\(k, v) acc -> Map.insertWith (++) k [v] acc) Map.empty
466+
go :: Expr a -> [(TS.Text, Int)]
458467
go e = case e of
459-
Gas freshVar -> [freshVar]
468+
Gas prefix v -> [(prefix, v)]
460469
_ -> []
461470

462471
declareFrameContext :: [(Builder, [Prop])] -> Err SMT2
@@ -872,8 +881,8 @@ exprToSMT = \case
872881
pure $ "(store" `sp` encPrev `sp` encIdx `sp` encVal <> ")"
873882
SLoad idx store -> op2 "select" store idx
874883
LitAddr n -> pure $ fromLazyText $ "(_ bv" <> T.pack (show (into n :: Integer)) <> " 160)"
875-
Gas freshVar -> pure $ fromLazyText $ "gas_" <> (T.pack $ show freshVar)
876884
CodeHash a@(LitAddr _) -> pure $ fromLazyText "codehash_" <> formatEAddr a
885+
Gas prefix var -> pure $ fromLazyText $ "gas-" <> T.pack (TS.unpack prefix) <> T.pack (show var)
877886

878887
a -> internalError $ "TODO: implement: " <> show a
879888
where
@@ -1060,14 +1069,12 @@ parseBlockCtx "prevrandao" = PrevRandao
10601069
parseBlockCtx "gaslimit" = GasLimit
10611070
parseBlockCtx "chainid" = ChainId
10621071
parseBlockCtx "basefee" = BaseFee
1063-
parseBlockCtx gas | TS.isPrefixOf (TS.pack "gas_") gas = Gas (textToInt $ TS.drop 4 gas)
10641072
parseBlockCtx val = internalError $ "cannot parse '" <> (TS.unpack val) <> "' into an Expr"
10651073

10661074
parseTxCtx :: TS.Text -> Expr EWord
10671075
parseTxCtx name
10681076
| name == "txvalue" = TxValue
10691077
| Just a <- TS.stripPrefix "balance_" name = Balance (parseEAddr a)
1070-
| Just a <- TS.stripPrefix "gas_" name = Gas (textToInt a)
10711078
| Just a <- TS.stripPrefix "codehash_" name = CodeHash (parseEAddr a)
10721079
| otherwise = internalError $ "cannot parse " <> (TS.unpack name) <> " into an Expr"
10731080

0 commit comments

Comments
 (0)