зеркало из https://github.com/microsoft/clang-1.git
Moved registration of basic path-sensitive checks from GRSimpleVals.cpp to GRExprEngineInternalChecks.cpp.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@53909 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
e3ae82accd
Коммит
78d46242e3
|
@ -298,7 +298,8 @@ static void ActionWarnUninitVals(AnalysisManager& mgr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf) {
|
static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf,
|
||||||
|
bool StandardWarnings = true) {
|
||||||
|
|
||||||
|
|
||||||
llvm::OwningPtr<GRTransferFuncs> TF(tf);
|
llvm::OwningPtr<GRTransferFuncs> TF(tf);
|
||||||
|
@ -314,6 +315,11 @@ static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf) {
|
||||||
GRExprEngine Eng(*mgr.getCFG(), *mgr.getCodeDecl(), mgr.getContext(), *L);
|
GRExprEngine Eng(*mgr.getCFG(), *mgr.getCodeDecl(), mgr.getContext(), *L);
|
||||||
Eng.setTransferFunctions(tf);
|
Eng.setTransferFunctions(tf);
|
||||||
|
|
||||||
|
if (StandardWarnings) {
|
||||||
|
Eng.RegisterInternalChecks();
|
||||||
|
RegisterAppleChecks(Eng);
|
||||||
|
}
|
||||||
|
|
||||||
// Execute the worklist algorithm.
|
// Execute the worklist algorithm.
|
||||||
Eng.ExecuteWorkList();
|
Eng.ExecuteWorkList();
|
||||||
|
|
||||||
|
@ -330,10 +336,9 @@ static void ActionCheckerCFRefAux(AnalysisManager& mgr, bool GCEnabled,
|
||||||
|
|
||||||
GRTransferFuncs* TF = MakeCFRefCountTF(mgr.getContext(),
|
GRTransferFuncs* TF = MakeCFRefCountTF(mgr.getContext(),
|
||||||
GCEnabled,
|
GCEnabled,
|
||||||
StandardWarnings,
|
|
||||||
mgr.getLangOptions());
|
mgr.getLangOptions());
|
||||||
|
|
||||||
ActionGRExprEngine(mgr, TF);
|
ActionGRExprEngine(mgr, TF, StandardWarnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ActionCheckerCFRef(AnalysisManager& mgr) {
|
static void ActionCheckerCFRef(AnalysisManager& mgr) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ class LiveVariables;
|
||||||
class BugReporter;
|
class BugReporter;
|
||||||
class ObjCImplementationDecl;
|
class ObjCImplementationDecl;
|
||||||
class LangOptions;
|
class LangOptions;
|
||||||
|
class GRExprEngine;
|
||||||
|
|
||||||
void CheckDeadStores(LiveVariables& L, BugReporter& BR);
|
void CheckDeadStores(LiveVariables& L, BugReporter& BR);
|
||||||
|
|
||||||
|
@ -38,7 +39,6 @@ void CheckUninitializedValues(CFG& cfg, ASTContext& Ctx, Diagnostic& Diags,
|
||||||
|
|
||||||
GRTransferFuncs* MakeGRSimpleValsTF();
|
GRTransferFuncs* MakeGRSimpleValsTF();
|
||||||
GRTransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
GRTransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
||||||
bool StandardWarnings,
|
|
||||||
const LangOptions& lopts);
|
const LangOptions& lopts);
|
||||||
|
|
||||||
void CheckObjCDealloc(ObjCImplementationDecl* D, const LangOptions& L,
|
void CheckObjCDealloc(ObjCImplementationDecl* D, const LangOptions& L,
|
||||||
|
@ -46,6 +46,8 @@ void CheckObjCDealloc(ObjCImplementationDecl* D, const LangOptions& L,
|
||||||
|
|
||||||
void CheckObjCInstMethSignature(ObjCImplementationDecl* ID, BugReporter& BR);
|
void CheckObjCInstMethSignature(ObjCImplementationDecl* ID, BugReporter& BR);
|
||||||
|
|
||||||
|
void RegisterAppleChecks(GRExprEngine& Eng);
|
||||||
|
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -220,6 +220,8 @@ public:
|
||||||
BugTypes.push_back(B);
|
BugTypes.push_back(B);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RegisterInternalChecks();
|
||||||
|
|
||||||
void EmitWarnings(BugReporterData& BRData);
|
void EmitWarnings(BugReporterData& BRData);
|
||||||
|
|
||||||
bool isRetStackAddr(const NodeTy* N) const {
|
bool isRetStackAddr(const NodeTy* N) const {
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
|
|
||||||
#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
|
#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
|
||||||
#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
|
#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
|
||||||
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
||||||
#include "clang/Analysis/PathSensitive/ValueState.h"
|
#include "clang/Analysis/PathSensitive/ValueState.h"
|
||||||
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
||||||
#include "clang/Analysis/PathDiagnostic.h"
|
#include "clang/Analysis/PathDiagnostic.h"
|
||||||
|
#include "clang/Analysis/LocalCheckers.h"
|
||||||
|
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
|
@ -547,3 +550,15 @@ clang::CreateAuditCFNumberCreate(ASTContext& Ctx,
|
||||||
return new AuditCFNumberCreate(Ctx, VMgr);
|
return new AuditCFNumberCreate(Ctx, VMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Check registration.
|
||||||
|
|
||||||
|
void clang::RegisterAppleChecks(GRExprEngine& Eng) {
|
||||||
|
ASTContext& Ctx = Eng.getContext();
|
||||||
|
ValueStateManager* VMgr = &Eng.getStateManager();
|
||||||
|
|
||||||
|
Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, VMgr),
|
||||||
|
Stmt::ObjCMessageExprClass);
|
||||||
|
|
||||||
|
Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, VMgr), Stmt::CallExprClass);
|
||||||
|
}
|
||||||
|
|
|
@ -1244,7 +1244,6 @@ private:
|
||||||
// Instance variables.
|
// Instance variables.
|
||||||
|
|
||||||
RetainSummaryManager Summaries;
|
RetainSummaryManager Summaries;
|
||||||
const bool EmitStandardWarnings;
|
|
||||||
const LangOptions& LOpts;
|
const LangOptions& LOpts;
|
||||||
RefBFactoryTy RefBFactory;
|
RefBFactoryTy RefBFactory;
|
||||||
|
|
||||||
|
@ -1293,10 +1292,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CFRefCount(ASTContext& Ctx, bool gcenabled, bool StandardWarnings,
|
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
|
||||||
const LangOptions& lopts)
|
|
||||||
: Summaries(Ctx, gcenabled),
|
: Summaries(Ctx, gcenabled),
|
||||||
EmitStandardWarnings(StandardWarnings),
|
|
||||||
LOpts(lopts),
|
LOpts(lopts),
|
||||||
RetainSelector(GetNullarySelector("retain", Ctx)),
|
RetainSelector(GetNullarySelector("retain", Ctx)),
|
||||||
ReleaseSelector(GetNullarySelector("release", Ctx)),
|
ReleaseSelector(GetNullarySelector("release", Ctx)),
|
||||||
|
@ -2223,7 +2220,6 @@ namespace {
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
|
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
|
||||||
if (EmitStandardWarnings) GRSimpleVals::RegisterChecks(Eng);
|
|
||||||
Eng.Register(new UseAfterRelease(*this));
|
Eng.Register(new UseAfterRelease(*this));
|
||||||
Eng.Register(new BadRelease(*this));
|
Eng.Register(new BadRelease(*this));
|
||||||
Eng.Register(new Leak(*this));
|
Eng.Register(new Leak(*this));
|
||||||
|
@ -2593,7 +2589,6 @@ bool Leak::isCached(BugReport& R) {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
|
||||||
bool StandardWarnings,
|
|
||||||
const LangOptions& lopts) {
|
const LangOptions& lopts) {
|
||||||
return new CFRefCount(Ctx, GCEnabled, StandardWarnings, lopts);
|
return new CFRefCount(Ctx, GCEnabled, lopts);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,332 @@
|
||||||
|
//=-- GRExprEngineInternalChecks.cpp - Builtin GRExprEngine Checks---*- 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 the BugType classes used by GRExprEngine to report
|
||||||
|
// bugs derived from builtin checks in the path-sensitive engine.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
||||||
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
||||||
|
#include "llvm/Support/Compiler.h"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Utility functions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
template <typename ITERATOR> inline
|
||||||
|
ExplodedNode<ValueState>* GetNode(ITERATOR I) {
|
||||||
|
return *I;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline
|
||||||
|
ExplodedNode<ValueState>* GetNode(GRExprEngine::undef_arg_iterator I) {
|
||||||
|
return I->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Bug Descriptions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class VISIBILITY_HIDDEN BuiltinBug : public BugTypeCacheLocation {
|
||||||
|
const char* name;
|
||||||
|
const char* desc;
|
||||||
|
public:
|
||||||
|
BuiltinBug(const char* n, const char* d) : name(n), desc(d) {}
|
||||||
|
virtual const char* getName() const { return name; }
|
||||||
|
virtual const char* getDescription() const { return desc; }
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) = 0;
|
||||||
|
virtual void EmitWarnings(BugReporter& BR) {
|
||||||
|
EmitBuiltinWarnings(BR, cast<GRBugReporter>(BR).getEngine());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ITER>
|
||||||
|
void Emit(BugReporter& BR, ITER I, ITER E) {
|
||||||
|
for (; I != E; ++I) {
|
||||||
|
BugReport R(*this, GetNode(I));
|
||||||
|
BR.EmitWarning(R);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN NullDeref : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
NullDeref() : BuiltinBug("null dereference",
|
||||||
|
"Dereference of null pointer.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
Emit(BR, Eng.null_derefs_begin(), Eng.null_derefs_end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
UndefinedDeref() : BuiltinBug("bad dereference",
|
||||||
|
"Dereference of undefined value.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
Emit(BR, Eng.undef_derefs_begin(), Eng.undef_derefs_end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN DivZero : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
DivZero() : BuiltinBug("divide-by-zero",
|
||||||
|
"Division by zero/undefined value.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
Emit(BR, Eng.explicit_bad_divides_begin(), Eng.explicit_bad_divides_end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN UndefResult : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
UndefResult() : BuiltinBug("undefined result",
|
||||||
|
"Result of operation is undefined.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
Emit(BR, Eng.undef_results_begin(), Eng.undef_results_end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN BadCall : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
BadCall()
|
||||||
|
: BuiltinBug("invalid function call",
|
||||||
|
"Called function is a NULL or undefined function pointer value.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
Emit(BR, Eng.bad_calls_begin(), Eng.bad_calls_end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN BadArg : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
BadArg() : BuiltinBug("bad argument",
|
||||||
|
"Pass-by-value argument in function is undefined.") {}
|
||||||
|
|
||||||
|
BadArg(const char* d) : BuiltinBug("bad argument", d) {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
for (GRExprEngine::UndefArgsTy::iterator I = Eng.undef_arg_begin(),
|
||||||
|
E = Eng.undef_arg_end(); I!=E; ++I) {
|
||||||
|
|
||||||
|
// Generate a report for this bug.
|
||||||
|
RangedBugReport report(*this, I->first);
|
||||||
|
report.addRange(I->second->getSourceRange());
|
||||||
|
|
||||||
|
// Emit the warning.
|
||||||
|
BR.EmitWarning(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN BadMsgExprArg : public BadArg {
|
||||||
|
public:
|
||||||
|
BadMsgExprArg()
|
||||||
|
: BadArg("Pass-by-value argument in message expression is undefined.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
for (GRExprEngine::UndefArgsTy::iterator I=Eng.msg_expr_undef_arg_begin(),
|
||||||
|
E = Eng.msg_expr_undef_arg_end(); I!=E; ++I) {
|
||||||
|
|
||||||
|
// Generate a report for this bug.
|
||||||
|
RangedBugReport report(*this, I->first);
|
||||||
|
report.addRange(I->second->getSourceRange());
|
||||||
|
|
||||||
|
// Emit the warning.
|
||||||
|
BR.EmitWarning(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN BadReceiver : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
BadReceiver()
|
||||||
|
: BuiltinBug("bad receiver",
|
||||||
|
"Receiver in message expression is an uninitialized value.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
for (GRExprEngine::UndefReceiversTy::iterator I=Eng.undef_receivers_begin(),
|
||||||
|
End = Eng.undef_receivers_end(); I!=End; ++I) {
|
||||||
|
|
||||||
|
// Generate a report for this bug.
|
||||||
|
RangedBugReport report(*this, *I);
|
||||||
|
|
||||||
|
ExplodedNode<ValueState>* N = *I;
|
||||||
|
Stmt *S = cast<PostStmt>(N->getLocation()).getStmt();
|
||||||
|
Expr* E = cast<ObjCMessageExpr>(S)->getReceiver();
|
||||||
|
assert (E && "Receiver cannot be NULL");
|
||||||
|
report.addRange(E->getSourceRange());
|
||||||
|
|
||||||
|
// Emit the warning.
|
||||||
|
BR.EmitWarning(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN RetStack : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
RetStack() : BuiltinBug("return of stack address",
|
||||||
|
"Address of stack-allocated variable returned.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
Emit(BR, Eng.ret_stackaddr_begin(), Eng.ret_stackaddr_end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN UndefBranch : public BuiltinBug {
|
||||||
|
struct VISIBILITY_HIDDEN FindUndefExpr {
|
||||||
|
ValueStateManager& VM;
|
||||||
|
const ValueState* St;
|
||||||
|
|
||||||
|
FindUndefExpr(ValueStateManager& V, const ValueState* S) : VM(V), St(S) {}
|
||||||
|
|
||||||
|
Expr* FindExpr(Expr* Ex) {
|
||||||
|
|
||||||
|
if (!MatchesCriteria(Ex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end(); I!=E; ++I)
|
||||||
|
if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) {
|
||||||
|
Expr* E2 = FindExpr(ExI);
|
||||||
|
if (E2) return E2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MatchesCriteria(Expr* Ex) { return VM.GetRVal(St, Ex).isUndef(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
UndefBranch()
|
||||||
|
: BuiltinBug("uninitialized value",
|
||||||
|
"Branch condition evaluates to an uninitialized value.") {}
|
||||||
|
|
||||||
|
virtual void EmitBuiltinWarnings(BugReporter& BR, GRExprEngine& Eng) {
|
||||||
|
for (GRExprEngine::undef_branch_iterator I=Eng.undef_branches_begin(),
|
||||||
|
E=Eng.undef_branches_end(); I!=E; ++I) {
|
||||||
|
|
||||||
|
// What's going on here: we want to highlight the subexpression of the
|
||||||
|
// condition that is the most likely source of the "uninitialized
|
||||||
|
// branch condition." We do a recursive walk of the condition's
|
||||||
|
// subexpressions and roughly look for the most nested subexpression
|
||||||
|
// that binds to Undefined. We then highlight that expression's range.
|
||||||
|
|
||||||
|
BlockEdge B = cast<BlockEdge>((*I)->getLocation());
|
||||||
|
Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition());
|
||||||
|
assert (Ex && "Block must have a terminator.");
|
||||||
|
|
||||||
|
// Get the predecessor node and check if is a PostStmt with the Stmt
|
||||||
|
// being the terminator condition. We want to inspect the state
|
||||||
|
// of that node instead because it will contain main information about
|
||||||
|
// the subexpressions.
|
||||||
|
|
||||||
|
assert (!(*I)->pred_empty());
|
||||||
|
|
||||||
|
// Note: any predecessor will do. They should have identical state,
|
||||||
|
// since all the BlockEdge did was act as an error sink since the value
|
||||||
|
// had to already be undefined.
|
||||||
|
ExplodedNode<ValueState> *N = *(*I)->pred_begin();
|
||||||
|
ProgramPoint P = N->getLocation();
|
||||||
|
|
||||||
|
const ValueState* St = (*I)->getState();
|
||||||
|
|
||||||
|
if (PostStmt* PS = dyn_cast<PostStmt>(&P))
|
||||||
|
if (PS->getStmt() == Ex)
|
||||||
|
St = N->getState();
|
||||||
|
|
||||||
|
FindUndefExpr FindIt(Eng.getStateManager(), St);
|
||||||
|
Ex = FindIt.FindExpr(Ex);
|
||||||
|
|
||||||
|
RangedBugReport R(*this, *I);
|
||||||
|
R.addRange(Ex->getSourceRange());
|
||||||
|
|
||||||
|
BR.EmitWarning(R);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// __attribute__(nonnull) checking
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN CheckAttrNonNull : public GRSimpleAPICheck {
|
||||||
|
SimpleBugType BT;
|
||||||
|
std::list<RangedBugReport> Reports;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CheckAttrNonNull() :
|
||||||
|
BT("'nonnull' argument passed null",
|
||||||
|
"Null pointer passed as an argument to a 'nonnull' parameter") {}
|
||||||
|
|
||||||
|
virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager& VMgr) {
|
||||||
|
CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
|
||||||
|
const ValueState* state = N->getState();
|
||||||
|
|
||||||
|
RVal X = VMgr.GetRVal(state, CE->getCallee());
|
||||||
|
|
||||||
|
if (!isa<lval::FuncVal>(X))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FunctionDecl* FD = dyn_cast<FunctionDecl>(cast<lval::FuncVal>(X).getDecl());
|
||||||
|
const NonNullAttr* Att = FD->getAttr<NonNullAttr>();
|
||||||
|
|
||||||
|
if (!Att)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Iterate through the arguments of CE and check them for null.
|
||||||
|
|
||||||
|
unsigned idx = 0;
|
||||||
|
bool hasError = false;
|
||||||
|
|
||||||
|
for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E;
|
||||||
|
++I, ++idx) {
|
||||||
|
|
||||||
|
if (!VMgr.isEqual(state, *I, 0) || !Att->isNonNull(idx))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
RangedBugReport R(BT, N);
|
||||||
|
R.addRange((*I)->getSourceRange());
|
||||||
|
Reports.push_back(R);
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasError;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void EmitWarnings(BugReporter& BR) {
|
||||||
|
for (std::list<RangedBugReport>::iterator I=Reports.begin(),
|
||||||
|
E=Reports.end(); I!=E; ++I)
|
||||||
|
BR.EmitWarning(*I);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Check registration.
|
||||||
|
|
||||||
|
void GRExprEngine::RegisterInternalChecks() {
|
||||||
|
Register(new NullDeref());
|
||||||
|
Register(new UndefinedDeref());
|
||||||
|
Register(new UndefBranch());
|
||||||
|
Register(new DivZero());
|
||||||
|
Register(new UndefResult());
|
||||||
|
Register(new BadCall());
|
||||||
|
Register(new RetStack());
|
||||||
|
Register(new BadArg());
|
||||||
|
Register(new BadMsgExprArg());
|
||||||
|
Register(new BadReceiver());
|
||||||
|
AddCheck(new CheckAttrNonNull(), Stmt::CallExprClass);
|
||||||
|
}
|
|
@ -26,400 +26,6 @@
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Utility functions.
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
template <typename ITERATOR> inline
|
|
||||||
ExplodedNode<ValueState>* GetNode(ITERATOR I) {
|
|
||||||
return *I;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> inline
|
|
||||||
ExplodedNode<ValueState>* GetNode(GRExprEngine::undef_arg_iterator I) {
|
|
||||||
return I->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ITER>
|
|
||||||
void GenericEmitWarnings(BugReporter& BR, BugType& D, ITER I, ITER E) {
|
|
||||||
|
|
||||||
for (; I != E; ++I) {
|
|
||||||
BugReport R(D, GetNode(I));
|
|
||||||
BR.EmitWarning(R);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Bug Descriptions.
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN NullDeref : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "null dereference";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Dereference of null pointer.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
GenericEmitWarnings(BR, *this, Eng.null_derefs_begin(),
|
|
||||||
Eng.null_derefs_end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN UndefDeref : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "bad dereference";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Dereference of undefined value.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
GenericEmitWarnings(BR, *this, Eng.undef_derefs_begin(),
|
|
||||||
Eng.undef_derefs_end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN UndefBranch : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "uninitialized value";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Branch condition evaluates to an uninitialized value.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR);
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN DivZero : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "divide-by-zero";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Division by zero/undefined value.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
GenericEmitWarnings(BR, *this, Eng.explicit_bad_divides_begin(),
|
|
||||||
Eng.explicit_bad_divides_end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN UndefResult : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "undefined result";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Result of operation is undefined.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
GenericEmitWarnings(BR, *this, Eng.undef_results_begin(),
|
|
||||||
Eng.undef_results_end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN BadCall : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "invalid function call";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Called function is a NULL or undefined function pointer value.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
GenericEmitWarnings(BR, *this, Eng.bad_calls_begin(),
|
|
||||||
Eng.bad_calls_end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN BadArg : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~BadArg() {}
|
|
||||||
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "bad argument";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Pass-by-value argument in function is undefined.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
|
|
||||||
for (GRExprEngine::UndefArgsTy::iterator I = Eng.undef_arg_begin(),
|
|
||||||
E = Eng.undef_arg_end(); I!=E; ++I) {
|
|
||||||
|
|
||||||
// Generate a report for this bug.
|
|
||||||
RangedBugReport report(*this, I->first);
|
|
||||||
report.addRange(I->second->getSourceRange());
|
|
||||||
|
|
||||||
// Emit the warning.
|
|
||||||
BR.EmitWarning(report);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN BadMsgExprArg : public BadArg {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "bad argument";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Pass-by-value argument in message expression is undefined.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
|
|
||||||
for (GRExprEngine::UndefArgsTy::iterator I=Eng.msg_expr_undef_arg_begin(),
|
|
||||||
E = Eng.msg_expr_undef_arg_end(); I!=E; ++I) {
|
|
||||||
|
|
||||||
// Generate a report for this bug.
|
|
||||||
RangedBugReport report(*this, I->first);
|
|
||||||
report.addRange(I->second->getSourceRange());
|
|
||||||
|
|
||||||
// Emit the warning.
|
|
||||||
BR.EmitWarning(report);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN BadReceiver : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "bad receiver";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Receiver in message expression is an uninitialized value.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
|
|
||||||
for (GRExprEngine::UndefReceiversTy::iterator I=Eng.undef_receivers_begin(),
|
|
||||||
End = Eng.undef_receivers_end(); I!=End; ++I) {
|
|
||||||
|
|
||||||
// Generate a report for this bug.
|
|
||||||
RangedBugReport report(*this, *I);
|
|
||||||
|
|
||||||
ExplodedNode<ValueState>* N = *I;
|
|
||||||
Stmt *S = cast<PostStmt>(N->getLocation()).getStmt();
|
|
||||||
Expr* E = cast<ObjCMessageExpr>(S)->getReceiver();
|
|
||||||
assert (E && "Receiver cannot be NULL");
|
|
||||||
report.addRange(E->getSourceRange());
|
|
||||||
|
|
||||||
// Emit the warning.
|
|
||||||
BR.EmitWarning(report);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN RetStack : public BugTypeCacheLocation {
|
|
||||||
public:
|
|
||||||
virtual const char* getName() const {
|
|
||||||
return "return of stack address";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const char* getDescription() const {
|
|
||||||
return "Address of stack-allocated variable returned.";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
GenericEmitWarnings(BR, *this, Eng.ret_stackaddr_begin(),
|
|
||||||
Eng.ret_stackaddr_end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end anonymous namespace
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct VISIBILITY_HIDDEN FindUndefExpr {
|
|
||||||
ValueStateManager& VM;
|
|
||||||
const ValueState* St;
|
|
||||||
|
|
||||||
FindUndefExpr(ValueStateManager& V, const ValueState* S) : VM(V), St(S) {}
|
|
||||||
|
|
||||||
Expr* FindExpr(Expr* Ex) {
|
|
||||||
|
|
||||||
if (!MatchesCriteria(Ex))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end(); I!=E; ++I)
|
|
||||||
if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) {
|
|
||||||
Expr* E2 = FindExpr(ExI);
|
|
||||||
if (E2) return E2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MatchesCriteria(Expr* Ex) { return VM.GetRVal(St, Ex).isUndef(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end anonymous namespace
|
|
||||||
|
|
||||||
|
|
||||||
void UndefBranch::EmitWarnings(BugReporter& BR) {
|
|
||||||
|
|
||||||
GRExprEngine& Eng = cast<GRBugReporter>(BR).getEngine();
|
|
||||||
|
|
||||||
for (GRExprEngine::undef_branch_iterator I=Eng.undef_branches_begin(),
|
|
||||||
E=Eng.undef_branches_end(); I!=E; ++I) {
|
|
||||||
|
|
||||||
// What's going on here: we want to highlight the subexpression of the
|
|
||||||
// condition that is the most likely source of the "uninitialized
|
|
||||||
// branch condition." We do a recursive walk of the condition's
|
|
||||||
// subexpressions and roughly look for the most nested subexpression
|
|
||||||
// that binds to Undefined. We then highlight that expression's range.
|
|
||||||
|
|
||||||
BlockEdge B = cast<BlockEdge>((*I)->getLocation());
|
|
||||||
Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition());
|
|
||||||
assert (Ex && "Block must have a terminator.");
|
|
||||||
|
|
||||||
// Get the predecessor node and check if is a PostStmt with the Stmt
|
|
||||||
// being the terminator condition. We want to inspect the state
|
|
||||||
// of that node instead because it will contain main information about
|
|
||||||
// the subexpressions.
|
|
||||||
|
|
||||||
assert (!(*I)->pred_empty());
|
|
||||||
|
|
||||||
// Note: any predecessor will do. They should have identical state,
|
|
||||||
// since all the BlockEdge did was act as an error sink since the value
|
|
||||||
// had to already be undefined.
|
|
||||||
ExplodedNode<ValueState> *N = *(*I)->pred_begin();
|
|
||||||
ProgramPoint P = N->getLocation();
|
|
||||||
|
|
||||||
const ValueState* St = (*I)->getState();
|
|
||||||
|
|
||||||
if (PostStmt* PS = dyn_cast<PostStmt>(&P))
|
|
||||||
if (PS->getStmt() == Ex)
|
|
||||||
St = N->getState();
|
|
||||||
|
|
||||||
FindUndefExpr FindIt(Eng.getStateManager(), St);
|
|
||||||
Ex = FindIt.FindExpr(Ex);
|
|
||||||
|
|
||||||
RangedBugReport R(*this, *I);
|
|
||||||
R.addRange(Ex->getSourceRange());
|
|
||||||
|
|
||||||
BR.EmitWarning(R);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// __attribute__(nonnull) checking
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN CheckAttrNonNull : public GRSimpleAPICheck {
|
|
||||||
SimpleBugType BT;
|
|
||||||
std::list<RangedBugReport> Reports;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CheckAttrNonNull() :
|
|
||||||
BT("'nonnull' argument passed null",
|
|
||||||
"Null pointer passed as an argument to a 'nonnull' parameter") {}
|
|
||||||
|
|
||||||
|
|
||||||
virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager& VMgr) {
|
|
||||||
CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
|
|
||||||
const ValueState* state = N->getState();
|
|
||||||
|
|
||||||
RVal X = VMgr.GetRVal(state, CE->getCallee());
|
|
||||||
|
|
||||||
if (!isa<lval::FuncVal>(X))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
FunctionDecl* FD = dyn_cast<FunctionDecl>(cast<lval::FuncVal>(X).getDecl());
|
|
||||||
const NonNullAttr* Att = FD->getAttr<NonNullAttr>();
|
|
||||||
|
|
||||||
if (!Att)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Iterate through the arguments of CE and check them for null.
|
|
||||||
|
|
||||||
unsigned idx = 0;
|
|
||||||
bool hasError = false;
|
|
||||||
|
|
||||||
for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E;
|
|
||||||
++I, ++idx) {
|
|
||||||
|
|
||||||
if (!VMgr.isEqual(state, *I, 0) || !Att->isNonNull(idx))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
RangedBugReport R(BT, N);
|
|
||||||
R.addRange((*I)->getSourceRange());
|
|
||||||
Reports.push_back(R);
|
|
||||||
hasError = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasError;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void EmitWarnings(BugReporter& BR) {
|
|
||||||
for (std::list<RangedBugReport>::iterator I=Reports.begin(),
|
|
||||||
E=Reports.end(); I!=E; ++I)
|
|
||||||
BR.EmitWarning(*I);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// Check registration.
|
|
||||||
|
|
||||||
void GRSimpleVals::RegisterChecks(GRExprEngine& Eng) {
|
|
||||||
|
|
||||||
// Path-sensitive checks.
|
|
||||||
Eng.Register(new NullDeref());
|
|
||||||
Eng.Register(new UndefDeref());
|
|
||||||
Eng.Register(new UndefBranch());
|
|
||||||
Eng.Register(new DivZero());
|
|
||||||
Eng.Register(new UndefResult());
|
|
||||||
Eng.Register(new BadCall());
|
|
||||||
Eng.Register(new RetStack());
|
|
||||||
Eng.Register(new BadArg());
|
|
||||||
Eng.Register(new BadMsgExprArg());
|
|
||||||
Eng.Register(new BadReceiver());
|
|
||||||
|
|
||||||
// Add extra checkers.
|
|
||||||
ASTContext& Ctx = Eng.getContext();
|
|
||||||
ValueStateManager* VMgr = &Eng.getStateManager();
|
|
||||||
|
|
||||||
GRSimpleAPICheck* Check = CreateBasicObjCFoundationChecks(Ctx, VMgr);
|
|
||||||
Eng.AddCheck(Check, Stmt::ObjCMessageExprClass);
|
|
||||||
|
|
||||||
Check = CreateAuditCFNumberCreate(Ctx, VMgr);
|
|
||||||
Eng.AddCheck(Check, Stmt::CallExprClass);
|
|
||||||
|
|
||||||
Eng.AddCheck(new CheckAttrNonNull(), Stmt::CallExprClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Transfer Function creation for External clients.
|
// Transfer Function creation for External clients.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -35,8 +35,6 @@ public:
|
||||||
GRSimpleVals() {}
|
GRSimpleVals() {}
|
||||||
virtual ~GRSimpleVals() {}
|
virtual ~GRSimpleVals() {}
|
||||||
|
|
||||||
virtual void RegisterChecks(GRExprEngine& Eng);
|
|
||||||
|
|
||||||
// Casts.
|
// Casts.
|
||||||
|
|
||||||
virtual RVal EvalCast(GRExprEngine& Engine, NonLVal V, QualType CastT);
|
virtual RVal EvalCast(GRExprEngine& Engine, NonLVal V, QualType CastT);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче