Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
c420a2a
remove unused expressions
butterunderflow Jul 5, 2025
fb2a2c4
let's start from the staged miniwasm interpreter
butterunderflow Jul 7, 2025
27e3e32
dup all concrete operations to symbolic
butterunderflow Jul 7, 2025
2143050
maintain a symbolic stack during the execution
butterunderflow Jul 7, 2025
8d81fbe
record path conditions
butterunderflow Jul 9, 2025
61215b6
The branch node only needs to remember the positive condition.
butterunderflow Jul 9, 2025
d18b5f7
symbolic runtime for explore tree
butterunderflow Jul 13, 2025
92ab8ba
add a to graphviz method, enhancing debug experience
butterunderflow Jul 13, 2025
e1d7fc8
put symbolic expression on the SymStack
butterunderflow Jul 14, 2025
77a4e6f
`type.symbolic` instruction
butterunderflow Jul 16, 2025
314ff5f
test staged concolic compilation in CI
butterunderflow Jul 16, 2025
8739369
dump graphviz by default
butterunderflow Jul 16, 2025
9a9988c
concolic driver
butterunderflow Jul 17, 2025
9ab162f
fix: add an unreachable node & use GENSYM_ASSERT
butterunderflow Jul 18, 2025
b75a627
call z3 to solve constraints
butterunderflow Jul 19, 2025
26c9917
remove unused & resize before update environment
butterunderflow Jul 19, 2025
319cfd6
use c++20
butterunderflow Jul 23, 2025
8f45912
branch in brtable
butterunderflow Jul 23, 2025
2e2259d
use driver's entrypoint by default
butterunderflow Jul 23, 2025
2b42b27
rename package name of staged miniwasm
butterunderflow Jul 23, 2025
619a8f0
tweak
butterunderflow Jul 23, 2025
af6751a
Reuse symbolic states (#90)
butterunderflow Aug 27, 2025
731ff9e
c++17 compatible
butterunderflow Aug 27, 2025
ffa5670
fix
butterunderflow Aug 29, 2025
b57929a
revert: don't split concrete/symbolic interpreter & don't support sna…
butterunderflow Aug 29, 2025
1bdb7da
introduce a SnapshotNode, which currently behaves same as UnexploredNode
butterunderflow Aug 30, 2025
64dce32
fill snapshot into SnapshotNode
butterunderflow Aug 30, 2025
463871c
snapshot reuse via continuation
butterunderflow Aug 31, 2025
261c650
remove debug printings
butterunderflow Sep 1, 2025
1c6a045
give every branch node an ID
butterunderflow Sep 4, 2025
8971eb5
a bitmap to record the branch coverage
butterunderflow Sep 4, 2025
1b92fc0
a new exploring strategy: exit when all branches are covered
butterunderflow Sep 4, 2025
0f7ca5a
support numeric globals
butterunderflow Sep 7, 2025
0fded4c
Explicitly classify the next stage computation and its type
butterunderflow Sep 9, 2025
5656536
correct behavior for global
ahuoguo Sep 10, 2025
51544e8
make log function returning std::monostate/Unit type
butterunderflow Sep 23, 2025
4bdc93b
Symbolic memory (#91)
ahuoguo Sep 24, 2025
74732ad
fix: high bits should be concat first
butterunderflow Sep 24, 2025
e3f8488
make btree example work with concolic execution
butterunderflow Sep 29, 2025
827f2b0
preallocate pages for the memory
butterunderflow Oct 3, 2025
75b6347
remove some unperformant code
butterunderflow Oct 4, 2025
8a40d30
work list algorithm for exploration
butterunderflow Oct 5, 2025
568928c
config header; fix extract evaluation; capture by value in lambda
butterunderflow Oct 5, 2025
ee9e57b
replace SymEnv_t's underlying representation
butterunderflow Oct 5, 2025
bd5036b
compare the exploration trees (w/ vs. w/o snapshot reuse)
butterunderflow Oct 5, 2025
f63f682
accelerate test by using O0 optimization
butterunderflow Oct 6, 2025
e4ac385
add an option to use immutable data structure
butterunderflow Oct 7, 2025
6bae60f
a simple test case to show immutable's improvements
butterunderflow Oct 7, 2025
46b8a85
add a benchmark testcase
butterunderflow Oct 11, 2025
db49251
a pool to store symbolic concrete
butterunderflow Oct 11, 2025
b4d18ba
a example to show when snapshot is highly effective
butterunderflow Oct 11, 2025
960aacb
some infra to support the cost model to decide if we should create sn…
butterunderflow Oct 18, 2025
a150103
a cost model to determin if we want to create snapshot
butterunderflow Oct 18, 2025
2ad0949
update btree benchmarks
butterunderflow Oct 29, 2025
a3ad84f
rename
butterunderflow Oct 29, 2025
8a8d6d0
turn wast to wat
butterunderflow Oct 29, 2025
ea05101
use imported api for symbolic operations so they can be run by wat2wasm
butterunderflow Oct 29, 2025
c82a8e3
replace call to i32.symbolic with external call
butterunderflow Oct 29, 2025
3e6964e
a normalize script
butterunderflow Oct 29, 2025
46df39c
replace sym_assume with (call $i32.sym_assume)
butterunderflow Oct 29, 2025
6425d38
remove print_btree, it's a dummy instruction only implemented in wasp
butterunderflow Oct 29, 2025
4d8241a
replace (sym_assert) with (call $i32.sym_assert)
butterunderflow Oct 29, 2025
25cb4f2
replace the usage of get_sym_int32
butterunderflow Oct 29, 2025
18e0b6b
remove two files that are not runable by wasp
butterunderflow Oct 29, 2025
7968700
convert all wat file to a form we can parse via (wasm2wat . wat2wasm)
butterunderflow Oct 29, 2025
f88be3b
get_sym_int32 is not useful anymore
butterunderflow Oct 29, 2025
b6056d8
take get_sym_int32 back but don't use them
butterunderflow Oct 29, 2025
f51290d
a test entry that compile btree cases
butterunderflow Oct 29, 2025
832af8b
a compile script in python
butterunderflow Oct 29, 2025
94fe901
avoid some usage of fun
butterunderflow Oct 30, 2025
ae49b39
remove an unused function
butterunderflow Oct 30, 2025
2e56851
nontermination (if loop termination depends on symbolic value, it wil…
ahuoguo Oct 31, 2025
c8e629f
support sym_assume to prune subtrees
butterunderflow Nov 3, 2025
2ced354
support write profile file
butterunderflow Nov 3, 2025
1818f53
a script for running and cleaning up benchmark executables.
butterunderflow Nov 3, 2025
6dd52b2
never build an expression twice
butterunderflow Nov 4, 2025
7cdfe53
update sym_assert and timer
butterunderflow Nov 5, 2025
efb8144
refactor operator to explicit methods
butterunderflow Nov 6, 2025
0552131
fixed a bug, zero initialize local variables
butterunderflow Nov 7, 2025
6744d18
use z3's api to evaluate a symbolic expression
butterunderflow Nov 8, 2025
5f65669
let z3 decide the value of symbols that are not in the model
butterunderflow Nov 8, 2025
4894829
tweak tests
butterunderflow Nov 8, 2025
da5efc8
update test script
butterunderflow Nov 10, 2025
9d2d23b
support some f32
butterunderflow Nov 10, 2025
3a7bdd7
support call_indirect
butterunderflow Nov 11, 2025
43e1c8e
final call_indirect
butterunderflow Nov 11, 2025
556f478
misc
butterunderflow Nov 11, 2025
a841ee5
up
butterunderflow Nov 11, 2025
9a1088b
fix
butterunderflow Nov 11, 2025
2fdc953
up btree for wasp
butterunderflow Nov 11, 2025
cf6b689
update collection-c benchmark
butterunderflow Nov 11, 2025
b382fe0
add import
butterunderflow Nov 11, 2025
4f30294
i32.symbolic
butterunderflow Nov 11, 2025
fb61bfb
sym_assume is call 1
butterunderflow Nov 11, 2025
c5a1392
sym_assert is call 2
butterunderflow Nov 11, 2025
1fce3a4
alloc and free wasp primitive
butterunderflow Nov 11, 2025
5f9a65f
support some operations
butterunderflow Nov 11, 2025
beb8647
tweak
butterunderflow Nov 11, 2025
f926137
a script to run wasp on btree benchmarks
butterunderflow Nov 11, 2025
0b3cfea
data section initialize
butterunderflow Nov 11, 2025
a4cd4a5
revert something
butterunderflow Nov 12, 2025
c880bd0
more operations
butterunderflow Nov 12, 2025
a497597
try not to build symbolic expressions when all operands are concrete
butterunderflow Nov 12, 2025
7c3878a
a crafted benchmark
butterunderflow Nov 13, 2025
5f97900
snapshot benchmark
butterunderflow Nov 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ jobs:
sbt 'testOnly gensym.wasm.TestConcolic'
sbt 'testOnly gensym.wasm.TestDriver'
sbt 'testOnly gensym.wasm.TestStagedEval'
sbt 'testOnly gensym.wasm.TestStagedConcolicEval'
1 change: 1 addition & 0 deletions benchmarks/wasm/branch-strip-buggy.wat
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
else
i32.const 0
call 2
i32.const 1 ;; to satisfy the type checker, this line will never be reached
end
end
)
Expand Down
22 changes: 22 additions & 0 deletions benchmarks/wasm/staged/brtable_concolic.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(module $brtable
(global (;0;) (mut i32) (i32.const 1048576))
(type (;0;) (func (param i32)))
(func (;0;) (type 1) (result i32)
i32.const 2
(block
(block
(block
i32.const 0
i32.symbolic
br_table 0 1 2 0 ;; br_table will consume an element from the stack
)
i32.const 1
call 1
br 1
)
i32.const 0
call 1
)
)
(import "console" "assert" (func (type 0)))
(start 0))
4 changes: 3 additions & 1 deletion headers/wasm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
#define WASM_HEADERS

#include "wasm/concrete_rt.hpp"

#include "wasm/symbolic_rt.hpp"
#include "wasm/concolic_driver.hpp"
#include "wasm/utils.hpp"
#endif
105 changes: 105 additions & 0 deletions headers/wasm/concolic_driver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#ifndef CONCOLIC_DRIVER_HPP
#define CONCOLIC_DRIVER_HPP

#include "concrete_rt.hpp"
#include "smt_solver.hpp"
#include "symbolic_rt.hpp"
#include <functional>
#include <ostream>
#include <string>
#include <vector>

class ConcolicDriver {
friend class ManagedConcolicCleanup;

public:
ConcolicDriver(std::function<void()> entrypoint, std::string tree_file)
: entrypoint(entrypoint), tree_file(tree_file) {}
ConcolicDriver(std::function<void()> entrypoint)
: entrypoint(entrypoint), tree_file(std::nullopt) {}
void run();

private:
Solver solver;
std::function<void()> entrypoint;
std::optional<std::string> tree_file;
};

class ManagedConcolicCleanup {
const ConcolicDriver &driver;

public:
ManagedConcolicCleanup(const ConcolicDriver &driver) : driver(driver) {}
~ManagedConcolicCleanup() {
if (driver.tree_file.has_value())
ExploreTree.dump_graphviz(driver.tree_file.value());
}
};

inline void ConcolicDriver::run() {
ManagedConcolicCleanup cleanup{*this};
while (true) {
ExploreTree.reset_cursor();

auto unexplored = ExploreTree.pick_unexplored();
if (!unexplored) {
std::cout << "No unexplored nodes found, exiting..." << std::endl;
return;
}
auto cond = unexplored->collect_path_conds();
auto result = solver.solve(cond);
if (!result.has_value()) {
// TODO: current implementation is buggy, there could be other reachable
// unexplored paths
std::cout << "Found an unreachable path, marking it as unreachable..."
<< std::endl;
unexplored->fillUnreachableNode();
continue;
}
auto new_env = result.value();
SymEnv.update(std::move(new_env));
try {
entrypoint();
std::cout << "Execution finished successfully with symbolic environment:"
<< std::endl;
std::cout << SymEnv.to_string() << std::endl;
} catch (...) {
ExploreTree.fillFailedNode();
std::cout << "Caught runtime error with symbolic environment:"
<< std::endl;
std::cout << SymEnv.to_string() << std::endl;
return;
}
}
}

static std::monostate reset_stacks() {
Stack.reset();
Frames.reset();
SymStack.reset();
SymFrames.reset();
initRand();
Memory = Memory_t(1);
return std::monostate{};
}

static void start_concolic_execution_with(
std::function<std::monostate(std::monostate)> entrypoint,
std::string tree_file) {
ConcolicDriver driver([=]() { entrypoint(std::monostate{}); }, tree_file);
driver.run();
}

static void start_concolic_execution_with(
std::function<std::monostate(std::monostate)> entrypoint) {

const char *env_tree_file = std::getenv("TREE_FILE");

ConcolicDriver driver =
env_tree_file ? ConcolicDriver([=]() { entrypoint(std::monostate{}); },
env_tree_file)
: ConcolicDriver([=]() { entrypoint(std::monostate{}); });
driver.run();
}

#endif // CONCOLIC_DRIVER_HPP
16 changes: 12 additions & 4 deletions headers/wasm/concrete_rt.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#ifndef WASM_CONCRETE_RT_HPP
#define WASM_CONCRETE_RT_HPP

#include <cassert>
#include <cstdint>
#include <cstdio>
Expand Down Expand Up @@ -49,8 +52,6 @@ static Num I32V(int v) { return v; }

static Num I64V(int64_t v) { return v; }

using Slice = std::vector<Num>;

const int STACK_SIZE = 1024 * 64;

class Stack_t {
Expand Down Expand Up @@ -115,9 +116,12 @@ class Stack_t {
}

void initialize() {
// do nothing for now
// todo: remove this method
reset();
}

void reset() { count = 0; }

private:
int32_t count;
Num *stack_ptr;
Expand Down Expand Up @@ -148,6 +152,8 @@ class Frames_t {
count += size;
}

void reset() { count = 0; }

private:
int32_t count;
Num *stack_ptr;
Expand Down Expand Up @@ -200,4 +206,6 @@ struct Memory_t {
}
};

static Memory_t Memory(1); // 1 page memory
static Memory_t Memory(1); // 1 page memory

#endif // WASM_CONCRETE_RT_HPP
126 changes: 126 additions & 0 deletions headers/wasm/smt_solver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#ifndef SMT_SOLVER_HPP
#define SMT_SOLVER_HPP

#include "concrete_rt.hpp"
#include "symbolic_rt.hpp"
#include "z3++.h"
#include <array>
#include <set>
#include <string>
#include <tuple>
#include <vector>

class Solver {
public:
Solver() {}
std::optional<std::vector<Num>> solve(const std::vector<SymVal> &conditions) {
// make an conjunction of all conditions
z3::expr conjunction = z3_ctx.bool_val(true);
for (const auto &cond : conditions) {
auto z3_cond = build_z3_expr(cond);
conjunction = conjunction && z3_cond != z3_ctx.bv_val(0, 32);
}
#ifdef DEBUG
std::cout << "Symbolic conditions size: " << conditions.size() << std::endl;
std::cout << "Solving conditions: " << conjunction << std::endl;
#endif
// call z3 to solve the condition
z3::solver z3_solver(z3_ctx);
z3_solver.add(conjunction);
switch (z3_solver.check()) {
case z3::unsat:
return std::nullopt; // No solution found
case z3::sat: {
z3::model model = z3_solver.get_model();
std::vector<Num> result;
// Reference:
// https://github.com/Z3Prover/z3/blob/master/examples/c%2B%2B/example.cpp#L59

std::cout << "Solved Z3 model" << std::endl << model << std::endl;
for (unsigned i = 0; i < model.size(); ++i) {
z3::func_decl var = model[i];
z3::expr value = model.get_const_interp(var);
std::string name = var.name().str();
if (name.starts_with("s_")) {
int id = std::stoi(name.substr(2));
if (id >= result.size()) {
result.resize(id + 1);
}
result[id] = Num(value.get_numeral_int64());
} else {
std::cout << "Find a variable that is not created by GenSym: " << name
<< std::endl;
}
}
return result;
}
case z3::unknown:
throw std::runtime_error("Z3 solver returned unknown status");
}
return std::nullopt; // Should not reach here
}

private:
z3::context z3_ctx;
z3::expr build_z3_expr(const SymVal &sym_val);
};

inline z3::expr Solver::build_z3_expr(const SymVal &sym_val) {
if (auto sym = std::dynamic_pointer_cast<Symbol>(sym_val.symptr)) {
return z3_ctx.bv_const(("s_" + std::to_string(sym->get_id())).c_str(), 32);
} else if (auto concrete =
std::dynamic_pointer_cast<SymConcrete>(sym_val.symptr)) {
return z3_ctx.bv_val(concrete->value.value, 32);
} else if (auto binary =
std::dynamic_pointer_cast<SymBinary>(sym_val.symptr)) {
auto bit_width = 32;
z3::expr zero_bv =
z3_ctx.bv_val(0, bit_width); // Represents 0 as a 32-bit bitvector
z3::expr one_bv =
z3_ctx.bv_val(1, bit_width); // Represents 1 as a 32-bit bitvector

z3::expr left = build_z3_expr(binary->lhs);
z3::expr right = build_z3_expr(binary->rhs);
// TODO: make sure the semantics of these operations are aligned with wasm
switch (binary->op) {
case EQ: {
auto temp_bool = left == right;
return z3::ite(temp_bool, one_bv, zero_bv);
}
case NEQ: {
auto temp_bool = left != right;
return z3::ite(temp_bool, one_bv, zero_bv);
}
case LT: {
auto temp_bool = left < right;
return z3::ite(temp_bool, one_bv, zero_bv);
}
case LEQ: {
auto temp_bool = left <= right;
return z3::ite(temp_bool, one_bv, zero_bv);
}
case GT: {
auto temp_bool = left > right;
return z3::ite(temp_bool, one_bv, zero_bv);
}
case GEQ: {
auto temp_bool = left >= right;
return z3::ite(temp_bool, one_bv, zero_bv);
}
case ADD: {
return left + right;
}
case SUB: {
return left - right;
}
case MUL: {
return left * right;
}
case DIV: {
return left / right;
}
}
}
throw std::runtime_error("Unsupported symbolic value type");
}
#endif // SMT_SOLVER_HPP
Loading
Loading