Skip to content

Commit 6744d18

Browse files
use z3's api to evaluate a symbolic expression
1 parent 0552131 commit 6744d18

File tree

3 files changed

+146
-40
lines changed

3 files changed

+146
-40
lines changed

headers/wasm/concolic_driver.hpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,27 @@ inline void ConcolicDriver::main_exploration_loop() {
9696
node->fillUnreachableNode();
9797
continue;
9898
}
99-
auto new_env = result.value();
99+
auto &new_env = result.value().first;
100+
auto &model = result.value().second;
100101

101102
// update global symbolic environment from SMT solved model
102103
SymEnv.update(std::move(new_env));
103104
try {
104105
GENSYM_INFO("Now execute the program with symbolic environment: ");
105106
GENSYM_INFO(SymEnv.to_string());
106107
if (REUSE_SNAPSHOT) {
107-
node->reach_here(entrypoint);
108+
if (auto snapshot = dynamic_cast<SnapshotNode *>(node->node.get())) {
109+
assert(REUSE_SNAPSHOT);
110+
auto snap = snapshot->get_snapshot();
111+
std::cout << "Model \n"
112+
<< model << std::endl;
113+
snap.resume_execution_by_model(node, model);
114+
} else {
115+
auto timer = ManagedTimer(TimeProfileKind::INSTR);
116+
ExploreTree.reset_cursor();
117+
reset_stacks();
118+
entrypoint();
119+
}
108120
} else {
109121
auto timer = ManagedTimer(TimeProfileKind::INSTR);
110122
ExploreTree.reset_cursor();

headers/wasm/smt_solver.hpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
class Solver {
1818
public:
1919
Solver() {}
20-
std::optional<NumMap> solve(std::vector<SymVal> &conditions) {
20+
std::optional<std::pair<NumMap, z3::model>>
21+
solve(std::vector<SymVal> &conditions) {
2122
z3::solver z3_solver(z3_ctx);
2223
z3::check_result solver_result;
2324
{
@@ -50,13 +51,15 @@ class Solver {
5051
GENSYM_INFO("Find a variable that is not created by GenSym: " + name);
5152
}
5253
}
53-
return result;
54+
return std::optional<std::pair<NumMap, z3::model>>(
55+
std::in_place, std::move(result), std::move(model));
5456
}
5557
case z3::unknown:
5658
throw std::runtime_error("Z3 solver returned unknown status");
5759
}
5860
return std::nullopt; // Should not reach here
5961
}
62+
z3::expr build_z3_expr(SymVal &sym_val);
6063

6164
private:
6265
z3::expr to_z3_conjunction(std::vector<SymVal> &conditions) {
@@ -73,7 +76,6 @@ class Solver {
7376
}
7477

7578
z3::context z3_ctx;
76-
z3::expr build_z3_expr(SymVal &sym_val);
7779
z3::expr build_z3_expr_aux(SymVal &sym_val);
7880
};
7981

@@ -158,14 +160,22 @@ inline z3::expr Solver::build_z3_expr_aux(SymVal &sym_val) {
158160
}
159161

160162
inline z3::expr Solver::build_z3_expr(SymVal &sym_val) {
161-
if (sym_val.z3_expr) {
162-
return *sym_val.z3_expr;
163+
if (sym_val.symptr->z3_expr()) {
164+
return *sym_val.symptr->z3_expr();
163165
}
164166
auto e = build_z3_expr_aux(sym_val);
165-
sym_val.z3_expr = std::make_shared<z3::expr>(e);
167+
sym_val.symptr->update_z3_expr(e);
166168
return e;
167169
}
168170

171+
inline EvalRes eval_sym_expr_by_model(const SymVal &sym, z3::model &model) {
172+
auto expr = solver.build_z3_expr(const_cast<SymVal &>(sym));
173+
z3::expr value = model.eval(expr, false);
174+
// every value is bitvector
175+
int width = expr.get_sort().bv_size();
176+
return EvalRes(Num(value.get_numeral_int64()), width);
177+
}
178+
169179
inline std::monostate GENSYM_SYM_ASSERT(SymVal &sym_cond) {
170180
std::vector<SymVal> conds = ExploreTree.collect_current_path_conds();
171181
conds.push_back(sym_cond.negate());

headers/wasm/symbolic_rt.hpp

Lines changed: 116 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class Symbolic {
3232
Symbolic() {}
3333
virtual ~Symbolic() = default; // Make Symbolic polymorphic
3434
virtual int size() = 0;
35+
virtual std::optional<z3::expr> z3_expr() { return _z3_expr; }
36+
virtual void update_z3_expr(z3::expr expr) { _z3_expr = expr; }
37+
38+
private:
39+
std::optional<z3::expr> _z3_expr;
3540
};
3641

3742
static int max_id = 0;
@@ -91,7 +96,6 @@ static std::shared_ptr<SmallBV> ZeroByte =
9196

9297
struct SymVal {
9398
std::shared_ptr<Symbolic> symptr;
94-
std::shared_ptr<z3::expr> z3_expr;
9599

96100
SymVal() : symptr(ZERO) {}
97101
SymVal(std::shared_ptr<Symbolic> symptr) : symptr(symptr) {}
@@ -147,20 +151,34 @@ struct SymExtract : Symbolic {
147151
SymVal value;
148152
int high;
149153
int low;
154+
int size_cache = -1;
150155

151156
SymExtract(SymVal value, int high, int low)
152157
: value(value), high(high), low(low) {}
153-
int size() override { return 1 + value.size(); }
158+
int size() override {
159+
if (size_cache != -1) {
160+
return size_cache;
161+
}
162+
size_cache = 1 + value.size();
163+
return size_cache;
164+
}
154165
};
155166

156167
struct SymBinary : Symbolic {
157168
Operation op;
158169
SymVal lhs;
159170
SymVal rhs;
171+
int size_cache = -1;
160172

161173
SymBinary(Operation op, SymVal lhs, SymVal rhs)
162174
: op(op), lhs(lhs), rhs(rhs) {}
163-
int size() override { return 1 + lhs.size() + rhs.size(); }
175+
int size() override {
176+
if (size_cache != -1) {
177+
return size_cache;
178+
}
179+
size_cache = 1 + lhs.size() + rhs.size();
180+
return size_cache;
181+
}
164182
};
165183

166184
inline SymVal SymVal::add(const SymVal &other) const {
@@ -517,6 +535,8 @@ class Snapshot_t {
517535
SymMemory_t get_memory() const { return memory; }
518536

519537
std::monostate resume_execution(NodeBox *node) const;
538+
std::monostate resume_execution_by_model(NodeBox *node,
539+
z3::model &model) const;
520540

521541
static int cost_of_snapshot();
522542

@@ -527,6 +547,7 @@ class Snapshot_t {
527547
// The continuation at the snapshot point
528548
Cont_t cont;
529549
MCont_t mcont;
550+
void restore_states_to_global() const;
530551
};
531552

532553
static SymFrames_t SymFrames;
@@ -899,7 +920,10 @@ inline int Snapshot_t::cost_of_snapshot() {
899920
auto cost_of_stack_copy = SymStack.cost_of_copy();
900921
auto cost_of_frame_copy = SymFrames.cost_of_copy();
901922
auto cost_of_memory_copy = SymMemory.cost_of_copy();
902-
return 5.336 *
923+
// The speed ratio between symbolic expression instantiation and WebAssembly
924+
// instruction execution, given by benchmark results
925+
auto ratio = 5.336;
926+
return ratio *
903927
(cost_of_stack_copy + cost_of_frame_copy + cost_of_memory_copy);
904928
}
905929

@@ -968,22 +992,16 @@ class ExploreTree_t {
968992

969993
bool worth_to_create_snapshot() {
970994
if (!ENABLE_COST_MODEL) {
995+
// If we are not using cost model, always create snapshot
971996
return REUSE_SNAPSHOT;
972997
}
973998
// find out the best way to reach the current position via our cost model
974999
auto snapshot_cost = Snapshot_t::cost_of_snapshot();
975-
int reach_parent_cost = 0;
976-
if (cursor->parent) {
977-
reach_parent_cost = cursor->parent->min_cost_of_reaching_here();
978-
} else {
979-
reach_parent_cost = 0;
1000+
int re_execution_cost = cursor->instr_cost;
1001+
if (snapshot_cost <= re_execution_cost) {
1002+
GENSYM_INFO("Snapshot is worth to create");
9801003
}
981-
auto parent_cost =
982-
cursor->parent ? cursor->parent->min_cost_of_reaching_here() : 0;
983-
auto exec_from_parent_cost = reach_parent_cost + cursor->instr_cost;
984-
GENSYM_INFO("The score of snapshot tendency: " +
985-
std::to_string(exec_from_parent_cost - snapshot_cost));
986-
return snapshot_cost <= exec_from_parent_cost;
1004+
return snapshot_cost <= re_execution_cost;
9871005
}
9881006

9891007
std::monostate moveCursor(bool branch, Control control) {
@@ -994,7 +1012,9 @@ class ExploreTree_t {
9941012
if_else_node != nullptr &&
9951013
"Can't move cursor when the branch node is not initialized correctly!");
9961014
int cost_from_parent = CostManager.dump_instr_cost();
997-
cursor->instr_cost = cost_from_parent;
1015+
int cost_from_root =
1016+
cost_from_parent + (cursor->parent ? cursor->parent->instr_cost : 0);
1017+
cursor->instr_cost = cost_from_root;
9981018
if (branch) {
9991019
true_branch_cov_map[if_else_node->id] = true;
10001020
if (worth_to_create_snapshot()) {
@@ -1026,7 +1046,9 @@ class ExploreTree_t {
10261046
if_else_node != nullptr &&
10271047
"Can't move cursor when the branch node is not initialized correctly!");
10281048
int cost_from_parent = CostManager.dump_instr_cost();
1029-
cursor->instr_cost = cost_from_parent;
1049+
int cost_from_root =
1050+
cost_from_parent + (cursor->parent ? cursor->parent->instr_cost : 0);
1051+
cursor->instr_cost = cost_from_root;
10301052
if (branch) {
10311053
true_branch_cov_map[if_else_node->id] = true;
10321054
if_else_node->false_branch->fillNotToExploreNode();
@@ -1343,6 +1365,8 @@ static EvalRes eval_sym_expr(const SymVal &sym, SymEnv_t &sym_env) {
13431365
throw std::runtime_error("Not supported symbolic expression");
13441366
}
13451367

1368+
inline EvalRes eval_sym_expr_by_model(const SymVal &sym, z3::model &model);
1369+
13461370
static void resume_conc_stack(const SymStack_t &sym_stack, Stack_t &stack,
13471371
SymEnv_t &sym_env) {
13481372
stack.resize(sym_stack.size());
@@ -1354,8 +1378,21 @@ static void resume_conc_stack(const SymStack_t &sym_stack, Stack_t &stack,
13541378
}
13551379
}
13561380

1381+
static void resume_conc_stack_by_model(const SymStack_t &sym_stack,
1382+
Stack_t &stack, z3::model &model) {
1383+
GENSYM_INFO("Restoring concrete stack from symbolic stack");
1384+
stack.resize(sym_stack.size());
1385+
for (size_t i = 0; i < sym_stack.size(); ++i) {
1386+
auto sym = sym_stack[i];
1387+
auto res = eval_sym_expr_by_model(sym, model);
1388+
auto conc = res.value;
1389+
stack.set_from_front(i, conc);
1390+
}
1391+
}
1392+
13571393
static void resume_conc_frames(const SymFrames_t &sym_frame, Frames_t &frames,
13581394
SymEnv_t &sym_env) {
1395+
GENSYM_INFO("Restoring concrete frames from symbolic frames");
13591396
frames.resize(sym_frame.size());
13601397
for (size_t i = 0; i < sym_frame.size(); ++i) {
13611398
auto sym = sym_frame[i];
@@ -1366,8 +1403,22 @@ static void resume_conc_frames(const SymFrames_t &sym_frame, Frames_t &frames,
13661403
}
13671404
}
13681405

1406+
static void resume_conc_frames_by_model(const SymFrames_t &sym_frame,
1407+
Frames_t &frames, z3::model &model) {
1408+
GENSYM_INFO("Restoring concrete frames from symbolic frames");
1409+
frames.resize(sym_frame.size());
1410+
for (size_t i = 0; i < sym_frame.size(); ++i) {
1411+
auto sym = sym_frame[i];
1412+
assert(sym.symptr != nullptr);
1413+
auto res = eval_sym_expr_by_model(sym, model);
1414+
auto conc = res.value;
1415+
frames.set_from_front(i, conc);
1416+
}
1417+
}
1418+
13691419
static void resume_conc_memory(const SymMemory_t &sym_memory, Memory_t &memory,
13701420
SymEnv_t &sym_env) {
1421+
GENSYM_INFO("Restoring concrete memory from symbolic memory");
13711422
memory.reset();
13721423
for (const auto &pair : sym_memory.memory) {
13731424
int32_t addr = pair.first;
@@ -1380,6 +1431,21 @@ static void resume_conc_memory(const SymMemory_t &sym_memory, Memory_t &memory,
13801431
}
13811432
}
13821433

1434+
static void resume_conc_memory_by_model(const SymMemory_t &sym_memory,
1435+
Memory_t &memory, z3::model &model) {
1436+
GENSYM_INFO("Restoring concrete memory from symbolic memory");
1437+
memory.reset();
1438+
for (const auto &pair : sym_memory.memory) {
1439+
int32_t addr = pair.first;
1440+
SymVal sym = pair.second;
1441+
assert(sym.symptr != nullptr);
1442+
auto res = eval_sym_expr_by_model(sym, model);
1443+
auto conc = res.value;
1444+
assert(res.width == 8 && "Memory should only store bytes");
1445+
memory.store_byte(addr, conc.value & 0xFF);
1446+
}
1447+
}
1448+
13831449
static void resume_conc_states(const SymStack_t &sym_stack,
13841450
const SymFrames_t &sym_frame,
13851451
const SymMemory_t &sym_memory, Stack_t &stack,
@@ -1390,33 +1456,51 @@ static void resume_conc_states(const SymStack_t &sym_stack,
13901456
resume_conc_memory(sym_memory, memory, sym_env);
13911457
}
13921458

1393-
inline std::monostate Snapshot_t::resume_execution(NodeBox *node) const {
1394-
// Reset explore tree's cursor
1395-
ExploreTree.set_cursor(node);
1459+
static void resume_conc_states_by_model(const SymStack_t &sym_stack,
1460+
const SymFrames_t &sym_frame,
1461+
const SymMemory_t &sym_memory,
1462+
Stack_t &stack, Frames_t &frames,
1463+
Memory_t &memory, z3::model &model) {
1464+
resume_conc_stack_by_model(sym_stack, stack, model);
1465+
resume_conc_frames_by_model(sym_frame, frames, model);
1466+
resume_conc_memory_by_model(sym_memory, memory, model);
1467+
}
13961468

1469+
inline void Snapshot_t::restore_states_to_global() const {
13971470
// Restore the symbolic state from the snapshot
13981471
GENSYM_INFO("Reusing symbolic state from snapshot");
13991472
SymStack = stack;
14001473
SymFrames = frames;
14011474
SymMemory = memory;
1475+
}
1476+
1477+
inline std::monostate
1478+
Snapshot_t::resume_execution_by_model(NodeBox *node, z3::model &model) const {
1479+
// Reset explore tree's cursor and restore symbolic states
1480+
ExploreTree.set_cursor(node);
1481+
restore_states_to_global();
1482+
14021483
{
14031484
auto timer = ManagedTimer(TimeProfileKind::RESUME_SNAPSHOT);
14041485
// Restore the concrete states from the symbolic states
1405-
resume_conc_states(stack, frames, memory, Stack, Frames, Memory, SymEnv);
1486+
resume_conc_states_by_model(stack, frames, memory, Stack, Frames, Memory,
1487+
model);
14061488
}
1407-
int sym_size = 0;
1489+
// Resume execution from the continuation
1490+
auto timer = ManagedTimer(TimeProfileKind::INSTR);
1491+
return cont(mcont);
1492+
}
1493+
1494+
[[deprecated]]
1495+
inline std::monostate Snapshot_t::resume_execution(NodeBox *node) const {
1496+
// Reset explore tree's cursor and restore symbolic states
1497+
ExploreTree.set_cursor(node);
1498+
restore_states_to_global();
14081499
{
1409-
auto timer = ManagedTimer(TimeProfileKind::COUNT_SYM_SIZE);
1410-
for (size_t i = 0; i < stack.size(); ++i) {
1411-
sym_size += stack[i].size();
1412-
}
1413-
for (size_t i = 0; i < frames.size(); ++i) {
1414-
sym_size += frames[i].size();
1415-
}
1500+
auto timer = ManagedTimer(TimeProfileKind::RESUME_SNAPSHOT);
1501+
// Restore the concrete states from the symbolic states
1502+
resume_conc_states(stack, frames, memory, Stack, Frames, Memory, SymEnv);
14161503
}
1417-
std::cout << "[Info] Resumed symbolic execution from snapshot, total "
1418-
"symbolic expression and frame size: "
1419-
<< sym_size << std::endl;
14201504

14211505
// Resume execution from the continuation
14221506
auto timer = ManagedTimer(TimeProfileKind::INSTR);

0 commit comments

Comments
 (0)