Skip to content

Commit ddb822d

Browse files
authored
Merge pull request #674 from ethereum/limit-num-branches
Limiting the branching factor and the depth of branching
2 parents 65c622b + 32b9ed9 commit ddb822d

File tree

11 files changed

+195
-64
lines changed

11 files changed

+195
-64
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4646
print some warnings related to zero-address dereference and to print `hemv test`'s
4747
output in case of failure
4848
- Simple test cases for the CLI
49+
- Allow limiting the branch depth and width limitation via --max-depth and --max-width
4950

5051
## Fixed
5152
- We now try to simplify expressions fully before trying to cast them to a concrete value

cli/cli.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ data CommonOptions = CommonOptions
7676
{ askSmtIterations ::Integer
7777
, loopDetectionHeuristic ::LoopHeuristic
7878
, noDecompose ::Bool
79-
, maxBranch ::Int
8079
, solver ::Text
8180
, debug ::Bool
8281
, calldata ::Maybe ByteString
@@ -92,6 +91,8 @@ data CommonOptions = CommonOptions
9291
, maxIterations ::Integer
9392
, promiseNoReent::Bool
9493
, maxBufSize ::Int
94+
, maxWidth ::Int
95+
, maxDepth ::Maybe Int
9596
}
9697

9798
commonOptions :: Parser CommonOptions
@@ -101,7 +102,6 @@ commonOptions = CommonOptions
101102
<*> option auto (long "loop-detection-heuristic" <> showDefault <> value StackBased <>
102103
help "Which heuristic should be used to determine if we are in a loop: StackBased or Naive")
103104
<*> (switch $ long "no-decompose" <> help "Don't decompose storage slots into separate arrays")
104-
<*> (option auto $ long "max-branch" <> showDefault <> value 100 <> help "Max number of branches to explore when encountering a symbolic value")
105105
<*> (strOption $ long "solver" <> value "z3" <> help "Used SMT solver: z3, cvc5, or bitwuzla")
106106
<*> (switch $ long "debug" <> help "Debug printing of internal behaviour, and dump internal expressions")
107107
<*> (optional $ strOption $ long "calldata" <> help "Tx: calldata")
@@ -117,6 +117,8 @@ commonOptions = CommonOptions
117117
<*> (option auto $ long "max-iterations" <> showDefault <> value 5 <> help "Number of times we may revisit a particular branching point. For no bound, set -1")
118118
<*> (switch $ long "promise-no-reent" <> help "Promise no reentrancy is possible into the contract(s) being examined")
119119
<*> (option auto $ long "max-buf-size" <> value 64 <> help "Maximum size of buffers such as calldata and returndata in exponents of 2 (default: 64, i.e. 2^64 bytes)")
120+
<*> (option auto $ long "max-width" <> showDefault <> value 100 <> help "Max number of concrete values to explore when encountering a symbolic value. This is a form of branch width limitation per symbolic value")
121+
<*> (optional $ option auto $ long "max-depth" <> help "Limit maximum depth of branching during exploration (default: unlimited)")
120122

121123
data CommonExecOptions = CommonExecOptions
122124
{ address ::Maybe Addr
@@ -346,9 +348,10 @@ main = do
346348
, numCexFuzz = cOpts.numCexFuzz
347349
, dumpTrace = cOpts.trace
348350
, decomposeStorage = Prelude.not cOpts.noDecompose
349-
, maxBranch = cOpts.maxBranch
350351
, promiseNoReent = cOpts.promiseNoReent
351352
, maxBufSize = cOpts.maxBufSize
353+
, maxWidth = cOpts.maxWidth
354+
, maxDepth = cOpts.maxDepth
352355
, verb = cOpts.verb
353356
} }
354357

doc/src/common-options.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,25 @@ Loops in the code cause a challenge to symbolic execution framework. In order
4848
to not run indefinitely, hevm will only explore a certain number of iterations
4949
of a loop before consideing abandoning the exploration of that branch. This
5050
number can be set via the `--ask-smt-iterations` flag.
51+
52+
## Maximum Branch Width Limit, ``--max-width``
53+
54+
Limits the number of potential concrete values that are explored in case a
55+
symbolic value is encountered, thus limiting branching width. For example, if a
56+
JUMP instruction is called with a symbolic expression, the system will explore
57+
all possible valid jump destinations, which may be too many. This option limits
58+
the branching factor in these cases. Default is 100.
59+
60+
If there are more than the given maximum number of possible values, the system
61+
will try to deal with the symbolic value, if possible, e.g. via
62+
over-approximation. If over-approximation is not possible, symbolic execution
63+
will terminate with a `Partial` node, which is often displayed as "Unexpected
64+
Symbolic Arguments to Opcode" to the user when e.g. running `hevm test`.
65+
66+
## Maximum Branch Depth Limit, ``--max-depth``
67+
68+
Limits the number of branching points on all paths during symbolic execution.
69+
This is helpful to prevent the exploration from running for too long. Useful in
70+
scenarios where you use e.g. both symbolic execution and fuzzing, and don't
71+
want the symbolic execution to run for too long. It will often read to
72+
WARNING-s related to `Branches too deep at program counter`.

0 commit comments

Comments
 (0)