Skip to content

Commit e0316da

Browse files
Kraksmkhan45
andauthored
Uninit ptr (#34)
* BST test * Added fixed BST test * added test case for uninit ptr * Partial implementation of fix for null deref aborting the whole program instead of a single path * Instead of aborting on null pointder deref, abort only the current path and create a test * some pointer testcases * updated uninitV on cpp end, made pointer arithmetic state-aware, attempted to throw exception for symvite pointers since they are uninit and possibly null * reorg test case * ptr_add has to know complete type SS * fix test cases * test both stp and z3 * try simplify backend ptr_add * move ptr_add back to value_ops.hpp * Added expected nPath for faultyBst, added additional cases to SS::update() to handle invalid pointers * added uninit ptr update and some code * finished update for uninit pointers by adding update_symloc * merged with Ruiqi's changes * argv separate test * fixed condition in get_sym_string_at * append null byte to arguments instead of uninitv * reverted unintentional value_ops changs, updated FaultyBST expected paths * reverted condition to fix argvConc, which has been fixed by a different change * Fixed flexAddr, added comments about assume * fix for assume.c, made calloc actually zero * made __calloc zero memory for PureGS * made stack arrays a different symbolic value for each index, fixed some other tests * added copynative2state constraints in PureGS to fix printf.c * Fixed bad test :/ * work on putting symbolic uninit behind a flag * fixed test with flags * made compile symbolicUninit imply runtime symbolicUninit * made tests set symbolicUninit correctly with a hack * refactor config * set symbolicUninit to false after test case * updated a test case for symbolicUninit * fixed final symbolicUninit test :/ * cleaned up symbolicUninit testcases * fixed duplicate test names * removed external_test.cpp --------- Co-authored-by: Mikail Khan <[email protected]>
1 parent 887145c commit e0316da

30 files changed

+413
-119
lines changed

benchmarks/demo-benchmarks/bst.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "klee/klee.h"
88
#endif
99

10+
// Note that this test must be run with --thread=1
11+
1012
#define SIZE 4
1113

1214
typedef struct node_s {
@@ -86,7 +88,7 @@ int main() {
8688
klee_make_symbolic(data, sizeof(data), "data");
8789
klee_make_symbolic(&key, sizeof(int), "key");
8890
#else
89-
make_symbolic(data, sizeof(int) * SIZE);
91+
make_symbolic(data, sizeof(int) * SIZE, "data");
9092
make_symbolic(&key, sizeof(int), "key");
9193
#endif
9294

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <stdio.h>
2+
#include <assert.h>
3+
#include <stdbool.h>
4+
#include <stdlib.h>
5+
#include <stdio.h>
6+
#ifdef KLEE
7+
#include "klee/klee.h"
8+
#endif
9+
10+
#define SIZE 4
11+
12+
typedef struct node_s {
13+
struct node_s* left;
14+
struct node_s* right;
15+
int val;
16+
} Node;
17+
18+
Node* init_node(int val) {
19+
Node* new_node = malloc(sizeof(Node));
20+
new_node->val = val;
21+
22+
// Because these are left uninitialized,
23+
// there may be an error in insert and find
24+
/* new_node->left = NULL; */
25+
/* new_node->right = NULL; */
26+
27+
return new_node;
28+
}
29+
30+
Node* bst_insert(Node* root, int val) {
31+
if (root == NULL) {
32+
return init_node(val);
33+
}
34+
35+
assert(root != NULL);
36+
if (root->val == -1) {
37+
return init_node(val);
38+
}
39+
40+
if (val > root->val) {
41+
root->right = bst_insert(root->right, val);
42+
return root;
43+
} else {
44+
root->left = bst_insert(root->left, val);
45+
return root;
46+
}
47+
}
48+
49+
Node* bst_from_list(int* arr, int len) {
50+
Node* root = NULL;
51+
for (int i = 0; i < len; i += 1) {
52+
root = bst_insert(root, arr[i]);
53+
}
54+
55+
return root;
56+
}
57+
58+
/* void bst_print_aux(Node* root) { */
59+
/* if (root != NULL) { */
60+
/* printf("%d ", root->val); */
61+
/* bst_print_aux(root->left); */
62+
/* bst_print_aux(root->right); */
63+
/* } */
64+
/* } */
65+
66+
/* void bst_print(Node* root) { */
67+
/* bst_print_aux(root); */
68+
/* printf("\n"); */
69+
/* } */
70+
71+
bool bst_find(Node* root, int key) {
72+
if (root == NULL || root->val == -1) {
73+
return false;
74+
} else if (key == root->val) {
75+
return true;
76+
} else if (key > root->val) {
77+
return bst_find(root->right, key);
78+
} else {
79+
return bst_find(root->left, key);
80+
}
81+
}
82+
83+
int main() {
84+
int data[SIZE];
85+
int key;
86+
/* for (int i = 0; i < SIZE; i += 1) { */
87+
/* data[i] = i; */
88+
/* } */
89+
#ifdef KLEE
90+
klee_make_symbolic(data, sizeof(data), "data");
91+
klee_make_symbolic(&key, sizeof(int), "key");
92+
#else
93+
make_symbolic(data, sizeof(int) * SIZE, "data");
94+
make_symbolic(&key, sizeof(int), "key");
95+
#endif
96+
97+
Node* tree = bst_from_list(data, SIZE);
98+
/* bst_print(tree); */
99+
bst_find(tree, key);
100+
}

benchmarks/external-lib/assume.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <stdio.h>
44
#include <stdlib.h>
55
#include <unistd.h>
6+
#include <string.h>
67

78
int main() {
89
int x, y, z;
@@ -21,6 +22,7 @@ int main() {
2122
int ememb = 11;
2223
int *p1 = calloc(ememb, sizeof(int)); // allocate and zero out an array of ememb int
2324
int *p2 = calloc(1, sizeof(int[ememb])); // same, naming the array type directly
25+
2426
for (int i=0;i<ememb;i++) {
2527
gs_assert_eager(p1[i] == p2[i]);
2628
gs_assert_eager(0 == p1[i]);

benchmarks/llvm/memchallenge.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ typedef struct {
1616
void gs_assert_eager(bool);
1717

1818
int main() {
19-
do {
19+
do { // TODO: fails
2020
int64_t val = 0x1234567890abcdef;
2121
pair_t *st = &val;
2222
int16_t tmp = st->b;
@@ -38,4 +38,4 @@ int main() {
3838
memcpy(&p2, &p1, sizeof(p2));
3939
} while(0);
4040
return 0;
41-
}
41+
}

benchmarks/llvm/sym_pointer.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <stdlib.h>
2+
3+
#ifdef KLEE
4+
#include "klee/klee.h"
5+
#endif
6+
7+
int main() {
8+
int* ptr;
9+
10+
#ifdef KLEE
11+
klee_make_symbolic(&ptr, sizeof(int*), "ptr");
12+
#else
13+
make_symbolic(&ptr, sizeof(int*), "ptr");
14+
#endif
15+
16+
if (ptr == NULL) {
17+
return 0;
18+
} else {
19+
return *ptr;
20+
}
21+
}

benchmarks/llvm/uninit_ptr.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <stdlib.h>
2+
3+
// This test must be run with --thread=1
4+
// so that the NullDerefException is caught
5+
6+
int main() {
7+
int* ptr;
8+
return *ptr;
9+
}

benchmarks/llvm/uninit_ptr_cond.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <stdlib.h>
2+
3+
// This test must be run with --thread=1
4+
// so that the NullDerefException is caught
5+
6+
int main() {
7+
int* ptr;
8+
9+
if (ptr == NULL) {
10+
return 0;
11+
} else {
12+
return *ptr;
13+
}
14+
}

benchmarks/llvm/uninit_ptr_update.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <stdlib.h>
2+
3+
// This test must be run with --thread=1
4+
// so that the NullDerefException is caught
5+
6+
int main() {
7+
int* ptr;
8+
*ptr = 10;
9+
return *ptr;
10+
}

headers/gensym/args.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ inline List<List<PtrVal>> parse_args(std::string argstrings) {
7979
immer::flex_vector_transient<List<PtrVal>> result;
8080
while (std::getline(ss, s, ' ')) {
8181
if (!s.empty()) {
82-
result.push_back(to_chars(match_arg(s)).push_back(make_UnInitV()));
82+
result.push_back(to_chars(match_arg(s)).push_back(make_IntV(0, 8)));
8383
}
8484
}
8585
return result.persistent();

headers/gensym/defs.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ inline unsigned int max_sym_array_size = 0;
114114
inline unsigned int max_size_bound = 400;
115115
// Use simplification when constructing SymV values
116116
inline bool use_symv_simplify = false;
117+
// Let uninitialized memory be set to symbolic values
118+
inline bool symbolic_uninit = false;
117119

118120
// Output directory name
119121
inline std::string output_dir_str = std::string("gensym-") + get_current_datetime();
@@ -248,4 +250,4 @@ inline std::string float_op2string(fOP op) {
248250
return "unknown op";
249251
}
250252

251-
#endif
253+
#endif

headers/gensym/external_imp.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ inline T __calloc(SS& state, List<PtrVal>& args, __Cont<T> k) {
207207
IntData nmemb = proj_IntV(args.at(0));
208208
IntData size = proj_IntV(args.at(1));
209209
ASSERT(size > 0 && nmemb > 0, "Invalid nmemb and size");
210-
auto emptyMem = List<PtrVal>(nmemb * size, make_UnInitV());
210+
auto emptyMem = List<PtrVal>(nmemb * size, make_IntV(0, 8));
211211
PtrVal memLoc = make_LocV(state.heap_size(), LocV::kHeap, nmemb * size);
212212
if (exlib_failure_branch)
213213
return k(state.heap_append(emptyMem), memLoc) + k(state, make_LocV_null());
@@ -572,7 +572,17 @@ inline void copy_native2state(SS& state, PtrVal ptr, char* buf, int size) {
572572
ASSERT(bytes_num > 0, "Invalid bytes");
573573
// Do not over-write symbolic variable
574574
if (old_val->to_SymV()) {
575-
i = i + bytes_num;
575+
#ifdef GENSYM_SYMBOLIC_UNINIT
576+
// add constraint on symbolic variable to be equal to concrete
577+
for (int j = 0; j < bytes_num; j++) {
578+
auto eq_constraint = int_op_2(iOP::op_eq, state.at_simpl(ptr + i), make_IntV(buf[i], 8));
579+
state.add_PC(eq_constraint);
580+
i++;
581+
if (i >= size) break;
582+
}
583+
#else
584+
i += bytes_num;
585+
#endif
576586
} else {
577587
for (int j = 0; j < bytes_num; j++) {
578588
state.update_simpl(ptr + i, make_IntV(buf[i], 8));

headers/gensym/external_pure.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ inline T __calloc(SS& state, List<PtrVal>& args, __Cont<T> k) {
239239
IntData nmemb = proj_IntV(args.at(0));
240240
IntData size = proj_IntV(args.at(1));
241241
ASSERT(size > 0 && nmemb > 0, "Invalid nmemb and size");
242-
auto emptyMem = List<PtrVal>(nmemb * size, make_UnInitV());
242+
auto emptyMem = List<PtrVal>(nmemb * size, make_IntV(0, 8));
243243

244244
PtrVal memLoc = make_LocV(state.heap_size(), LocV::kHeap, nmemb * size);
245245
if (exlib_failure_branch)
@@ -362,8 +362,17 @@ inline SS copy_native2state(SS state, PtrVal ptr, char* buf, int size) {
362362
ASSERT(bytes_num > 0, "Invalid bytes");
363363
// All bytes must be concrete IntV
364364
if (std::dynamic_pointer_cast<SymV>(old_val)) {
365-
// Todo: should we overwrite symbolic variables?
366-
i = i + bytes_num;
365+
#ifdef GENSYM_SYMBOLIC_UNINIT
366+
// add constraint on symbolic variable to be equal to concrete
367+
for (int j = 0; j < bytes_num; j++) {
368+
auto eq_constraint = int_op_2(iOP::op_eq, state.at_simpl(ptr + i), make_IntV(buf[i], 8));
369+
res = res.add_PC(eq_constraint);
370+
i++;
371+
if (i >= size) break;
372+
}
373+
#else
374+
i += bytes_num;
375+
#endif
367376
} else {
368377
for (int j = 0; j < bytes_num; j++) {
369378
res = res.update_simpl(ptr + i, make_IntV(buf[i], 8));

headers/gensym/state_tsnt.hpp

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class PreMem {
3131
return move_this();
3232
}
3333
M&& alloc(size_t size) {
34-
mem.append(List<V>(size, make_UnInitV()).transient());
34+
mem.append(make_UnInitList(size));
3535
return move_this();
3636
}
3737
M&& take(size_t keep) {
@@ -401,13 +401,21 @@ class SS {
401401
if (loc->l + size > heap.size()) throw NullDerefException { immer::box<SS>(*this) };
402402
return heap.at(loc->l, size);
403403
}
404+
} else if (auto symloc = std::dynamic_pointer_cast<SymLocV>(addr)) {
405+
return at_symloc(symloc, size);
406+
} else if (auto symvite = addr->to_SymV()) {
407+
if (iOP::op_ite == symvite->rator) {
408+
return ite((*symvite)[0], at((*symvite)[1], size), at((*symvite)[2], size));
409+
} else {
410+
// Now the location value is a general symbolic value other than `ite`/`SymLocV`,
411+
// which means the pointer is uninitialized anyway.
412+
std::cout << "Symbolic Case\n";
413+
throw NullDerefException { immer::box<SS>(*this) };
414+
}
404415
}
405-
if (auto symloc = std::dynamic_pointer_cast<SymLocV>(addr)) return at_symloc(symloc, size);
406-
if (auto symvite = addr->to_SymV()) {
407-
ASSERT(iOP::op_ite == symvite->rator, "Invalid memory read by symv index");
408-
return ite((*symvite)[0], at((*symvite)[1], size), at((*symvite)[2], size));
409-
}
410-
ABORT("dereferenceing a nullptr");
416+
// Default case: considered as an invalid memory access
417+
std::cout << "Default Case\n";
418+
throw NullDerefException { immer::box<SS>(*this) };
411419
}
412420
PtrVal at_struct(PtrVal addr, size_t size) {
413421
auto loc = addr->to_LocV();
@@ -454,37 +462,49 @@ class SS {
454462
heap.update(loc->l, val);
455463
return std::move(*this);
456464
}
457-
SS&& update_symloc(PtrVal addr, PtrVal val, size_t size) {
458-
auto symloc = std::dynamic_pointer_cast<SymLocV>(addr);
465+
SS&& update_symloc(simple_ptr<SymLocV> symloc, PtrVal val, size_t size) {
459466
ASSERT(symloc != nullptr && symloc->size >= size, "Lookup an non-address value");
460467
auto offsym = symloc->off->to_SymV();
461468
ASSERT(offsym && (offsym->get_bw() == addr_index_bw), "Invalid sym offset");
462469

463470
auto low_cond = int_op_2(iOP::op_sge, offsym, make_IntV(0, addr_index_bw));
464471
auto high_cond = int_op_2(iOP::op_sle, offsym, make_IntV(symloc->size - size, addr_index_bw));
472+
465473
auto pc2 = pc;
466474
pc2.add(low_cond).add(high_cond);
467475
auto res = get_sat_value(pc2, offsym);
468-
ASSERT(res.first, "no feasible offset\n");
469476

470-
// Todo (Ruiqi): Maybe use Intdata?
471-
int offset_val = res.second;
472-
auto t_cond = int_op_2(iOP::op_eq, offsym, make_IntV(offset_val, offsym->get_bw()));
477+
if (res.first) {
478+
// Todo (Ruiqi): Maybe use Intdata?
479+
int offset_val = res.second;
480+
auto t_cond = int_op_2(iOP::op_eq, offsym, make_IntV(offset_val, offsym->get_bw()));
481+
// Todo (Ruiqi): should we add this?
482+
pc.add(t_cond);
483+
484+
update(make_LocV(symloc->base, symloc->k, symloc->size, offset_val), val, size);
485+
return std::move(*this);
486+
} else {
487+
throw NullDerefException { immer::box<SS>(*this) };
488+
}
473489

474-
// Todo (Ruiqi): should we add this?
475-
pc.add(t_cond);
476-
update(make_LocV(symloc->base, symloc->k, symloc->size, offset_val), val, size);
477490
return std::move(*this);
478491
}
479492
SS&& update(PtrVal addr, PtrVal val, size_t size) {
480493
if (auto loc = addr->to_LocV()) {
481-
if (loc->k == LocV::kStack) stack.update(loc->l, val, size);
494+
if (loc->k == LocV::kStack) stack.update(loc->l, val, size);
482495
else heap.update(loc->l, val, size);
483496
} else if (auto symloc = std::dynamic_pointer_cast<SymLocV>(addr)) {
484-
update_symloc(symloc, val, size);
485-
} else {
486-
ABORT("dereferenceing a nullptr");
497+
update_symloc(symloc, val, size);
498+
} else if (auto symvite = addr->to_SymV()){
499+
if (iOP::op_ite == symvite->rator) {
500+
auto cond = (*symvite)[0];
501+
SS s1 = update((*symvite)[1], ite(cond, val, at((*symvite)[1], size)), size);
502+
return s1.update((*symvite)[2], ite(cond, val, at((*symvite)[2], size)), size);
503+
} else {
504+
throw NullDerefException { immer::box<SS>(*this) };
505+
}
487506
}
507+
488508
return std::move(*this);
489509
}
490510
SS&& update_seq(PtrVal addr, List<PtrVal> vals) {

0 commit comments

Comments
 (0)