Skip to content

Commit 78f5095

Browse files
committed
[fix] Model objects for ctype_* functions for 32-bit executables. Handle ConstantPointer instead of ConstantExpr where it is required. Removed propagating of operations in SelectExpr (caused division on 0 for *Div operations).
[fix] Model return value of ctype_* functions as pointers. [fix] Allow to resolve SelectExpr of ConsantExprs on all memory objects in address space. [fix] DebugInfo may not point to any address. [fix] Do not optimize SelectExpr for *Div operations, terminate state on call to raise function. [refactor] Logic for writing in CType objects put in lambdas. [test] Added tests for propagating select through division, calling to raise and for resolve of symbolic pointer represeneted as a select by a symbolic condition. [refactor] addCType* functions moved to private methods for better readability, optimization on sifting up selects during construction of expressions moved to seperate method also for better readability.
1 parent b7f9c79 commit 78f5095

10 files changed

+372
-130
lines changed

lib/Core/AddressSpace.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "Memory.h"
1414
#include "TimingSolver.h"
1515

16+
#include "klee/Expr/ArrayExprVisitor.h"
1617
#include "klee/Expr/Expr.h"
1718
#include "klee/Module/KType.h"
1819
#include "klee/Statistics/TimerStatIncrementer.h"
@@ -199,6 +200,24 @@ class ResolvePredicate {
199200
timestamp = moBasePair.first->timestamp;
200201
}
201202
}
203+
// This is hack to deal with the poineters, which are represented as
204+
// select expressions from constant pointers with symbolic condition.
205+
// in such case we allow to resolve in all objects in the address space,
206+
// but should forbid lazy initilization.
207+
if (isa<SelectExpr>(base)) {
208+
std::vector<ref<Expr>> alternatives;
209+
ArrayExprHelper::collectAlternatives(cast<SelectExpr>(*base),
210+
alternatives);
211+
if (std::find_if_not(alternatives.begin(), alternatives.end(),
212+
[](ref<Expr> expr) {
213+
return isa<ConstantExpr>(expr);
214+
}) == alternatives.end()) {
215+
skipNotSymbolicObjects = false;
216+
skipNotLazyInitialized = false;
217+
skipLocal = false;
218+
skipGlobal = false;
219+
}
220+
}
202221
}
203222

204223
bool operator()(const MemoryObject *mo, const ObjectState *os) const {

lib/Core/Executor.cpp

Lines changed: 173 additions & 67 deletions
Large diffs are not rendered by default.

lib/Core/Executor.h

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ DISABLE_WARNING_POP
5555
#include <unordered_map>
5656
#include <vector>
5757

58+
#ifdef HAVE_CTYPE_EXTERNALS
59+
#include <ctype.h>
60+
#endif
61+
5862
struct KTest;
5963

6064
namespace llvm {
@@ -129,6 +133,12 @@ class Executor : public Interpreter {
129133
private:
130134
int *errno_addr;
131135

136+
#ifdef HAVE_CTYPE_EXTERNALS
137+
decltype(__ctype_b_loc()) c_type_b_loc_addr;
138+
decltype(__ctype_tolower_loc()) c_type_tolower_addr;
139+
decltype(__ctype_toupper_loc()) c_type_toupper_addr;
140+
#endif
141+
132142
size_t maxNewWriteableOSSize = 0;
133143
size_t maxNewStateStackSize = 0;
134144

@@ -267,8 +277,21 @@ class Executor : public Interpreter {
267277

268278
// Given a concrete object in our [klee's] address space, add it to
269279
// objects checked code can reference.
270-
MemoryObject *addExternalObject(ExecutionState &state, void *addr, KType *,
271-
unsigned size, bool isReadOnly);
280+
ObjectPair addExternalObject(ExecutionState &state, const void *addr, KType *,
281+
unsigned size, bool isReadOnly);
282+
ObjectPair addExternalObjectAsNonStatic(ExecutionState &state, KType *,
283+
unsigned size, bool isReadOnly);
284+
285+
#ifdef HAVE_CTYPE_EXTERNALS
286+
template <typename F>
287+
decltype(auto) addCTypeFixedObject(ExecutionState &state, int addressSpaceNum,
288+
llvm::Module &m, F objectProvider);
289+
290+
template <typename F>
291+
decltype(auto) addCTypeModelledObject(ExecutionState &state,
292+
int addressSpaceNum, llvm::Module &m,
293+
F objectProvider);
294+
#endif
272295

273296
void initializeGlobalAlias(const llvm::Constant *c, ExecutionState &state);
274297
void initializeGlobalObject(ExecutionState &state, ObjectState *os,
@@ -442,8 +465,8 @@ class Executor : public Interpreter {
442465
StatePair forkInternal(ExecutionState &current, ref<Expr> condition,
443466
BranchType reason);
444467

445-
// If the MaxStatic*Pct limits have been reached, concretize the condition and
446-
// return it. Otherwise, return the unmodified condition.
468+
// If the MaxStatic*Pct limits have been reached, concretize the condition
469+
// and return it. Otherwise, return the unmodified condition.
447470
ref<Expr> maxStaticPctChecks(ExecutionState &current, ref<Expr> condition);
448471

449472
/// Add the given (boolean) condition as a constraint on state. This
@@ -587,8 +610,8 @@ class Executor : public Interpreter {
587610
unsigned size = 0,
588611
const MemoryObject *mo = nullptr) const;
589612

590-
// Determines the \param lastInstruction of the \param state which is not KLEE
591-
// internal and returns its KInstruction
613+
// Determines the \param lastInstruction of the \param state which is not
614+
// KLEE internal and returns its KInstruction
592615
const KInstruction *
593616
getLastNonKleeInternalInstruction(const ExecutionState &state) const;
594617

@@ -697,7 +720,8 @@ class Executor : public Interpreter {
697720
void doImpliedValueConcretization(ExecutionState &state, ref<Expr> e,
698721
ref<ConstantExpr> value);
699722

700-
/// check memory usage and terminate states when over threshold of -max-memory
723+
/// check memory usage and terminate states when over threshold of
724+
/// -max-memory
701725
/// + 100MB \return true if below threshold, false otherwise (states were
702726
/// terminated)
703727
bool checkMemoryUsage();

lib/Core/SpecialFunctionHandler.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = {
191191
add("klee_rintf", handleRint, true),
192192
#if defined(__x86_64__) || defined(__i386__)
193193
add("klee_rintl", handleRint, true),
194+
#if defined(HAVE_CTYPE_EXTERNALS)
195+
add("__ctype_b_loc", handleCTypeBLoc, true),
196+
add("__ctype_tolower_loc", handleCTypeToLowerLoc, true),
197+
add("__ctype_toupper_loc", handleCTypeToUpperLoc, true),
198+
#endif
194199
#endif
195200
#undef addDNR
196201
#undef add
@@ -1214,3 +1219,34 @@ void SpecialFunctionHandler::handleFAbs(ExecutionState &state,
12141219
ref<Expr> result = FAbsExpr::create(arguments[0]);
12151220
executor.bindLocal(target, state, result);
12161221
}
1222+
1223+
#ifdef HAVE_CTYPE_EXTERNALS
1224+
1225+
void SpecialFunctionHandler::handleCTypeBLoc(
1226+
ExecutionState &state, KInstruction *target,
1227+
std::vector<ref<Expr>> &arguments) {
1228+
ref<ConstantExpr> pointerValue = Expr::createPointer(
1229+
reinterpret_cast<uint64_t>(executor.c_type_b_loc_addr));
1230+
ref<Expr> result = ConstantPointerExpr::create(pointerValue, pointerValue);
1231+
executor.bindLocal(target, state, result);
1232+
}
1233+
1234+
void SpecialFunctionHandler::handleCTypeToLowerLoc(
1235+
ExecutionState &state, KInstruction *target,
1236+
std::vector<ref<Expr>> &arguments) {
1237+
ref<ConstantExpr> pointerValue = Expr::createPointer(
1238+
reinterpret_cast<uint64_t>(executor.c_type_tolower_addr));
1239+
ref<Expr> result = ConstantPointerExpr::create(pointerValue, pointerValue);
1240+
executor.bindLocal(target, state, result);
1241+
}
1242+
1243+
void SpecialFunctionHandler::handleCTypeToUpperLoc(
1244+
ExecutionState &state, KInstruction *target,
1245+
std::vector<ref<Expr>> &arguments) {
1246+
ref<ConstantExpr> pointerValue = Expr::createPointer(
1247+
reinterpret_cast<uint64_t>(executor.c_type_toupper_addr));
1248+
ref<Expr> result = ConstantPointerExpr::create(pointerValue, pointerValue);
1249+
executor.bindLocal(target, state, result);
1250+
}
1251+
1252+
#endif

lib/Core/SpecialFunctionHandler.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ class SpecialFunctionHandler {
181181
HANDLER(handleNonnullArg);
182182
HANDLER(handleNullabilityArg);
183183
HANDLER(handlePointerOverflow);
184+
#ifdef HAVE_CTYPE_EXTERNALS
185+
HANDLER(handleCTypeBLoc);
186+
HANDLER(handleCTypeToLowerLoc);
187+
HANDLER(handleCTypeToUpperLoc);
188+
#endif
184189
#undef HANDLER
185190
};
186191
} // namespace klee

lib/Expr/Expr.cpp

Lines changed: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,20 +2329,45 @@ static ref<Expr> AShrExpr_create(const ref<Expr> &l, const ref<Expr> &r) {
23292329
}
23302330
}
23312331

2332+
template <typename T> static bool isDiv() {
2333+
return T::kind == Expr::Kind::SDiv || T::kind == Expr::Kind::UDiv ||
2334+
T::kind == Expr::Kind::FDiv;
2335+
}
2336+
2337+
/// Heuristic.
2338+
/// Attempts to sift up select expression during creation.
2339+
template <typename T>
2340+
static ref<Expr> tryCreateWithSiftUpSelectExpr(const ref<Expr> &l,
2341+
const ref<Expr> &r,
2342+
bool skipInnerSelect = false) {
2343+
if (isDiv<T>()) {
2344+
return nullptr;
2345+
}
2346+
2347+
if (ref<SelectExpr> sel = dyn_cast<SelectExpr>(l)) {
2348+
if (isa<ConstantExpr>(sel->trueExpr) &&
2349+
(!skipInnerSelect || !isa<SelectExpr>(r))) {
2350+
return SelectExpr::create(sel->cond, T::create(sel->trueExpr, r),
2351+
T::create(sel->falseExpr, r));
2352+
}
2353+
}
2354+
if (ref<SelectExpr> ser = dyn_cast<SelectExpr>(r)) {
2355+
if (isa<ConstantExpr>(ser->trueExpr) &&
2356+
(!skipInnerSelect || !isa<SelectExpr>(l))) {
2357+
return SelectExpr::create(ser->cond, T::create(l, ser->trueExpr),
2358+
T::create(l, ser->falseExpr));
2359+
}
2360+
}
2361+
2362+
return nullptr;
2363+
}
2364+
23322365
#define BCREATE_R(_e_op, _op, partialL, partialR, pointerL, pointerR) \
23332366
ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \
23342367
assert(l->getWidth() == r->getWidth() && "type mismatch"); \
2335-
if (SelectExpr *sel = dyn_cast<SelectExpr>(l)) { \
2336-
if (isa<ConstantExpr>(sel->trueExpr)) { \
2337-
return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \
2338-
_e_op::create(sel->falseExpr, r)); \
2339-
} \
2340-
} \
2341-
if (SelectExpr *ser = dyn_cast<SelectExpr>(r)) { \
2342-
if (isa<ConstantExpr>(ser->trueExpr)) { \
2343-
return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \
2344-
_e_op::create(l, ser->falseExpr)); \
2345-
} \
2368+
if (auto withSiftUpSelectExpr = \
2369+
tryCreateWithSiftUpSelectExpr<_e_op>(l, r)) { \
2370+
return withSiftUpSelectExpr; \
23462371
} \
23472372
if (PointerExpr *pl = dyn_cast<PointerExpr>(l)) { \
23482373
if (PointerExpr *pr = dyn_cast<PointerExpr>(r)) \
@@ -2364,17 +2389,9 @@ static ref<Expr> AShrExpr_create(const ref<Expr> &l, const ref<Expr> &r) {
23642389
#define BCREATE_R_C(_e_op, _op, partialL, partialR, pointerL, pointerR) \
23652390
ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \
23662391
assert(l->getWidth() == r->getWidth() && "type mismatch"); \
2367-
if (SelectExpr *sel = dyn_cast<SelectExpr>(l)) { \
2368-
if (isa<ConstantExpr>(sel->trueExpr)) { \
2369-
return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \
2370-
_e_op::create(sel->falseExpr, r)); \
2371-
} \
2372-
} \
2373-
if (SelectExpr *ser = dyn_cast<SelectExpr>(r)) { \
2374-
if (isa<ConstantExpr>(ser->trueExpr)) { \
2375-
return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \
2376-
_e_op::create(l, ser->falseExpr)); \
2377-
} \
2392+
if (auto withSiftUpSelectExpr = \
2393+
tryCreateWithSiftUpSelectExpr<_e_op>(l, r)) { \
2394+
return withSiftUpSelectExpr; \
23782395
} \
23792396
if (PointerExpr *pl = dyn_cast<PointerExpr>(l)) { \
23802397
if (PointerExpr *pr = dyn_cast<PointerExpr>(r)) \
@@ -2409,17 +2426,9 @@ static ref<Expr> AShrExpr_create(const ref<Expr> &l, const ref<Expr> &r) {
24092426
#define BCREATE(_e_op, _op) \
24102427
ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \
24112428
assert(l->getWidth() == r->getWidth() && "type mismatch"); \
2412-
if (SelectExpr *sel = dyn_cast<SelectExpr>(l)) { \
2413-
if (isa<ConstantExpr>(sel->trueExpr)) { \
2414-
return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \
2415-
_e_op::create(sel->falseExpr, r)); \
2416-
} \
2417-
} \
2418-
if (SelectExpr *ser = dyn_cast<SelectExpr>(r)) { \
2419-
if (isa<ConstantExpr>(ser->trueExpr)) { \
2420-
return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \
2421-
_e_op::create(l, ser->falseExpr)); \
2422-
} \
2429+
if (auto withSiftUpSelectExpr = \
2430+
tryCreateWithSiftUpSelectExpr<_e_op>(l, r)) { \
2431+
return withSiftUpSelectExpr; \
24232432
} \
24242433
if (PointerExpr *pl = dyn_cast<PointerExpr>(l)) { \
24252434
if (PointerExpr *pr = dyn_cast<PointerExpr>(r)) \
@@ -2457,17 +2466,9 @@ BCREATE(AShrExpr, AShr)
24572466
#define CMPCREATE(_e_op, _op) \
24582467
ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \
24592468
assert(l->getWidth() == r->getWidth() && "type mismatch"); \
2460-
if (SelectExpr *sel = dyn_cast<SelectExpr>(l)) { \
2461-
if (isa<ConstantExpr>(sel->trueExpr) && !isa<SelectExpr>(r)) { \
2462-
return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \
2463-
_e_op::create(sel->falseExpr, r)); \
2464-
} \
2465-
} \
2466-
if (SelectExpr *ser = dyn_cast<SelectExpr>(r)) { \
2467-
if (isa<ConstantExpr>(ser->trueExpr) && !isa<SelectExpr>(l)) { \
2468-
return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \
2469-
_e_op::create(l, ser->falseExpr)); \
2470-
} \
2469+
if (auto withSiftUpSelectExpr = \
2470+
tryCreateWithSiftUpSelectExpr<_e_op>(l, r, true)) { \
2471+
return withSiftUpSelectExpr; \
24712472
} \
24722473
if (PointerExpr *pl = dyn_cast<PointerExpr>(l)) { \
24732474
if (PointerExpr *pr = dyn_cast<PointerExpr>(r)) \
@@ -2485,17 +2486,9 @@ BCREATE(AShrExpr, AShr)
24852486
#define CMPCREATE_T(_e_op, _op, _reflexive_e_op, partialL, partialR) \
24862487
ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \
24872488
assert(l->getWidth() == r->getWidth() && "type mismatch"); \
2488-
if (SelectExpr *sel = dyn_cast<SelectExpr>(l)) { \
2489-
if (isa<ConstantExpr>(sel->trueExpr) && !isa<SelectExpr>(r)) { \
2490-
return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \
2491-
_e_op::create(sel->falseExpr, r)); \
2492-
} \
2493-
} \
2494-
if (SelectExpr *ser = dyn_cast<SelectExpr>(r)) { \
2495-
if (isa<ConstantExpr>(ser->trueExpr) && !isa<SelectExpr>(l)) { \
2496-
return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \
2497-
_e_op::create(l, ser->falseExpr)); \
2498-
} \
2489+
if (auto withSiftUpSelectExpr = \
2490+
tryCreateWithSiftUpSelectExpr<_e_op>(l, r, true)) { \
2491+
return withSiftUpSelectExpr; \
24992492
} \
25002493
if (PointerExpr *pl = dyn_cast<PointerExpr>(l)) { \
25012494
if (PointerExpr *pr = dyn_cast<PointerExpr>(r)) \

lib/Module/LocalVarDeclarationFinderPass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ bool LocalVarDeclarationFinderPass::runOnFunction(llvm::Function &function) {
3535
llvm::dyn_cast<const llvm::DbgDeclareInst>(&instruction)) {
3636
llvm::Value *source = debugDeclareInstruction->getAddress();
3737
if (llvm::Instruction *sourceInstruction =
38-
llvm::dyn_cast<llvm::Instruction>(source)) {
38+
llvm::dyn_cast_or_null<llvm::Instruction>(source)) {
3939
sourceInstruction->setDebugLoc(
4040
debugDeclareInstruction->getDebugLoc());
4141
anyChanged = true;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc
2+
// RUN: rm -rf %t.klee-out
3+
// RUN: %klee --output-dir=%t.klee-out --mock-policy=all --external-calls=all %t.bc 2>&1 | FileCheck %s
4+
5+
#include "klee/klee.h"
6+
#include <signal.h>
7+
8+
int main() {
9+
int n;
10+
klee_make_symbolic(&n, sizeof(n), "n");
11+
12+
if (n) {
13+
// CHECK: Call to "raise" is unmodelled
14+
raise(5);
15+
}
16+
// CHECK: KLEE: done: completed paths = 1
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc
2+
// RUN: rm -rf %t.klee-out
3+
// RUN: %klee --output-dir=%t.klee-out %t.bc 2>&1 | FileCheck %s
4+
5+
#include "klee/klee.h"
6+
7+
int main() {
8+
int n, m;
9+
klee_make_symbolic(&n, sizeof(n), "n");
10+
klee_make_symbolic(&m, sizeof(m), "m");
11+
12+
int z = (n ? 1 : 0) / (m ? 1 : 0);
13+
(void)z;
14+
}
15+
// CHECK: KLEE: done
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc
2+
// RUN: rm -rf %t.klee-out
3+
// RUN: %klee --output-dir=%t.klee-out --skip-not-symbolic-objects --skip-not-lazy-initialized --skip-local --skip-global %t.bc 2>&1 | FileCheck %s
4+
5+
#include "klee/klee.h"
6+
#include <assert.h>
7+
8+
int main() {
9+
int x = 0;
10+
int y = 1;
11+
12+
int cond;
13+
klee_make_symbolic(&cond, sizeof(cond), "cond");
14+
15+
int *z = cond ? &x : &y;
16+
// CHECK-NOT: memory error: out of bound pointer
17+
*z = 10;
18+
19+
// CHECK-NOT: ASSERTION FAIL
20+
if (x == 10) {
21+
assert(y == 1);
22+
} else {
23+
assert(x == 0);
24+
assert(y == 10);
25+
}
26+
// CHECK: KLEE: done: completed paths = 2
27+
}

0 commit comments

Comments
 (0)