Skip to content

Commit 2d82a0f

Browse files
config header; fix extract evaluation; capture by value in lambda
1 parent 8a40d30 commit 2d82a0f

File tree

6 files changed

+167
-124
lines changed

6 files changed

+167
-124
lines changed

headers/wasm/concolic_driver.hpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
#define CONCOLIC_DRIVER_HPP
33

44
#include "concrete_rt.hpp"
5+
#include "config.hpp"
6+
#include "profile.hpp"
57
#include "smt_solver.hpp"
68
#include "symbolic_rt.hpp"
79
#include "utils.hpp"
8-
#include "wasm/profile.hpp"
910
#include <cassert>
1011
#include <chrono>
1112
#include <functional>
@@ -15,16 +16,6 @@
1516
#include <string>
1617
#include <vector>
1718

18-
enum class ExploreMode { EarlyExit, ExitByCoverage };
19-
20-
#ifdef EARLY_EXIT
21-
static const ExploreMode EXPLORE_MODE = ExploreMode::EarlyExit;
22-
#elif defined(BY_COVERAGE)
23-
static const ExploreMode EXPLORE_MODE = ExploreMode::ExitByCoverage;
24-
#else
25-
static const ExploreMode EXPLORE_MODE = ExploreMode::EarlyExit;
26-
#endif
27-
2819
class ConcolicDriver {
2920
friend class ManagedConcolicCleanup;
3021

@@ -107,12 +98,14 @@ inline void ConcolicDriver::main_exploration_loop() {
10798
try {
10899
GENSYM_INFO("Now execute the program with symbolic environment: ");
109100
GENSYM_INFO(SymEnv.to_string());
110-
if (auto snapshot_node =
111-
dynamic_cast<SnapshotNode *>(node->node.get())) {
101+
if (auto snapshot_node = dynamic_cast<SnapshotNode *>(node->node.get())) {
102+
assert(REUSE_SNAPSHOT);
103+
auto timer = ManagedTimer();
112104
snapshot_node->get_snapshot().resume_execution(SymEnv, node);
113105
} else {
114106
auto timer = ManagedTimer();
115107
reset_stacks();
108+
ExploreTree.reset_cursor();
116109
entrypoint();
117110
}
118111

@@ -134,6 +127,7 @@ inline void ConcolicDriver::main_exploration_loop() {
134127
GENSYM_INFO(
135128
"Found a bug, but not all branches covered, continuing...");
136129
}
130+
std::cout << e.what() << std::endl;
137131
}
138132
}
139133
#if defined(RUN_ONCE)
@@ -143,18 +137,18 @@ inline void ConcolicDriver::main_exploration_loop() {
143137
}
144138

145139
inline void ConcolicDriver::run() {
146-
ExploreTree.reset_cursor();
147140
main_exploration_loop();
148141
Profile.print_summary();
149142
}
150143

151144
static std::monostate reset_stacks() {
152145
Stack.reset();
153-
Frames.reset();
154146
SymStack.reset();
147+
Frames.reset();
155148
SymFrames.reset();
156-
initRand();
157149
Memory.reset();
150+
SymMemory.reset();
151+
initRand();
158152
return std::monostate{};
159153
}
160154

headers/wasm/concrete_rt.hpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,31 @@
1616
struct Num {
1717
Num(int64_t value) : value(value) {}
1818
Num() : value(0) {}
19-
int64_t value;
20-
int32_t toInt() { return static_cast<int32_t>(value); }
19+
int32_t value;
20+
int32_t toInt() const { return static_cast<int32_t>(value); }
2121

22-
bool operator==(const Num &other) const { return value == other.value; }
22+
// TODO: support different bit width operations, for now we just assume all
23+
// oprands are i32
24+
bool operator==(const Num &other) const { return toInt() == other.toInt(); }
2325
bool operator!=(const Num &other) const { return !(*this == other); }
24-
Num operator+(const Num &other) const { return Num(value + other.value); }
25-
Num operator-(const Num &other) const { return Num(value - other.value); }
26-
Num operator*(const Num &other) const { return Num(value * other.value); }
26+
Num operator+(const Num &other) const { return Num(toInt() + other.toInt()); }
27+
Num operator-(const Num &other) const { return Num(toInt() - other.toInt()); }
28+
Num operator*(const Num &other) const { return Num(toInt() * other.toInt()); }
2729
Num operator/(const Num &other) const {
28-
if (other.value == 0) {
30+
if (other.toInt() == 0) {
2931
throw std::runtime_error("Division by zero");
3032
}
31-
return Num(value / other.value);
33+
return Num(toInt() / other.toInt());
3234
}
33-
Num operator<(const Num &other) const { return Num(value < other.value); }
34-
Num operator<=(const Num &other) const { return Num(value <= other.value); }
35-
Num operator>(const Num &other) const { return Num(value > other.value); }
36-
Num operator>=(const Num &other) const { return Num(value >= other.value); }
37-
Num operator&(const Num &other) const { return Num(value & other.value); }
35+
Num operator<(const Num &other) const { return Num(toInt() < other.toInt()); }
36+
Num operator<=(const Num &other) const {
37+
return Num(toInt() <= other.toInt());
38+
}
39+
Num operator>(const Num &other) const { return Num(toInt() > other.toInt()); }
40+
Num operator>=(const Num &other) const {
41+
return Num(toInt() >= other.toInt());
42+
}
43+
Num operator&(const Num &other) const { return Num(toInt() & other.toInt()); }
3844
};
3945

4046
static Num I32V(int v) { return v; }
@@ -71,8 +77,9 @@ class Stack_t {
7177
Profile.step(ProfileKind::POP);
7278
#ifdef DEBUG
7379
assert(count > 0 && "Stack underflow");
74-
printf("[Debug] popping from stack, size of concrete stack is: %d\n",
75-
count);
80+
printf("[Debug] popping a value %ld from stack, size of concrete stack is: "
81+
"%d\n",
82+
stack_ptr[count - 1].value, count);
7683
#endif
7784
Num num = stack_ptr[count - 1];
7885
count--;
@@ -223,6 +230,10 @@ struct Memory_t {
223230
page_count(init_page_count), allocated_pages(PRE_ALLOC_PAGES) {}
224231

225232
int32_t loadInt(int32_t base, int32_t offset) {
233+
#ifdef DEBUG
234+
std::cout << "[Debug] loading int from memory at address: "
235+
<< (base + offset) << std::endl;
236+
#endif
226237
// just load a 4-byte integer from memory of the vector
227238
int32_t addr = base + offset;
228239
if (!(addr + 3 < memory.size())) {
@@ -238,8 +249,14 @@ struct Memory_t {
238249

239250
std::monostate storeInt(int32_t base, int32_t offset, int32_t value) {
240251
int32_t addr = base + offset;
252+
#ifdef DEBUG
253+
std::cout << "[Debug] storing int " << value << " to memory at address "
254+
<< addr << std::endl;
255+
#endif
241256
// Ensure we don't write out of bounds
242-
assert(addr + 3 < memory.size());
257+
if (!(addr + 3 < memory.size())) {
258+
throw std::runtime_error("Invalid memory access " + std::to_string(addr));
259+
}
243260
for (int i = 0; i < 4; ++i) {
244261
memory[addr + i] = static_cast<uint8_t>((value >> (8 * i)) & 0xFF);
245262
// Optionally, update memory[addr + i].second (SymVal) if needed

headers/wasm/config.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#ifndef CONFIG_HPP
2+
#define CONFIG_HPP
3+
4+
// This file contains configuration settings for the concolic execution
5+
6+
// If ENABLE_PROFILE defined, the compiled program will collect and print
7+
// profiling information
8+
#ifdef ENABLE_PROFILE
9+
const bool PROFILE_ENABLED = true;
10+
#else
11+
const bool PROFILE_ENABLED = false;
12+
#endif
13+
14+
// This variable define when concolic execution will stop
15+
enum class ExploreMode {
16+
EarlyExit, // Stop at the first error encountered
17+
18+
ExitByCoverage // Exit when all syntactic branches are covered
19+
};
20+
21+
#ifdef EARLY_EXIT
22+
static const ExploreMode EXPLORE_MODE = ExploreMode::EarlyExit;
23+
#elif defined(BY_COVERAGE)
24+
static const ExploreMode EXPLORE_MODE = ExploreMode::ExitByCoverage;
25+
#else
26+
static const ExploreMode EXPLORE_MODE = ExploreMode::EarlyExit;
27+
#endif
28+
29+
// This variable decides whether we enable the snapshot reuse optimization
30+
#ifdef NO_REUSE
31+
static const bool REUSE_SNAPSHOT = false;
32+
#else
33+
static const bool REUSE_SNAPSHOT = true;
34+
#endif
35+
36+
#endif // CONFIG_HPP

headers/wasm/profile.hpp

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define PROFILE_HPP
33

44
#include "utils.hpp"
5+
#include "config.hpp"
56
#include <array>
67
#include <chrono>
78
#include <iomanip>
@@ -27,59 +28,57 @@ class Profile_t {
2728
public:
2829
Profile_t() : step_count(0) {}
2930
std::monostate step() {
30-
#ifdef ENABLE_PROFILE
31-
step_count++;
32-
#endif
31+
if (PROFILE_ENABLED)
32+
step_count++;
3333
return std::monostate();
3434
}
3535
std::monostate step(ProfileKind op) {
36-
#ifdef ENABLE_PROFILE
37-
op_count[static_cast<std::size_t>(op)]++;
38-
#endif
36+
if (PROFILE_ENABLED)
37+
op_count[static_cast<std::size_t>(op)]++;
3938
return std::monostate();
4039
}
4140
void print_summary() {
42-
#ifdef ENABLE_PROFILE
43-
std::cout << "Profile Summary:" << std::endl;
44-
std::cout << "Total PUSH operations: "
45-
<< op_count[static_cast<std::size_t>(ProfileKind::PUSH)]
46-
<< std::endl;
47-
std::cout << "Total POP operations: "
48-
<< op_count[static_cast<std::size_t>(ProfileKind::POP)]
49-
<< std::endl;
50-
std::cout << "Total PEEK operations: "
51-
<< op_count[static_cast<std::size_t>(ProfileKind::PEEK)]
52-
<< std::endl;
53-
std::cout << "Total SHIFT operations: "
54-
<< op_count[static_cast<std::size_t>(ProfileKind::SHIFT)]
55-
<< std::endl;
56-
std::cout << "Total SET operations: "
57-
<< op_count[static_cast<std::size_t>(ProfileKind::SET)]
58-
<< std::endl;
59-
std::cout << "Total GET operations: "
60-
<< op_count[static_cast<std::size_t>(ProfileKind::GET)]
61-
<< std::endl;
62-
std::cout << "Total BINARY operations: "
63-
<< op_count[static_cast<std::size_t>(ProfileKind::BINARY)]
64-
<< std::endl;
65-
std::cout << "Total TREE_FILL operations: "
66-
<< op_count[static_cast<std::size_t>(ProfileKind::TREE_FILL)]
67-
<< std::endl;
68-
std::cout << "Total CURSOR_MOVE operations: "
69-
<< op_count[static_cast<std::size_t>(ProfileKind::CURSOR_MOVE)]
70-
<< std::endl;
71-
std::cout << "Total other instructions executed: " << step_count
72-
<< std::endl;
73-
std::cout << "Total MEM_GROW operations: "
74-
<< op_count[static_cast<std::size_t>(ProfileKind::MEM_GROW)]
75-
<< std::endl;
76-
std::cout
77-
<< "Total SNAPSHOT_CREATE operations: "
78-
<< op_count[static_cast<std::size_t>(ProfileKind::SNAPSHOT_CREATE)]
79-
<< std::endl;
80-
std::cout << "Total time for instruction execution (s): "
81-
<< std::setprecision(15) << execution_time << std::endl;
82-
#endif
41+
if (PROFILE_ENABLED) {
42+
std::cout << "Profile Summary:" << std::endl;
43+
std::cout << "Total PUSH operations: "
44+
<< op_count[static_cast<std::size_t>(ProfileKind::PUSH)]
45+
<< std::endl;
46+
std::cout << "Total POP operations: "
47+
<< op_count[static_cast<std::size_t>(ProfileKind::POP)]
48+
<< std::endl;
49+
std::cout << "Total PEEK operations: "
50+
<< op_count[static_cast<std::size_t>(ProfileKind::PEEK)]
51+
<< std::endl;
52+
std::cout << "Total SHIFT operations: "
53+
<< op_count[static_cast<std::size_t>(ProfileKind::SHIFT)]
54+
<< std::endl;
55+
std::cout << "Total SET operations: "
56+
<< op_count[static_cast<std::size_t>(ProfileKind::SET)]
57+
<< std::endl;
58+
std::cout << "Total GET operations: "
59+
<< op_count[static_cast<std::size_t>(ProfileKind::GET)]
60+
<< std::endl;
61+
std::cout << "Total BINARY operations: "
62+
<< op_count[static_cast<std::size_t>(ProfileKind::BINARY)]
63+
<< std::endl;
64+
std::cout << "Total TREE_FILL operations: "
65+
<< op_count[static_cast<std::size_t>(ProfileKind::TREE_FILL)]
66+
<< std::endl;
67+
std::cout << "Total CURSOR_MOVE operations: "
68+
<< op_count[static_cast<std::size_t>(ProfileKind::CURSOR_MOVE)]
69+
<< std::endl;
70+
std::cout << "Total other instructions executed: " << step_count
71+
<< std::endl;
72+
std::cout << "Total MEM_GROW operations: "
73+
<< op_count[static_cast<std::size_t>(ProfileKind::MEM_GROW)]
74+
<< std::endl;
75+
std::cout
76+
<< "Total SNAPSHOT_CREATE operations: "
77+
<< op_count[static_cast<std::size_t>(ProfileKind::SNAPSHOT_CREATE)]
78+
<< std::endl;
79+
std::cout << "Total time for instruction execution (s): "
80+
<< std::setprecision(15) << execution_time << std::endl;
81+
}
8382
}
8483

8584
// record the time spent in main instruction execution, in seconds

0 commit comments

Comments
 (0)