forked from OSchip/llvm-project
[analyzer] Migrate MallocChecker to CheckerV2.
llvm-svn: 126606
This commit is contained in:
parent
4e23dbd0fd
commit
183f0fb4cf
|
@ -189,6 +189,12 @@ public:
|
|||
const Stmt *S,
|
||||
ExprEngine &Eng);
|
||||
|
||||
/// \brief Run checkers for binding of a value to a location.
|
||||
void runCheckersForBind(ExplodedNodeSet &Dst,
|
||||
const ExplodedNodeSet &Src,
|
||||
SVal location, SVal val,
|
||||
const Stmt *S, ExprEngine &Eng);
|
||||
|
||||
/// \brief Run checkers for end of analysis.
|
||||
void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,
|
||||
ExprEngine &Eng);
|
||||
|
@ -214,6 +220,10 @@ public:
|
|||
const MemRegion * const *Begin,
|
||||
const MemRegion * const *End);
|
||||
|
||||
/// \brief Run checkers for handling assumptions on symbolic values.
|
||||
const GRState *runCheckersForEvalAssume(const GRState *state,
|
||||
SVal Cond, bool Assumption);
|
||||
|
||||
/// \brief Run checkers for evaluating a call.
|
||||
void runCheckersForEvalCall(ExplodedNodeSet &Dst,
|
||||
const ExplodedNodeSet &Src,
|
||||
|
@ -243,6 +253,8 @@ public:
|
|||
typedef CheckerFn<const ObjCMessage &, CheckerContext &> CheckObjCMessageFunc;
|
||||
typedef CheckerFn<const SVal &/*location*/, bool/*isLoad*/, CheckerContext &>
|
||||
CheckLocationFunc;
|
||||
typedef CheckerFn<const SVal &/*location*/, const SVal &/*val*/,
|
||||
CheckerContext &> CheckBindFunc;
|
||||
typedef CheckerFn<ExplodedGraph &, BugReporter &, ExprEngine &>
|
||||
CheckEndAnalysisFunc;
|
||||
typedef CheckerFn<EndOfFunctionNodeBuilder &, ExprEngine &> CheckEndPathFunc;
|
||||
|
@ -260,6 +272,8 @@ public:
|
|||
|
||||
void _registerForLocation(CheckLocationFunc checkfn);
|
||||
|
||||
void _registerForBind(CheckBindFunc checkfn);
|
||||
|
||||
void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn);
|
||||
|
||||
void _registerForEndPath(CheckEndPathFunc checkfn);
|
||||
|
@ -298,6 +312,21 @@ public:
|
|||
void _registerForRegionChanges(CheckRegionChangesFunc checkfn,
|
||||
WantsRegionChangeUpdateFunc wantUpdateFn);
|
||||
|
||||
class EvalAssumeFunc {
|
||||
typedef const GRState * (*Func)(void *, const GRState *,
|
||||
const SVal &/*cond*/, bool /*assumption*/);
|
||||
Func Fn;
|
||||
public:
|
||||
void *Checker;
|
||||
EvalAssumeFunc(void *checker, Func fn) : Fn(fn), Checker(checker) {}
|
||||
const GRState *operator()(const GRState *state,
|
||||
const SVal &cond, bool assumption) {
|
||||
return Fn(Checker, state, cond, assumption);
|
||||
}
|
||||
};
|
||||
|
||||
void _registerForEvalAssume(EvalAssumeFunc checkfn);
|
||||
|
||||
class EvalCallFunc {
|
||||
typedef bool (*Func)(void *, const CallExpr *, CheckerContext &);
|
||||
Func Fn;
|
||||
|
@ -380,6 +409,8 @@ private:
|
|||
|
||||
std::vector<CheckLocationFunc> LocationCheckers;
|
||||
|
||||
std::vector<CheckBindFunc> BindCheckers;
|
||||
|
||||
std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;
|
||||
|
||||
std::vector<CheckEndPathFunc> EndPathCheckers;
|
||||
|
@ -394,6 +425,8 @@ private:
|
|||
};
|
||||
std::vector<RegionChangesCheckerInfo> RegionChangesCheckers;
|
||||
|
||||
std::vector<EvalAssumeFunc> EvalAssumeCheckers;
|
||||
|
||||
std::vector<EvalCallFunc> EvalCallCheckers;
|
||||
};
|
||||
|
||||
|
|
|
@ -145,6 +145,21 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class Bind {
|
||||
template <typename CHECKER>
|
||||
static void _checkBind(void *checker, const SVal &location, const SVal &val,
|
||||
CheckerContext &C) {
|
||||
((const CHECKER *)checker)->checkBind(location, val, C);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename CHECKER>
|
||||
static void _register(CHECKER *checker, CheckerManager &mgr) {
|
||||
mgr._registerForBind(
|
||||
CheckerManager::CheckBindFunc(checker, _checkBind<CHECKER>));
|
||||
}
|
||||
};
|
||||
|
||||
class EndAnalysis {
|
||||
template <typename CHECKER>
|
||||
static void _checkEndAnalysis(void *checker, ExplodedGraph &G,
|
||||
|
@ -232,6 +247,21 @@ public:
|
|||
|
||||
namespace eval {
|
||||
|
||||
class Assume {
|
||||
template <typename CHECKER>
|
||||
static const GRState *_evalAssume(void *checker, const GRState *state,
|
||||
const SVal &cond, bool assumption) {
|
||||
return ((const CHECKER *)checker)->evalAssume(state, cond, assumption);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename CHECKER>
|
||||
static void _register(CHECKER *checker, CheckerManager &mgr) {
|
||||
mgr._registerForEvalAssume(
|
||||
CheckerManager::EvalAssumeFunc(checker, _evalAssume<CHECKER>));
|
||||
}
|
||||
};
|
||||
|
||||
class Call {
|
||||
template <typename CHECKER>
|
||||
static bool _evalCall(void *checker, const CallExpr *CE, CheckerContext &C) {
|
||||
|
|
|
@ -129,6 +129,11 @@ def CFGDumper : Checker<"DumpCFG">,
|
|||
|
||||
let Group = AllExperimental in {
|
||||
|
||||
def MallocChecker : Checker<"Malloc">,
|
||||
InPackage<CoreExperimental>,
|
||||
HelpText<"Check for potential memory leaks, double free, and use-after-free problems">,
|
||||
DescFile<"MallocChecker.cpp">;
|
||||
|
||||
def CStringChecker : Checker<"CString">,
|
||||
InPackage<CoreExperimental>,
|
||||
HelpText<"Check calls to functions in <string.h>">,
|
||||
|
|
|
@ -22,5 +22,4 @@ using namespace ento;
|
|||
void ento::RegisterExperimentalChecks(ExprEngine &Eng) {
|
||||
// These are checks that never belong as internal checks
|
||||
// within ExprEngine.
|
||||
RegisterMallocChecker(Eng); // ArrayBoundChecker depends on this.
|
||||
}
|
||||
|
|
|
@ -267,10 +267,12 @@ void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst,
|
|||
SVal val, bool isPrevisit) {
|
||||
|
||||
if (Checkers.empty()) {
|
||||
Dst.insert(Src);
|
||||
getCheckerManager().runCheckersForBind(Dst, Src, location, val, StoreE,
|
||||
*this);
|
||||
return;
|
||||
}
|
||||
|
||||
ExplodedNodeSet CheckerV1Tmp;
|
||||
ExplodedNodeSet Tmp;
|
||||
ExplodedNodeSet *PrevSet = &Src;
|
||||
|
||||
|
@ -278,7 +280,7 @@ void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst,
|
|||
{
|
||||
ExplodedNodeSet *CurrSet = 0;
|
||||
if (I+1 == E)
|
||||
CurrSet = &Dst;
|
||||
CurrSet = &CheckerV1Tmp;
|
||||
else {
|
||||
CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||
CurrSet->clear();
|
||||
|
@ -296,6 +298,9 @@ void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst,
|
|||
PrevSet = CurrSet;
|
||||
}
|
||||
|
||||
getCheckerManager().runCheckersForBind(Dst, CheckerV1Tmp, location, val,
|
||||
StoreE, *this);
|
||||
|
||||
// Don't autotransition. The CheckerContext objects should do this
|
||||
// automatically.
|
||||
}
|
||||
|
@ -493,6 +498,8 @@ const GRState *ExprEngine::processAssume(const GRState *state, SVal cond,
|
|||
CO_Ref = NewCO.take();
|
||||
}
|
||||
|
||||
state = getCheckerManager().runCheckersForEvalAssume(state, cond, assumption);
|
||||
|
||||
// If the state is infeasible at this point, bail out.
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ExperimentalChecks.h"
|
||||
#include "ClangSACheckers.h"
|
||||
#include "clang/StaticAnalyzer/Core/CheckerV2.h"
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
||||
|
@ -62,55 +64,52 @@ public:
|
|||
|
||||
class RegionState {};
|
||||
|
||||
class MallocChecker : public CheckerVisitor<MallocChecker> {
|
||||
BuiltinBug *BT_DoubleFree;
|
||||
BuiltinBug *BT_Leak;
|
||||
BuiltinBug *BT_UseFree;
|
||||
BuiltinBug *BT_UseRelinquished;
|
||||
BuiltinBug *BT_BadFree;
|
||||
IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc;
|
||||
class MallocChecker : public CheckerV2<eval::Call, check::DeadSymbols, check::EndPath, check::PreStmt<ReturnStmt>, check::Location,
|
||||
check::Bind, eval::Assume> {
|
||||
mutable llvm::OwningPtr<BuiltinBug> BT_DoubleFree;
|
||||
mutable llvm::OwningPtr<BuiltinBug> BT_Leak;
|
||||
mutable llvm::OwningPtr<BuiltinBug> BT_UseFree;
|
||||
mutable llvm::OwningPtr<BuiltinBug> BT_UseRelinquished;
|
||||
mutable llvm::OwningPtr<BuiltinBug> BT_BadFree;
|
||||
mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc;
|
||||
|
||||
public:
|
||||
MallocChecker()
|
||||
: BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_UseRelinquished(0),
|
||||
BT_BadFree(0),
|
||||
II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {}
|
||||
static void *getTag();
|
||||
bool evalCallExpr(CheckerContext &C, const CallExpr *CE);
|
||||
void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper);
|
||||
void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng);
|
||||
void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S);
|
||||
const GRState *evalAssume(const GRState *state, SVal Cond, bool Assumption,
|
||||
bool *respondsToCallback);
|
||||
void visitLocation(CheckerContext &C, const Stmt *S, SVal l, bool isLoad);
|
||||
virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE,
|
||||
SVal location, SVal val);
|
||||
MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {}
|
||||
|
||||
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
||||
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
|
||||
void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
|
||||
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
|
||||
const GRState *evalAssume(const GRState *state, SVal Cond,
|
||||
bool Assumption) const;
|
||||
void checkLocation(SVal l, bool isLoad, CheckerContext &C) const;
|
||||
void checkBind(SVal location, SVal val, CheckerContext &C) const;
|
||||
|
||||
private:
|
||||
void MallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
|
||||
static void MallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
static void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
|
||||
const OwnershipAttr* Att);
|
||||
const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
static const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
const Expr *SizeEx, SVal Init,
|
||||
const GRState *state) {
|
||||
return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state);
|
||||
}
|
||||
const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
static const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
SVal SizeEx, SVal Init,
|
||||
const GRState *state);
|
||||
|
||||
void FreeMem(CheckerContext &C, const CallExpr *CE);
|
||||
void FreeMem(CheckerContext &C, const CallExpr *CE) const;
|
||||
void FreeMemAttr(CheckerContext &C, const CallExpr *CE,
|
||||
const OwnershipAttr* Att);
|
||||
const OwnershipAttr* Att) const;
|
||||
const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
const GRState *state, unsigned Num, bool Hold);
|
||||
const GRState *state, unsigned Num, bool Hold) const;
|
||||
|
||||
void ReallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
void CallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
void ReallocMem(CheckerContext &C, const CallExpr *CE) const;
|
||||
static void CallocMem(CheckerContext &C, const CallExpr *CE);
|
||||
|
||||
bool SummarizeValue(llvm::raw_ostream& os, SVal V);
|
||||
bool SummarizeRegion(llvm::raw_ostream& os, const MemRegion *MR);
|
||||
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range);
|
||||
static bool SummarizeValue(llvm::raw_ostream& os, SVal V);
|
||||
static bool SummarizeRegion(llvm::raw_ostream& os, const MemRegion *MR);
|
||||
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range) const;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
@ -121,21 +120,12 @@ namespace ento {
|
|||
template <>
|
||||
struct GRStateTrait<RegionState>
|
||||
: public GRStatePartialTrait<RegionStateTy> {
|
||||
static void *GDMIndex() { return MallocChecker::getTag(); }
|
||||
static void *GDMIndex() { static int x; return &x; }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void ento::RegisterMallocChecker(ExprEngine &Eng) {
|
||||
Eng.registerCheck(new MallocChecker());
|
||||
}
|
||||
|
||||
void *MallocChecker::getTag() {
|
||||
static int x;
|
||||
return &x;
|
||||
}
|
||||
|
||||
bool MallocChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) {
|
||||
bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
||||
const GRState *state = C.getState();
|
||||
const Expr *Callee = CE->getCallee();
|
||||
SVal L = state->getSVal(Callee);
|
||||
|
@ -256,7 +246,7 @@ const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
|
|||
return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE));
|
||||
}
|
||||
|
||||
void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) {
|
||||
void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) const {
|
||||
const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false);
|
||||
|
||||
if (state)
|
||||
|
@ -264,7 +254,7 @@ void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) {
|
|||
}
|
||||
|
||||
void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE,
|
||||
const OwnershipAttr* Att) {
|
||||
const OwnershipAttr* Att) const {
|
||||
if (Att->getModule() != "malloc")
|
||||
return;
|
||||
|
||||
|
@ -279,7 +269,7 @@ void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE,
|
|||
|
||||
const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
|
||||
const GRState *state, unsigned Num,
|
||||
bool Hold) {
|
||||
bool Hold) const {
|
||||
const Expr *ArgExpr = CE->getArg(Num);
|
||||
SVal ArgVal = state->getSVal(ArgExpr);
|
||||
|
||||
|
@ -357,9 +347,9 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
|
|||
if (RS->isReleased()) {
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_DoubleFree)
|
||||
BT_DoubleFree
|
||||
= new BuiltinBug("Double free",
|
||||
"Try to free a memory block that has been released");
|
||||
BT_DoubleFree.reset(
|
||||
new BuiltinBug("Double free",
|
||||
"Try to free a memory block that has been released"));
|
||||
// FIXME: should find where it's freed last time.
|
||||
BugReport *R = new BugReport(*BT_DoubleFree,
|
||||
BT_DoubleFree->getDescription(), N);
|
||||
|
@ -463,10 +453,10 @@ bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os,
|
|||
}
|
||||
|
||||
void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
||||
SourceRange range) {
|
||||
SourceRange range) const {
|
||||
if (ExplodedNode *N = C.generateSink()) {
|
||||
if (!BT_BadFree)
|
||||
BT_BadFree = new BuiltinBug("Bad free");
|
||||
BT_BadFree.reset(new BuiltinBug("Bad free"));
|
||||
|
||||
llvm::SmallString<100> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
@ -500,7 +490,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
|||
}
|
||||
}
|
||||
|
||||
void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) {
|
||||
void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const {
|
||||
const GRState *state = C.getState();
|
||||
const Expr *arg0Expr = CE->getArg(0);
|
||||
DefinedOrUnknownSVal arg0Val
|
||||
|
@ -562,7 +552,8 @@ void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) {
|
|||
C.addTransition(MallocMemAux(C, CE, TotalSize, zeroVal, state));
|
||||
}
|
||||
|
||||
void MallocChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper)
|
||||
void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
||||
CheckerContext &C) const
|
||||
{
|
||||
if (!SymReaper.hasDeadSymbols())
|
||||
return;
|
||||
|
@ -576,8 +567,8 @@ void MallocChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper)
|
|||
if (I->second.isAllocated()) {
|
||||
if (ExplodedNode *N = C.generateNode()) {
|
||||
if (!BT_Leak)
|
||||
BT_Leak = new BuiltinBug("Memory leak",
|
||||
"Allocated memory never released. Potential memory leak.");
|
||||
BT_Leak.reset(new BuiltinBug("Memory leak",
|
||||
"Allocated memory never released. Potential memory leak."));
|
||||
// FIXME: where it is allocated.
|
||||
BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
|
||||
C.EmitReport(R);
|
||||
|
@ -591,8 +582,8 @@ void MallocChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper)
|
|||
C.generateNode(state->set<RegionState>(RS));
|
||||
}
|
||||
|
||||
void MallocChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag,
|
||||
ExprEngine &Eng) {
|
||||
void MallocChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
|
||||
ExprEngine &Eng) const {
|
||||
const GRState *state = B.getState();
|
||||
RegionStateTy M = state->get<RegionState>();
|
||||
|
||||
|
@ -602,8 +593,8 @@ void MallocChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag,
|
|||
ExplodedNode *N = B.generateNode(state);
|
||||
if (N) {
|
||||
if (!BT_Leak)
|
||||
BT_Leak = new BuiltinBug("Memory leak",
|
||||
"Allocated memory never released. Potential memory leak.");
|
||||
BT_Leak.reset(new BuiltinBug("Memory leak",
|
||||
"Allocated memory never released. Potential memory leak."));
|
||||
BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
|
||||
Eng.getBugReporter().EmitReport(R);
|
||||
}
|
||||
|
@ -611,7 +602,7 @@ void MallocChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag,
|
|||
}
|
||||
}
|
||||
|
||||
void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) {
|
||||
void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
|
||||
const Expr *retExpr = S->getRetValue();
|
||||
if (!retExpr)
|
||||
return;
|
||||
|
@ -634,8 +625,7 @@ void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) {
|
|||
}
|
||||
|
||||
const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond,
|
||||
bool Assumption,
|
||||
bool * /* respondsToCallback */) {
|
||||
bool Assumption) const {
|
||||
// If a symblic region is assumed to NULL, set its state to AllocateFailed.
|
||||
// FIXME: should also check symbols assumed to non-null.
|
||||
|
||||
|
@ -650,16 +640,15 @@ const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond,
|
|||
}
|
||||
|
||||
// Check if the location is a freed symbolic region.
|
||||
void MallocChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l,
|
||||
bool isLoad) {
|
||||
void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const {
|
||||
SymbolRef Sym = l.getLocSymbolInBase();
|
||||
if (Sym) {
|
||||
const RefState *RS = C.getState()->get<RegionState>(Sym);
|
||||
if (RS && RS->isReleased()) {
|
||||
if (ExplodedNode *N = C.generateNode()) {
|
||||
if (!BT_UseFree)
|
||||
BT_UseFree = new BuiltinBug("Use dynamically allocated memory after"
|
||||
" it is freed.");
|
||||
BT_UseFree.reset(new BuiltinBug("Use dynamically allocated memory "
|
||||
"after it is freed."));
|
||||
|
||||
BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(),
|
||||
N);
|
||||
|
@ -669,10 +658,7 @@ void MallocChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l,
|
|||
}
|
||||
}
|
||||
|
||||
void MallocChecker::PreVisitBind(CheckerContext &C,
|
||||
const Stmt *StoreE,
|
||||
SVal location,
|
||||
SVal val) {
|
||||
void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const {
|
||||
// The PreVisitBind implements the same algorithm as already used by the
|
||||
// Objective C ownership checker: if the pointer escaped from this scope by
|
||||
// assignment, let it go. However, assigning to fields of a stack-storage
|
||||
|
@ -721,7 +707,7 @@ void MallocChecker::PreVisitBind(CheckerContext &C,
|
|||
// We no longer own this pointer.
|
||||
notNullState =
|
||||
notNullState->set<RegionState>(Sym,
|
||||
RefState::getRelinquished(StoreE));
|
||||
RefState::getRelinquished(C.getStmt()));
|
||||
}
|
||||
while (false);
|
||||
}
|
||||
|
@ -729,3 +715,7 @@ void MallocChecker::PreVisitBind(CheckerContext &C,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ento::registerMallocChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<MallocChecker>();
|
||||
}
|
||||
|
|
|
@ -205,6 +205,40 @@ void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
|
|||
expandGraphWithCheckers(C, Dst, Src);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CheckBindContext {
|
||||
typedef std::vector<CheckerManager::CheckBindFunc> CheckersTy;
|
||||
const CheckersTy &Checkers;
|
||||
SVal Loc;
|
||||
SVal Val;
|
||||
const Stmt *S;
|
||||
ExprEngine &Eng;
|
||||
|
||||
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
|
||||
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
|
||||
|
||||
CheckBindContext(const CheckersTy &checkers,
|
||||
SVal loc, SVal val, const Stmt *s, ExprEngine &eng)
|
||||
: Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng) { }
|
||||
|
||||
void runChecker(CheckerManager::CheckBindFunc checkFn,
|
||||
ExplodedNodeSet &Dst, ExplodedNode *Pred) {
|
||||
CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
|
||||
ProgramPoint::PreStmtKind, 0, S);
|
||||
checkFn(Loc, Val, C);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// \brief Run checkers for binding of a value to a location.
|
||||
void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
|
||||
const ExplodedNodeSet &Src,
|
||||
SVal location, SVal val,
|
||||
const Stmt *S, ExprEngine &Eng) {
|
||||
CheckBindContext C(BindCheckers, location, val, S, Eng);
|
||||
expandGraphWithCheckers(C, Dst, Src);
|
||||
}
|
||||
|
||||
void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
|
||||
BugReporter &BR,
|
||||
ExprEngine &Eng) {
|
||||
|
@ -287,6 +321,20 @@ CheckerManager::runCheckersForRegionChanges(const GRState *state,
|
|||
return state;
|
||||
}
|
||||
|
||||
/// \brief Run checkers for handling assumptions on symbolic values.
|
||||
const GRState *
|
||||
CheckerManager::runCheckersForEvalAssume(const GRState *state,
|
||||
SVal Cond, bool Assumption) {
|
||||
for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
|
||||
// If any checker declares the state infeasible (or if it starts that way),
|
||||
// bail out.
|
||||
if (!state)
|
||||
return NULL;
|
||||
state = EvalAssumeCheckers[i](state, Cond, Assumption);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/// \brief Run checkers for evaluating a call.
|
||||
/// Only one checker will evaluate the call.
|
||||
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
|
||||
|
@ -371,6 +419,10 @@ void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
|
|||
LocationCheckers.push_back(checkfn);
|
||||
}
|
||||
|
||||
void CheckerManager::_registerForBind(CheckBindFunc checkfn) {
|
||||
BindCheckers.push_back(checkfn);
|
||||
}
|
||||
|
||||
void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
|
||||
EndAnalysisCheckers.push_back(checkfn);
|
||||
}
|
||||
|
@ -393,6 +445,10 @@ void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn,
|
|||
RegionChangesCheckers.push_back(info);
|
||||
}
|
||||
|
||||
void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) {
|
||||
EvalAssumeCheckers.push_back(checkfn);
|
||||
}
|
||||
|
||||
void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
|
||||
EvalCallCheckers.push_back(checkfn);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-experimental-checks -analyzer-checker=core.experimental.UnreachableCode -verify -analyzer-constraints=basic %s
|
||||
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-experimental-checks -analyzer-checker=core.experimental.UnreachableCode -verify -analyzer-constraints=range %s
|
||||
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-checker=core.experimental.UnreachableCode,core.experimental.Malloc -verify -analyzer-constraints=basic %s
|
||||
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-checker=core.experimental.UnreachableCode,core.experimental.Malloc -verify -analyzer-constraints=range %s
|
||||
|
||||
// These are used to trigger warnings.
|
||||
typedef typeof(sizeof(int)) size_t;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-experimental-checks -fblocks -verify %s
|
||||
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-checker=core.experimental.Malloc -fblocks -verify %s
|
||||
void free(void *);
|
||||
|
||||
void t1 () {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental.UnreachableCode,core.experimental.CastSize -analyzer-check-objc-mem -analyzer-experimental-checks -analyzer-store=region -verify %s
|
||||
// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental.UnreachableCode,core.experimental.CastSize,core.experimental.Malloc -analyzer-check-objc-mem -analyzer-experimental-checks -analyzer-store=region -verify %s
|
||||
typedef __typeof(sizeof(int)) size_t;
|
||||
void *malloc(size_t);
|
||||
void free(void *);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// NOTE: Use '-fobjc-gc' to test the analysis being run twice, and multiple reports are not issued.
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=basic -fobjc-gc -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-disable-checker=core.experimental.Malloc -analyzer-check-objc-mem -analyzer-store=basic -fobjc-gc -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-disable-checker=core.experimental.Malloc -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=basic -fobjc-gc -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-disable-checker=core.experimental.Malloc -analyzer-check-objc-mem -analyzer-store=basic -fobjc-gc -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-disable-checker=core.experimental.Malloc -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify -fblocks -Wno-unreachable-code %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core.experimental,cocoa.AtSync -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify -fblocks -Wno-unreachable-code %s
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ void free(void *);
|
|||
|
||||
void field() {
|
||||
struct vec { size_t len; int data[0]; };
|
||||
struct vec *a = malloc(sizeof(struct vec) + 10);
|
||||
// FIXME: Not warn for this.
|
||||
struct vec *a = malloc(sizeof(struct vec) + 10); // expected-warning {{Cast a region whose size is not a multiple of the destination type size}}
|
||||
a->len = 10;
|
||||
a->data[1] = 5; // no-warning
|
||||
free(a);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue