@@ -9,9 +9,10 @@ import scala.collection.immutable.Queue
9
9
import scala .collection .mutable .{HashMap , HashSet }
10
10
11
11
import z3 .scala ._
12
+ import scala .tools .nsc .doc .model .Val
12
13
13
14
object ConcolicDriver {
14
- def condsToEnv (conds : List [Cond ])(implicit z3Ctx : Z3Context ): HashMap [Int , Value ] = {
15
+ def condsToEnv (conds : List [Cond ])(implicit z3Ctx : Z3Context ): Option [ HashMap [Int , Value ] ] = {
15
16
val intSort = z3Ctx.mkIntSort()
16
17
val boolSort = z3Ctx.mkBoolSort()
17
18
@@ -24,16 +25,57 @@ object ConcolicDriver {
24
25
case Add (_) => z3Ctx.mkAdd(symVToZ3(lhs), symVToZ3(rhs)) // does numtype matter?
25
26
case Sub (_) => z3Ctx.mkSub(symVToZ3(lhs), symVToZ3(rhs))
26
27
case Mul (_) => z3Ctx.mkMul(symVToZ3(lhs), symVToZ3(rhs))
27
- case _ => ???
28
+ case Or (_) =>
29
+ var result = z3Ctx.mkBVOr(
30
+ z3Ctx.mkInt2BV(32 , symVToZ3(lhs)),
31
+ z3Ctx.mkInt2BV(32 , symVToZ3(rhs))
32
+ )
33
+ z3Ctx.mkBV2Int(result, false )
34
+ case _ => throw new NotImplementedError (s " Unsupported binary operation: $op" )
28
35
}
29
36
case SymUnary (op, v) =>
30
37
op match {
31
38
case _ => ???
32
39
}
33
40
case SymIte (cond, thenV, elseV) => z3Ctx.mkITE(condToZ3(cond), symVToZ3(thenV), symVToZ3(elseV))
34
- case Concrete (v) => ???
35
- case _ => ???
41
+ case Concrete (v) =>
42
+ v match {
43
+ // todo: replace with bitvector
44
+ case I32V (i) => z3Ctx.mkInt(i, intSort)
45
+ case I64V (i) => z3Ctx.mkNumeral(i.toString(), intSort)
46
+ // TODO: Float
47
+ case _ => ???
48
+ }
49
+ case RelCond (op, lhs, rhs) =>
50
+ val res = op match {
51
+ case GeS (_) => z3Ctx.mkGE(symVToZ3(lhs), symVToZ3(rhs))
52
+ case GtS (_) => z3Ctx.mkGT(symVToZ3(lhs), symVToZ3(rhs))
53
+ case LtS (_) => z3Ctx.mkLT(symVToZ3(lhs), symVToZ3(rhs))
54
+ case LeS (_) => z3Ctx.mkLE(symVToZ3(lhs), symVToZ3(rhs))
55
+ case GtU (_) => z3Ctx.mkGT(symVToZ3(lhs), symVToZ3(rhs))
56
+ case GeU (_) => z3Ctx.mkGE(symVToZ3(lhs), symVToZ3(rhs))
57
+ case LtU (_) => z3Ctx.mkLT(symVToZ3(lhs), symVToZ3(rhs))
58
+ case LeU (_) => z3Ctx.mkLE(symVToZ3(lhs), symVToZ3(rhs))
59
+ case Eq (_) => z3Ctx.mkEq(symVToZ3(lhs), symVToZ3(rhs))
60
+ case Ne (_) => z3Ctx.mkNot(z3Ctx.mkEq(symVToZ3(lhs), symVToZ3(rhs)))
61
+ case Ge (_) => z3Ctx.mkGE(symVToZ3(lhs), symVToZ3(rhs))
62
+ case Gt (_) => z3Ctx.mkGT(symVToZ3(lhs), symVToZ3(rhs))
63
+ case Le (_) => z3Ctx.mkLE(symVToZ3(lhs), symVToZ3(rhs))
64
+ case Lt (_) => z3Ctx.mkLT(symVToZ3(lhs), symVToZ3(rhs))
65
+ }
66
+ // convert resutl to int
67
+ z3Ctx.mkITE(res, z3Ctx.mkInt(1 , intSort), z3Ctx.mkInt(0 , intSort))
68
+ case _ => throw new NotImplementedError (s " Unsupported SymVal: $symV" )
69
+ }
70
+
71
+ def getIndexOfSym (sym : String ): Int = {
72
+ val pattern = " .*_(\\ d+)$" .r
73
+ sym match {
74
+ case pattern(index) => index.toInt
75
+ case _ => throw new IllegalArgumentException (s " Invalid symbol format: $sym" )
76
+ }
36
77
}
78
+
37
79
def condToZ3 (cond : Cond ): Z3AST = cond match {
38
80
case CondEqz (v) => z3Ctx.mkEq(symVToZ3(v), z3Ctx.mkInt(0 , intSort))
39
81
case Not (cond) => z3Ctx.mkNot(condToZ3(cond))
@@ -49,74 +91,93 @@ object ConcolicDriver {
49
91
}
50
92
51
93
// solve for all vars
94
+ println(s " solving constraints: ${solver.toString()}" )
52
95
solver.check() match {
53
96
case Some (true ) => {
54
97
val model = solver.getModel()
55
98
val vars = model.getConsts
56
- val env = HashMap ()
99
+ val env = HashMap [ Int , Value ] ()
57
100
for (v <- vars) {
58
101
val name = v.getName
59
102
val ast = z3Ctx.mkConst(name, intSort)
60
103
val value = model.eval(ast)
104
+ println(s " name: $name" )
105
+ println(s " value: $value" )
106
+ // TODO: support other types of symbolic values(currently only i32)
61
107
val intValue = if (value.isDefined && value.get.getSort.isIntSort) {
62
- I32V (value.toString.toInt)
108
+ val negPattern = """ \(\-\s*(\d+)\)""" .r
109
+ val plainPattern = """ (-?\d+)""" .r
110
+ val num = value.get.toString match {
111
+ case negPattern(digits) => - digits.toInt
112
+ case plainPattern(number) => number.toInt
113
+ case _ => throw new IllegalArgumentException (" Invalid format" )
114
+ }
115
+ I32V (num)
63
116
} else {
64
117
???
65
118
}
66
- // env += (name.toString -> intValue)
67
- ???
119
+ env += (getIndexOfSym(name.toString) -> intValue)
68
120
}
69
- ???
70
- // env
121
+ println( s " solved env: $env " )
122
+ Some ( env)
71
123
}
72
- case _ => ???
124
+ case _ => None
73
125
}
74
126
}
75
127
76
128
def negateCond (conds : List [Cond ], i : Int ): List [Cond ] = {
77
- ???
129
+ conds(i).negated :: conds.drop(i + 1 )
78
130
}
79
131
80
132
def checkPCToFile (pc : List [Cond ]): Unit = {
133
+ // TODO: what this function for?
81
134
???
82
135
}
83
136
84
137
def exec (module : Module , mainFun : String , startEnv : HashMap [Int , Value ])(implicit z3Ctx : Z3Context ) = {
85
138
val worklist = Queue (startEnv)
86
- // val visited = ??? // how to avoid re-execution
87
-
139
+ val unreachables = HashSet [ExploreTree ]()
140
+ val visited = HashSet [ExploreTree ]()
141
+ // the root node of exploration tree
142
+ val root = new ExploreTree ()
88
143
def loop (worklist : Queue [HashMap [Int , Value ]]): Unit = worklist match {
89
144
case Queue () => ()
90
145
case env +: rest => {
91
146
val moduleInst = ModuleInstance (module)
92
147
Evaluator (moduleInst).execWholeProgram(
93
148
Some (mainFun),
94
149
env,
95
- (_endStack, _endSymStack, pathConds) => {
96
- println(s " env: $env" )
97
- val newEnv = condsToEnv(pathConds)
98
- val newWork = for (i <- 0 until pathConds.length) yield {
99
- val newConds = negateCond(pathConds, i)
100
- checkPCToFile(newConds)
101
- condsToEnv(newConds)
150
+ root,
151
+ (_endStack, _endSymStack, tree) => {
152
+ tree.fillWithFinished()
153
+ val unexploredTrees = root.unexploredTrees()
154
+ // if a node is already visited or marked as unreachable, don't try to explore it
155
+ val addedNewWork = unexploredTrees.filterNot(unreachables.contains)
156
+ .filterNot(visited.contains)
157
+ .flatMap { tree =>
158
+ val conds = tree.collectConds()
159
+ val newEnv = condsToEnv(conds)
160
+ // if the path conditions to reach this node are unsatisfiable, mark it as unreachable.
161
+ if (newEnv.isEmpty) unreachables.add(tree)
162
+ newEnv
163
+ }
164
+ for (tree <- unexploredTrees) {
165
+ visited.add(tree)
102
166
}
103
- loop(rest ++ newWork )
167
+ loop(rest ++ addedNewWork )
104
168
}
105
169
)
106
170
}
107
171
}
108
172
109
173
loop(worklist)
174
+ println(s " unreachable trees number: ${unreachables.size}" )
175
+ println(s " number of normal explored paths: ${root.finishedTrees().size}" )
176
+ val failedTrees = root.failedTrees()
177
+ println(s " number of failed explored paths: ${failedTrees.size}" )
178
+ for (tree <- failedTrees) {
179
+ println(s " find a failed endpoint: ${tree}" )
180
+ }
181
+ println(s " exploration tree: ${root.toString}" )
110
182
}
111
183
}
112
-
113
- object DriverSimpleTest {
114
- def fileTestDriver (file : String , mainFun : String , startEnv : HashMap [Int , Value ]) = {
115
- import gensym .wasm .concolicminiwasm ._
116
- import collection .mutable .ArrayBuffer
117
- val module = Parser .parseFile(file)
118
- ConcolicDriver .exec(module, mainFun, startEnv)(new Z3Context ())
119
- }
120
-
121
- def main (args : Array [String ]) = {}
122
- }
0 commit comments