Newer
Older
// GRSimpleVals.cpp - Transfer functions for tracking simple values -*- C++ -*--
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines GRSimpleVals, a sub-class of GRTransferFuncs that
// provides transfer functions for performing simple value tracking with
// limited support for symbolics.
//
//===----------------------------------------------------------------------===//
#include "GRSimpleVals.h"
Ted Kremenek
committed
#include "BasicObjCFoundationChecks.h"
#include "clang/Basic/SourceManager.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek
committed
#include <sstream>
using namespace clang;
//===----------------------------------------------------------------------===//
// Bug Descriptions.
//===----------------------------------------------------------------------===//
namespace bugdesc {
struct NullDeref {
static const char* getName() { return "Null dereference"; }
static PathDiagnosticPiece* getEndPath(SourceManager& SMgr,
ExplodedNode<ValueState> *N);
};
PathDiagnosticPiece* NullDeref::getEndPath(SourceManager& SMgr,
ExplodedNode<ValueState> *N) {
Expr* E = cast<Expr>(cast<PostStmt>(N->getLocation()).getStmt());
// FIXME: Do better ranges for different kinds of null dereferences.
FullSourceLoc L(E->getLocStart(), SMgr);
PathDiagnosticPiece* P = new PathDiagnosticPiece(L, getName());
P->addRange(E->getSourceRange());
return P;
}
} // end namespace: bugdesc
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
template <typename ITERATOR>
static inline ExplodedNode<ValueState>* GetNode(ITERATOR I) {
return *I;
}
template <>
static inline ExplodedNode<ValueState>*
GetNode(GRExprEngine::undef_arg_iterator I) {
return I->first;
}
template <typename ITERATOR>
static inline ProgramPoint GetLocation(ITERATOR I) {
return (*I)->getLocation();
}
template <>
static inline ProgramPoint GetLocation(GRExprEngine::undef_arg_iterator I) {
return I->first->getLocation();
}
static inline Stmt* GetStmt(const ProgramPoint& P) {
if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
return PS->getStmt();
}
else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
return BE->getSrc()->getTerminator();
}
else if (const BlockEntrance* BE = dyn_cast<BlockEntrance>(&P)) {
return BE->getFirstStmt();
}
assert (false && "Unsupported ProgramPoint.");
return NULL;
//===----------------------------------------------------------------------===//
// Pathless Warnings
//===----------------------------------------------------------------------===//
template <typename ITERATOR>
Ted Kremenek
committed
static void EmitDiag(Diagnostic& Diag, PathDiagnosticClient* PD,
SourceManager& SrcMgr,
unsigned ErrorDiag, ITERATOR I) {
Stmt* S = GetStmt(GetLocation(I));
SourceRange R = S->getSourceRange();
Diag.Report(PD, FullSourceLoc(S->getLocStart(), SrcMgr), ErrorDiag,
NULL, 0, &R, 1);
}
template <>
Ted Kremenek
committed
static void EmitDiag(Diagnostic& Diag, PathDiagnosticClient* PD,
SourceManager& SrcMgr, unsigned ErrorDiag,
GRExprEngine::undef_arg_iterator I) {
Stmt* S1 = GetStmt(GetLocation(I));
Expr* E2 = cast<Expr>(I->second);
SourceLocation Loc = S1->getLocStart();
SourceRange R = E2->getSourceRange();
Ted Kremenek
committed
Diag.Report(PD, FullSourceLoc(Loc, SrcMgr), ErrorDiag, 0, 0, &R, 1);
template <typename ITERATOR>
static void EmitWarning(Diagnostic& Diag, PathDiagnosticClient* PD,
SourceManager& SrcMgr,
ITERATOR I, ITERATOR E, const char* msg) {
Ted Kremenek
committed
std::ostringstream Out;
std::string Str(msg);
if (!PD) {
Out << "[CHECKER] " << msg;
Str = Out.str();
msg = Str.c_str();
Ted Kremenek
committed
unsigned ErrorDiag = 0;
llvm::SmallPtrSet<void*,10> CachedErrors;
for (; I != E; ++I) {
if (isFirst) {
isFirst = false;
ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, msg);
}
else {
// HACK: Cache the location of the error. Don't emit the same
// warning for the same error type that occurs at the same program
// location but along a different path.
void* p = GetLocation(I).getRawData();
if (CachedErrors.count(p))
continue;
CachedErrors.insert(p);
}
EmitDiag(Diag, PD, SrcMgr, ErrorDiag, I);
}
}
//===----------------------------------------------------------------------===//
// Path warnings.
//===----------------------------------------------------------------------===//
static void GeneratePathDiagnostic(PathDiagnostic& PD,
ASTContext& Ctx,
ExplodedNode<ValueState>* N) {
SourceManager& SMgr = Ctx.getSourceManager();
while (!N->pred_empty()) {
ExplodedNode<ValueState>* LastNode = N;
N = *(N->pred_begin());
ProgramPoint P = N->getLocation();
if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
CFGBlock* Src = BE->getSrc();
CFGBlock* Dst = BE->getDst();
Stmt* T = Src->getTerminator();
if (!T)
continue;
FullSourceLoc L(T->getLocStart(), SMgr);
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
257
258
259
260
261
262
263
264
switch (T->getStmtClass()) {
default:
break;
case Stmt::GotoStmtClass:
case Stmt::IndirectGotoStmtClass: {
Stmt* S = GetStmt(LastNode->getLocation());
if (!S)
continue;
std::ostringstream os;
os << "Control jumps to line "
<< SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
PD.push_front(new PathDiagnosticPiece(L, os.str()));
break;
}
case Stmt::SwitchStmtClass: {
// Figure out what case arm we took.
Stmt* S = Dst->getLabel();
if (!S)
continue;
std::ostringstream os;
switch (S->getStmtClass()) {
default:
continue;
case Stmt::DefaultStmtClass: {
os << "Control jumps to the 'default' case at line "
<< SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
break;
}
case Stmt::CaseStmtClass: {
os << "Control jumps to 'case ";
Expr* CondE = cast<SwitchStmt>(T)->getCond();
unsigned bits = Ctx.getTypeSize(CondE->getType());
llvm::APSInt V1(bits, false);
CaseStmt* Case = cast<CaseStmt>(S);
if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
assert (false &&
"Case condition must evaluate to an integer constant.");
continue;
}
os << V1.toString();
// Get the RHS of the case, if it exists.
if (Expr* E = Case->getRHS()) {
llvm::APSInt V2(bits, false);
if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
assert (false &&
"Case condition (RHS) must evaluate to an integer constant.");
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
continue;
}
os << " .. " << V2.toString();
}
os << ":' at line "
<< SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
break;
}
}
PD.push_front(new PathDiagnosticPiece(L, os.str()));
break;
}
case Stmt::DoStmtClass:
case Stmt::WhileStmtClass:
case Stmt::ForStmtClass:
case Stmt::IfStmtClass: {
if (*(Src->succ_begin()+1) == Dst)
PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
else
PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
break;
}
}
}
}
}
template <typename ITERATOR, typename DESC>
static void Report(PathDiagnosticClient& PDC, ASTContext& Ctx, DESC,
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
ITERATOR I, ITERATOR E) {
if (I == E)
return;
const char* BugName = DESC::getName();
llvm::SmallPtrSet<void*,10> CachedErrors;
for (; I != E; ++I) {
// HACK: Cache the location of the error. Don't emit the same
// warning for the same error type that occurs at the same program
// location but along a different path.
void* p = GetLocation(I).getRawData();
if (CachedErrors.count(p))
continue;
CachedErrors.insert(p);
// Create the PathDiagnostic.
PathDiagnostic D(BugName);
// Get the end-of-path diagnostic.
D.push_back(DESC::getEndPath(Ctx.getSourceManager(), GetNode(I)));
// Generate the rest of the diagnostic.
GeneratePathDiagnostic(D, Ctx, GetNode(I));
PDC.HandlePathDiagnostic(D);
}
}
//===----------------------------------------------------------------------===//
// Analysis Driver.
//===----------------------------------------------------------------------===//
namespace clang {
Ted Kremenek
committed
unsigned RunGRSimpleVals(CFG& cfg, Decl& CD, ASTContext& Ctx,
Diagnostic& Diag, PathDiagnosticClient* PD,
bool Visualize, bool TrimGraph) {
Ted Kremenek
committed
GRCoreEngine<GRExprEngine> Eng(cfg, CD, Ctx);
GRExprEngine* CS = &Eng.getCheckerState();
Ted Kremenek
committed
// Set base transfer functions.
GRSimpleVals GRSV;
CS->setTransferFunctions(GRSV);
Ted Kremenek
committed
// Add extra checkers.
llvm::OwningPtr<GRSimpleAPICheck> FoundationCheck(
CreateBasicObjCFoundationChecks(Ctx, &CS->getStateManager()));
Ted Kremenek
committed
CS->AddObjCMessageExprCheck(FoundationCheck.get());
Ted Kremenek
committed
// Execute the worklist algorithm.
Ted Kremenek
committed
Eng.ExecuteWorkList(120000);
SourceManager& SrcMgr = Ctx.getSourceManager();
if (!PD)
EmitWarning(Diag, PD, SrcMgr,
CS->null_derefs_begin(), CS->null_derefs_end(),
"Dereference of NULL pointer.");
else
Report(*PD, Ctx, bugdesc::NullDeref(),
CS->null_derefs_begin(), CS->null_derefs_end());
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->undef_derefs_begin(),
CS->undef_derefs_end(),
Ted Kremenek
committed
"Dereference of undefined value.");
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->undef_branches_begin(),
CS->undef_branches_end(),
"Branch condition evaluates to an uninitialized value.");
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->explicit_bad_divides_begin(),
CS->explicit_bad_divides_end(),
Ted Kremenek
committed
"Division by zero/undefined value.");
Ted Kremenek
committed
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->undef_results_begin(),
CS->undef_results_end(),
Ted Kremenek
committed
"Result of operation is undefined.");
Ted Kremenek
committed
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->bad_calls_begin(),
CS->bad_calls_end(),
Ted Kremenek
committed
"Call using a NULL or undefined function pointer value.");
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->undef_arg_begin(),
CS->undef_arg_end(),
"Pass-by-value argument in function is undefined.");
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->msg_expr_undef_arg_begin(),
CS->msg_expr_undef_arg_end(),
"Pass-by-value argument in message expression is undefined.");
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->undef_receivers_begin(),
CS->undef_receivers_end(),
"Receiver in message expression is an uninitialized value.");
Ted Kremenek
committed
EmitWarning(Diag, PD, SrcMgr,
CS->ret_stackaddr_begin(),
CS->ret_stackaddr_end(),
"Address of stack-allocated variable returned.");
FoundationCheck.get()->ReportResults(Diag);
if (Visualize) CS->ViewGraph(TrimGraph);
#endif
return Eng.getGraph().size();
}
//===----------------------------------------------------------------------===//
// Transfer function for Casts.
//===----------------------------------------------------------------------===//
RVal GRSimpleVals::EvalCast(GRExprEngine& Eng, NonLVal X, QualType T) {
if (!isa<nonlval::ConcreteInt>(X))
return UnknownVal();
BasicValueFactory& BasicVals = Eng.getBasicVals();
llvm::APSInt V = cast<nonlval::ConcreteInt>(X).getValue();
V.setIsUnsigned(T->isUnsignedIntegerType() || T->isPointerType()
|| T->isObjCQualifiedIdType());
V.extOrTrunc(Eng.getContext().getTypeSize(T));
if (T->isPointerType())
return lval::ConcreteInt(BasicVals.getValue(V));
else
return nonlval::ConcreteInt(BasicVals.getValue(V));
}
// Casts.
RVal GRSimpleVals::EvalCast(GRExprEngine& Eng, LVal X, QualType T) {
if (T->isPointerLikeType() || T->isObjCQualifiedIdType())
return X;
assert (T->isIntegerType());
if (!isa<lval::ConcreteInt>(X))
return UnknownVal();
BasicValueFactory& BasicVals = Eng.getBasicVals();
llvm::APSInt V = cast<lval::ConcreteInt>(X).getValue();
V.setIsUnsigned(T->isUnsignedIntegerType() || T->isPointerType());
V.extOrTrunc(Eng.getContext().getTypeSize(T));
return nonlval::ConcreteInt(BasicVals.getValue(V));
Ted Kremenek
committed
}
// Unary operators.
RVal GRSimpleVals::EvalMinus(GRExprEngine& Eng, UnaryOperator* U, NonLVal X){
Ted Kremenek
committed
switch (X.getSubKind()) {
Ted Kremenek
committed
case nonlval::ConcreteIntKind:
return cast<nonlval::ConcreteInt>(X).EvalMinus(Eng.getBasicVals(), U);
Ted Kremenek
committed
default:
return UnknownVal();
RVal GRSimpleVals::EvalComplement(GRExprEngine& Eng, NonLVal X) {
Ted Kremenek
committed
switch (X.getSubKind()) {
Ted Kremenek
committed
case nonlval::ConcreteIntKind:
return cast<nonlval::ConcreteInt>(X).EvalComplement(Eng.getBasicVals());
Ted Kremenek
committed
default:
return UnknownVal();
Ted Kremenek
committed
}
}
// Binary operators.
RVal GRSimpleVals::EvalBinOp(GRExprEngine& Eng, BinaryOperator::Opcode Op,
NonLVal L, NonLVal R) {
BasicValueFactory& BasicVals = Eng.getBasicVals();
while (1) {
switch (L.getSubKind()) {
default:
case nonlval::ConcreteIntKind:
if (isa<nonlval::ConcreteInt>(R)) {
const nonlval::ConcreteInt& L_CI = cast<nonlval::ConcreteInt>(L);
const nonlval::ConcreteInt& R_CI = cast<nonlval::ConcreteInt>(R);
return L_CI.EvalBinOp(BasicVals, Op, R_CI);
}
else {
NonLVal tmp = R;
R = L;
L = tmp;
continue;
}
case nonlval::SymbolValKind: {
if (isa<nonlval::ConcreteInt>(R)) {
const SymIntConstraint& C =
BasicVals.getConstraint(cast<nonlval::SymbolVal>(L).getSymbol(), Op,
cast<nonlval::ConcreteInt>(R).getValue());
return nonlval::SymIntConstraintVal(C);
}
else
return UnknownVal();
}
}
}
}
Ted Kremenek
committed
Ted Kremenek
committed
// Binary Operators (except assignments and comma).
RVal GRSimpleVals::EvalBinOp(GRExprEngine& Eng, BinaryOperator::Opcode Op,
LVal L, LVal R) {
Ted Kremenek
committed
switch (Op) {
Ted Kremenek
committed
default:
return UnknownVal();
case BinaryOperator::EQ:
return EvalEQ(Eng, L, R);
Ted Kremenek
committed
case BinaryOperator::NE:
return EvalNE(Eng, L, R);
Ted Kremenek
committed
}
}
Ted Kremenek
committed
// Pointer arithmetic.
RVal GRSimpleVals::EvalBinOp(GRExprEngine& Eng, BinaryOperator::Opcode Op,
LVal L, NonLVal R) {
return UnknownVal();
Ted Kremenek
committed
}
// Equality operators for LVals.
RVal GRSimpleVals::EvalEQ(GRExprEngine& Eng, LVal L, LVal R) {
BasicValueFactory& BasicVals = Eng.getBasicVals();
switch (L.getSubKind()) {
default:
assert(false && "EQ not implemented for this LVal.");
return UnknownVal();
case lval::ConcreteIntKind:
if (isa<lval::ConcreteInt>(R)) {
bool b = cast<lval::ConcreteInt>(L).getValue() ==
cast<lval::ConcreteInt>(R).getValue();
return NonLVal::MakeIntTruthVal(BasicVals, b);
}
else if (isa<lval::SymbolVal>(R)) {
const SymIntConstraint& C =
BasicVals.getConstraint(cast<lval::SymbolVal>(R).getSymbol(),
BinaryOperator::EQ,
cast<lval::ConcreteInt>(L).getValue());
return nonlval::SymIntConstraintVal(C);
}
break;
case lval::SymbolValKind: {
if (isa<lval::ConcreteInt>(R)) {
const SymIntConstraint& C =
BasicVals.getConstraint(cast<lval::SymbolVal>(L).getSymbol(),
BinaryOperator::EQ,
cast<lval::ConcreteInt>(R).getValue());
return nonlval::SymIntConstraintVal(C);
}
// FIXME: Implement == for lval Symbols. This is mainly useful
// in iterator loops when traversing a buffer, e.g. while(z != zTerm).
// Since this is not useful for many checkers we'll punt on this for
// now.
return UnknownVal();
}
case lval::DeclValKind:
Ted Kremenek
committed
case lval::FuncValKind:
case lval::GotoLabelKind:
return NonLVal::MakeIntTruthVal(BasicVals, L == R);
}
return NonLVal::MakeIntTruthVal(BasicVals, false);
}
RVal GRSimpleVals::EvalNE(GRExprEngine& Eng, LVal L, LVal R) {
BasicValueFactory& BasicVals = Eng.getBasicVals();
switch (L.getSubKind()) {
default:
assert(false && "NE not implemented for this LVal.");
return UnknownVal();
case lval::ConcreteIntKind:
if (isa<lval::ConcreteInt>(R)) {
bool b = cast<lval::ConcreteInt>(L).getValue() !=
cast<lval::ConcreteInt>(R).getValue();
return NonLVal::MakeIntTruthVal(BasicVals, b);
}
else if (isa<lval::SymbolVal>(R)) {
const SymIntConstraint& C =
BasicVals.getConstraint(cast<lval::SymbolVal>(R).getSymbol(),
BinaryOperator::NE,
cast<lval::ConcreteInt>(L).getValue());
return nonlval::SymIntConstraintVal(C);
}
break;
case lval::SymbolValKind: {
if (isa<lval::ConcreteInt>(R)) {
const SymIntConstraint& C =
BasicVals.getConstraint(cast<lval::SymbolVal>(L).getSymbol(),
BinaryOperator::NE,
cast<lval::ConcreteInt>(R).getValue());
return nonlval::SymIntConstraintVal(C);
}
// FIXME: Implement != for lval Symbols. This is mainly useful
// in iterator loops when traversing a buffer, e.g. while(z != zTerm).
// Since this is not useful for many checkers we'll punt on this for
// now.
return UnknownVal();
break;
}
case lval::DeclValKind:
Ted Kremenek
committed
case lval::FuncValKind:
case lval::GotoLabelKind:
return NonLVal::MakeIntTruthVal(BasicVals, L != R);
}
return NonLVal::MakeIntTruthVal(BasicVals, true);
}
//===----------------------------------------------------------------------===//
// Transfer function for Function Calls.
//===----------------------------------------------------------------------===//
void GRSimpleVals::EvalCall(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
CallExpr* CE, LVal L,
ExplodedNode<ValueState>* Pred) {
Ted Kremenek
committed
ValueStateManager& StateMgr = Eng.getStateManager();
ValueState* St = Builder.GetState(Pred);
// Invalidate all arguments passed in by reference (LVals).
for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
I != E; ++I) {
Ted Kremenek
committed
RVal V = StateMgr.GetRVal(St, *I);
if (isa<LVal>(V))
Ted Kremenek
committed
St = StateMgr.SetRVal(St, cast<LVal>(V), UnknownVal());
Ted Kremenek
committed
// Make up a symbol for the return value of this function.
if (CE->getType() != Eng.getContext().VoidTy) {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
Ted Kremenek
committed
RVal X = CE->getType()->isPointerType()
? cast<RVal>(lval::SymbolVal(Sym))
: cast<RVal>(nonlval::SymbolVal(Sym));
St = StateMgr.SetRVal(St, CE, X, Eng.getCFG().isBlkExpr(CE), false);
}