From 10525566a4b08a14ca728c6bc18b34517bda4a40 Mon Sep 17 00:00:00 2001
From: dxo <hi@d-xo.org>
Date: Mon, 11 Sep 2023 17:23:28 +0200
Subject: [PATCH 1/3] EVM: OpCreate: better comments

---
 src/EVM.hs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/EVM.hs b/src/EVM.hs
index 349e0f040..227ff1423 100644
--- a/src/EVM.hs
+++ b/src/EVM.hs
@@ -1805,23 +1805,29 @@ create :: (?op :: Word8)
   -> W256 -> Word64 -> Expr EWord -> [Expr EWord] -> Expr EAddr -> Expr Buf -> EVM s ()
 create self this xSize xGas xValue xs newAddr initCode = do
   vm0 <- get
+  -- are we exceeding the max init code size
   if xSize > vm0.block.maxCodeSize * 2
   then do
     assign (#state % #stack) (Lit 0 : xs)
     assign (#state % #returndata) mempty
     vmError $ MaxInitCodeSizeExceeded (vm0.block.maxCodeSize * 2) xSize
+  -- are we overflowing the nonce
   else if this.nonce == Just maxBound
   then do
     assign (#state % #stack) (Lit 0 : xs)
     assign (#state % #returndata) mempty
     pushTrace $ ErrorTrace NonceOverflow
     next
+  -- are we overflowing the stack
   else if length vm0.frames >= 1024
   then do
     assign (#state % #stack) (Lit 0 : xs)
     assign (#state % #returndata) mempty
     pushTrace $ ErrorTrace CallDepthLimitReached
     next
+  -- are we deploying to an address that already has a contract?
+  -- note: this check is only sound to do statically if symbolic addresses
+  -- cannot have the value of any existing concrete addresses in the state.
   else if collision $ Map.lookup newAddr vm0.env.contracts
   then burn xGas $ do
     assign (#state % #stack) (Lit 0 : xs)
@@ -1837,7 +1843,6 @@ create self this xSize xGas xValue xs newAddr initCode = do
         next
         touchAccount self
         touchAccount newAddr
-      -- are we overflowing the nonce
       False -> burn xGas $ do
         -- unfortunately we have to apply some (pretty hacky)
         -- heuristics here to parse the unstructured buffer read

From 34364f7c12ea90d0c89785a5a98d0abb93f056fc Mon Sep 17 00:00:00 2001
From: dxo <hi@d-xo.org>
Date: Mon, 11 Sep 2023 17:23:54 +0200
Subject: [PATCH 2/3] SMT: assume that symbolic addresses cannot be the zero
 address or precompiles

---
 src/EVM/SMT.hs | 4 +++-
 test/test.hs   | 8 ++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/EVM/SMT.hs b/src/EVM/SMT.hs
index b199031e2..b91266adb 100644
--- a/src/EVM/SMT.hs
+++ b/src/EVM/SMT.hs
@@ -411,9 +411,11 @@ declareVars names = SMT2 (["; variables"] <> fmap declare names) mempty cexvars
 
 -- Given a list of variable names, create an SMT2 object with the variables declared
 declareAddrs :: [Builder] -> SMT2
-declareAddrs names = SMT2 (["; symbolic addresseses"] <> fmap declare names) mempty cexvars
+declareAddrs names = SMT2 (["; symbolic addresseses"] <> fmap declare names <> fmap assume names) mempty cexvars
   where
     declare n = "(declare-const " <> n <> " Addr)"
+    -- assume that symbolic addresses do not collide with the zero address or precompiles
+    assume n = "(assert (bvugt " <> n <> " (_ bv9 160)))"
     cexvars = (mempty :: CexVars){ addrs = fmap toLazyText names }
 
 declareFrameContext :: [(Builder, [Prop])] -> SMT2
diff --git a/test/test.hs b/test/test.hs
index e6c2cd279..e62f2deac 100644
--- a/test/test.hs
+++ b/test/test.hs
@@ -1303,7 +1303,7 @@ tests = testGroup "hevm"
           [i|
             contract A {
               function f() external {
-                assert(msg.sender != address(0x0));
+                assert(msg.sender != address(0x10));
               }
             }
           |]
@@ -1311,7 +1311,7 @@ tests = testGroup "hevm"
           [i|
             contract B {
               function f() external {
-                assert(block.coinbase != address(0x1));
+                assert(block.coinbase != address(0x11));
               }
             }
           |]
@@ -1319,7 +1319,7 @@ tests = testGroup "hevm"
           [i|
             contract C {
               function f() external {
-                assert(tx.origin != address(0x2));
+                assert(tx.origin != address(0x12));
               }
             }
           |]
@@ -1327,7 +1327,7 @@ tests = testGroup "hevm"
           [i|
             contract D {
               function f() external {
-                assert(address(this) != address(0x3));
+                assert(address(this) != address(0x13));
               }
             }
           |]

From 05a1c6ac018c5f40f226a3aa87b485bb06dfa32b Mon Sep 17 00:00:00 2001
From: dxo <hi@d-xo.org>
Date: Thu, 14 Sep 2023 19:03:54 +0200
Subject: [PATCH 3/3] SymExec: add aliasing constraints for contract mapping
 keys

Adds constraints that ensure that symbolic addresses that are used to
key the contracts mapping cannot alias any other keys.
---
 src/EVM.hs         |  5 +++--
 src/EVM/Expr.hs    |  4 ++++
 src/EVM/SymExec.hs |  6 +++++-
 test/test.hs       | 17 +++++++++++++++--
 4 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/src/EVM.hs b/src/EVM.hs
index 227ff1423..53e2dc31c 100644
--- a/src/EVM.hs
+++ b/src/EVM.hs
@@ -1826,8 +1826,9 @@ create self this xSize xGas xValue xs newAddr initCode = do
     pushTrace $ ErrorTrace CallDepthLimitReached
     next
   -- are we deploying to an address that already has a contract?
-  -- note: this check is only sound to do statically if symbolic addresses
-  -- cannot have the value of any existing concrete addresses in the state.
+  -- note: the symbolic interpreter generates constraints ensuring that
+  -- symbolic storage keys cannot alias other storage keys, making this check
+  -- safe to perform statically
   else if collision $ Map.lookup newAddr vm0.env.contracts
   then burn xGas $ do
     assign (#state % #stack) (Lit 0 : xs)
diff --git a/src/EVM/Expr.hs b/src/EVM/Expr.hs
index 163d94c89..164c4e02c 100644
--- a/src/EVM/Expr.hs
+++ b/src/EVM/Expr.hs
@@ -1087,6 +1087,10 @@ isPartial = \case
   Partial {} -> True
   _ -> False
 
+isSymAddr :: Expr EAddr -> Bool
+isSymAddr (SymAddr _) = True
+isSymAddr _ = False
+
 -- | Returns the byte at idx from the given word.
 indexWord :: Expr EWord -> Expr EWord -> Expr Byte
 -- Simplify masked reads:
diff --git a/src/EVM/SymExec.hs b/src/EVM/SymExec.hs
index 5a02a45d9..2c560e38f 100644
--- a/src/EVM/SymExec.hs
+++ b/src/EVM/SymExec.hs
@@ -454,7 +454,7 @@ verifyContract solvers theCode signature' concreteArgs opts maybepre maybepost =
 runExpr :: Stepper.Stepper RealWorld (Expr End)
 runExpr = do
   vm <- Stepper.runFully
-  let asserts = vm.keccakEqs <> vm.constraints
+  let asserts = vm.keccakEqs <> vm.constraints <> consistentStorageKeys vm.env.contracts
       traces = Traces (Zipper.toForest vm.traces) vm.env.contracts
   pure $ case vm.result of
     Just (VMSuccess buf) -> Success asserts traces buf (fmap toEContract vm.env.contracts)
@@ -462,6 +462,10 @@ runExpr = do
     Just (Unfinished p) -> Partial asserts traces p
     _ -> internalError "vm in intermediate state after call to runFully"
 
+-- build constraints that ensure that symbolic storage keys cannot alias other storage keys
+consistentStorageKeys :: Map (Expr EAddr) Contract -> [Prop]
+consistentStorageKeys (Map.keys -> addrs) = [a ./= b | a <- addrs, b <- filter Expr.isSymAddr addrs]
+
 toEContract :: Contract -> Expr EContract
 toEContract c = C c.code c.storage c.balance c.nonce
 
diff --git a/test/test.hs b/test/test.hs
index e62f2deac..f64c24b5b 100644
--- a/test/test.hs
+++ b/test/test.hs
@@ -1101,8 +1101,7 @@ tests = testGroup "hevm"
         Just c <- solcRuntime "C" src
         res <- reachableUserAsserts c Nothing
         assertBool "unexpected cex" (isRight res)
-    -- TODO: implement missing aliasing rules
-    , expectFail $ testCase "deployed-contract-addresses-cannot-alias" $ do
+    , testCase "deployed-contract-addresses-cannot-alias" $ do
         Just c <- solcRuntime "C"
           [i|
             contract A {}
@@ -1298,6 +1297,20 @@ tests = testGroup "hevm"
         Right e <- reachableUserAsserts c Nothing
         -- TODO: this should work one day
         assertBool "should be partial" (Expr.containsNode isPartial e)
+    , testCase "symbolic-addresses-cannot-be-zero-or-precompiles" $ do
+        let addrs = [T.pack . show . Addr $ a | a <- [0x0..0x09]]
+            mkC a = fromJust <$> solcRuntime "A"
+              [i|
+                contract A {
+                  function f() external {
+                    assert(msg.sender != address(${a}));
+                  }
+                }
+              |]
+        codes <- mapM mkC addrs
+        results <- mapM (flip reachableUserAsserts (Just (Sig "f()" []))) codes
+        let ok = and $ fmap (isRight) results
+        assertBool "unexpected cex" ok
     , testCase "addresses-in-context-are-symbolic" $ do
         Just a <- solcRuntime "A"
           [i|