1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
//===-- ImpliedValue.cpp --------------------------------------------------===//
//
// The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ImpliedValue.h"
#include "Context.h"
#include "klee/Expr/Constraints.h"
#include "klee/Expr/Expr.h"
#include "klee/Expr/ExprUtil.h"
#include "klee/Internal/Support/IntEvaluation.h" // FIXME: Use APInt
#include "klee/Solver/Solver.h"
#include <map>
#include <set>
using namespace klee;
// XXX we really want to do some sort of canonicalization of exprs
// globally so that cases below become simpler
void ImpliedValue::getImpliedValues(ref<Expr> e,
ref<ConstantExpr> value,
ImpliedValueList &results) {
switch (e->getKind()) {
case Expr::Constant: {
assert(value == cast<ConstantExpr>(e) &&
"error in implied value calculation");
break;
}
// Special
case Expr::NotOptimized: break;
case Expr::Read: {
// XXX in theory it is possible to descend into a symbolic index
// under certain circumstances (all values known, known value
// unique, or range known, max / min hit). Seems unlikely this
// would work often enough to be worth the effort.
ReadExpr *re = cast<ReadExpr>(e);
results.push_back(std::make_pair(re, value));
break;
}
case Expr::Select: {
// not much to do, could improve with range analysis
SelectExpr *se = cast<SelectExpr>(e);
if (ConstantExpr *TrueCE = dyn_cast<ConstantExpr>(se->trueExpr)) {
if (ConstantExpr *FalseCE = dyn_cast<ConstantExpr>(se->falseExpr)) {
if (TrueCE != FalseCE) {
if (value == TrueCE) {
getImpliedValues(se->cond, ConstantExpr::alloc(1, Expr::Bool),
results);
} else {
assert(value == FalseCE &&
"err in implied value calculation");
getImpliedValues(se->cond, ConstantExpr::alloc(0, Expr::Bool),
results);
}
}
}
}
break;
}
case Expr::Concat: {
ConcatExpr *ce = cast<ConcatExpr>(e);
getImpliedValues(ce->getKid(0), value->Extract(ce->getKid(1)->getWidth(),
ce->getKid(0)->getWidth()),
results);
getImpliedValues(ce->getKid(1), value->Extract(0,
ce->getKid(1)->getWidth()),
results);
break;
}
case Expr::Extract: {
// XXX, could do more here with "some bits" mask
break;
}
// Casting
case Expr::ZExt:
case Expr::SExt: {
CastExpr *ce = cast<CastExpr>(e);
getImpliedValues(ce->src, value->Extract(0, ce->src->getWidth()),
results);
break;
}
// Arithmetic
case Expr::Add: { // constants on left
BinaryExpr *be = cast<BinaryExpr>(e);
// C_0 + A = C => A = C - C_0
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(be->left))
getImpliedValues(be->right, value->Sub(CE), results);
break;
}
case Expr::Sub: { // constants on left
BinaryExpr *be = cast<BinaryExpr>(e);
// C_0 - A = C => A = C_0 - C
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(be->left))
getImpliedValues(be->right, CE->Sub(value), results);
break;
}
case Expr::Mul: {
// FIXME: Can do stuff here, but need valid mask and other things because of
// bits that might be lost.
break;
}
case Expr::UDiv:
case Expr::SDiv:
case Expr::URem:
case Expr::SRem:
break;
// Binary
case Expr::And: {
BinaryExpr *be = cast<BinaryExpr>(e);
if (be->getWidth() == Expr::Bool) {
if (value->isTrue()) {
getImpliedValues(be->left, value, results);
getImpliedValues(be->right, value, results);
}
} else {
// FIXME; We can propogate a mask here where we know "some bits". May or
// may not be useful.
}
break;
}
case Expr::Or: {
BinaryExpr *be = cast<BinaryExpr>(e);
if (value->isZero()) {
getImpliedValues(be->left, 0, results);
getImpliedValues(be->right, 0, results);
} else {
// FIXME: Can do more?
}
break;
}
case Expr::Xor: {
BinaryExpr *be = cast<BinaryExpr>(e);
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(be->left))
getImpliedValues(be->right, value->Xor(CE), results);
break;
}
// Comparison
case Expr::Ne:
value = value->Not();
/* fallthru */
case Expr::Eq: {
EqExpr *ee = cast<EqExpr>(e);
if (value->isTrue()) {
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(ee->left))
getImpliedValues(ee->right, CE, results);
} else {
// Look for limited value range.
//
// In general anytime one side was restricted to two values we can apply
// this trick. The only obvious case where this occurs, aside from
// booleans, is as the result of a select expression where the true and
// false branches are single valued and distinct.
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(ee->left))
if (CE->getWidth() == Expr::Bool)
getImpliedValues(ee->right, CE->Not(), results);
}
break;
}
default:
break;
}
}
void ImpliedValue::checkForImpliedValues(Solver *S, ref<Expr> e,
ref<ConstantExpr> value) {
std::vector<ref<ReadExpr> > reads;
std::map<ref<ReadExpr>, ref<ConstantExpr> > found;
ImpliedValueList results;
getImpliedValues(e, value, results);
for (ImpliedValueList::iterator i = results.begin(), ie = results.end();
i != ie; ++i) {
std::map<ref<ReadExpr>, ref<ConstantExpr> >::iterator it =
found.find(i->first);
if (it != found.end()) {
assert(it->second == i->second && "Invalid ImpliedValue!");
} else {
found.insert(std::make_pair(i->first, i->second));
}
}
findReads(e, false, reads);
std::set< ref<ReadExpr> > readsSet(reads.begin(), reads.end());
reads = std::vector< ref<ReadExpr> >(readsSet.begin(), readsSet.end());
std::vector<ref<Expr> > assumption;
assumption.push_back(EqExpr::create(e, value));
// obscure... we need to make sure that all the read indices are
// bounds checked. if we don't do this we can end up constructing
// invalid counterexamples because STP will happily make out of
// bounds indices which will not get picked up. this is of utmost
// importance if we are being backed by the CexCachingSolver.
for (std::vector< ref<ReadExpr> >::iterator i = reads.begin(),
ie = reads.end(); i != ie; ++i) {
ReadExpr *re = i->get();
assumption.push_back(UltExpr::create(re->index,
ConstantExpr::alloc(re->updates.root->size,
Context::get().getPointerWidth())));
}
ConstraintManager assume(assumption);
for (std::vector< ref<ReadExpr> >::iterator i = reads.begin(),
ie = reads.end(); i != ie; ++i) {
ref<ReadExpr> var = *i;
ref<ConstantExpr> possible;
bool success = S->getValue(Query(assume, var), possible); (void) success;
assert(success && "FIXME: Unhandled solver failure");
std::map<ref<ReadExpr>, ref<ConstantExpr> >::iterator it = found.find(var);
bool res;
success = S->mustBeTrue(Query(assume, EqExpr::create(var, possible)), res);
assert(success && "FIXME: Unhandled solver failure");
if (res) {
if (it != found.end()) {
assert(possible == it->second && "Invalid ImpliedValue!");
found.erase(it);
}
} else {
if (it!=found.end()) {
ref<Expr> binding = it->second;
llvm::errs() << "checkForImpliedValues: " << e << " = " << value << "\n"
<< "\t\t implies " << var << " == " << binding
<< " (error)\n";
assert(0);
}
}
}
assert(found.empty());
}
|