ethereum / hevm

symbolic EVM evaluator
https://hevm.dev
GNU Affero General Public License v3.0
225 stars 46 forks source link

Random fuzzing can crash with heap overflow due to `copyBytesToMemory` #444

Closed msooseth closed 7 months ago

msooseth commented 7 months ago

OpCallDataCopy (and perhaps others?) can, when ran symbolically, ignore gas and run out of heap:

FAIL
      Exception: heap overflow

To reproduce an example, use 644316997dcb887638a35118cf7d72b4fbbb722e and issue :main -p "random-contract-concrete-call" --quickcheck-tests 10000000 --quickcheck-replay 101 in cabal repl test. It will die at:

        OpCalldatacopy -> do
          case stk of
            xTo:xFrom:xSize:xs -> do
              traceM $ (show xTo <> "\n" <> show xFrom <> "\n" <> show xSize)
              burnCalldatacopy xSize $
                accessMemoryRange xTo xSize $ do
                  next
                  assign (#state % #stack) xs
                  copyBytesToMemory vm.state.calldata xSize xFrom xTo
            _ -> underrun

where the trace will print:

Lit 0xa493d65e20984bc
Lit 0x4
Lit 0x7

So the offset to write to is extremely large, but UNDER 64b, and we run out of heap because:

accessMemoryRange (Lit offs) (Lit sz) continue =
  case (,) <$> toWord64 offs <*> toWord64 sz of
    Nothing -> vmError IllegalOverflow
    Just (offs64, sz64) ->
      if offs64 + sz64 < sz64
        then vmError IllegalOverflow
        else accessUnboundedMemoryRange offs64 sz64 continue

Only guards for 64b. However, 64b is an extreme amount of memory, and in this case, Lit 0xa493d65e20984bc is approx $2^{59}$, which is too big to fit. I think I'll fix the issue with throwing IllegalOverflow for anything more than 28b, which is ~256MB, way more than enough or reasonable for the EVM.

msooseth commented 7 months ago

Fixing with #445. It will fix this issue once it's merged.

msooseth commented 7 months ago

Merged https://github.com/ethereum/hevm/pull/445 -- closing.