diff options
author | Roman Divacky <rdivacky@FreeBSD.org> | 2009-11-18 14:59:57 +0000 |
---|---|---|
committer | Roman Divacky <rdivacky@FreeBSD.org> | 2009-11-18 14:59:57 +0000 |
commit | b3d5a323a5ca92ea73443499cee2f15db1ff0fb3 (patch) | |
tree | 60a1694bec5a44d15456acc880cb2f91619f66aa /lib/Analysis | |
parent | 8f57cb0305232cb53fff00ef151ca716766f3437 (diff) |
Notes
Diffstat (limited to 'lib/Analysis')
43 files changed, 2164 insertions, 944 deletions
diff --git a/lib/Analysis/AnalysisManager.cpp b/lib/Analysis/AnalysisManager.cpp deleted file mode 100644 index c2733faa683cc..0000000000000 --- a/lib/Analysis/AnalysisManager.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//== AnalysisManager.cpp - Path sensitive analysis data manager ----*- C++ -*-// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the AnalysisManager class. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/AnalysisManager.h" -#include "clang/Basic/SourceManager.h" - -using namespace clang; - -void AnalysisManager::DisplayFunction(Decl *D) { - - if (DisplayedFunction) - return; - - DisplayedFunction = true; - - // FIXME: Is getCodeDecl() always a named decl? - if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { - const NamedDecl *ND = cast<NamedDecl>(D); - SourceManager &SM = getASTContext().getSourceManager(); - (llvm::errs() << "ANALYZE: " - << SM.getPresumedLoc(ND->getLocation()).getFilename() - << ' ' << ND->getNameAsString() << '\n').flush(); - } -} - diff --git a/lib/Analysis/ArrayBoundChecker.cpp b/lib/Analysis/ArrayBoundChecker.cpp new file mode 100644 index 0000000000000..549a22bec1727 --- /dev/null +++ b/lib/Analysis/ArrayBoundChecker.cpp @@ -0,0 +1,86 @@ +//== ArrayBoundChecker.cpp ------------------------------*- 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 ArrayBoundChecker, which is a path-sensitive check +// which looks for an out-of-bound array element access. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN ArrayBoundChecker : + public CheckerVisitor<ArrayBoundChecker> { + BuiltinBug *BT; +public: + ArrayBoundChecker() : BT(0) {} + static void *getTag(); + void VisitLocation(CheckerContext &C, const Stmt *S, SVal l); +}; +} + +void clang::RegisterArrayBoundChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ArrayBoundChecker()); +} + +void *ArrayBoundChecker::getTag() { + static int x = 0; return &x; +} + +void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return; + + R = R->StripCasts(); + + const ElementRegion *ER = dyn_cast<ElementRegion>(R); + if (!ER) + return; + + // Get the index of the accessed element. + DefinedOrUnknownSVal &Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + + const GRState *state = C.getState(); + + // Get the size of the array. + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion()); + + const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.GenerateNode(S, StOutBound, true); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Out-of-bound array access", + "Access out-of-bound array element (buffer overflow)"); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + + // Generate a report for this bug. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + + C.EmitReport(report); + } +} diff --git a/lib/Analysis/AttrNonNullChecker.cpp b/lib/Analysis/AttrNonNullChecker.cpp index 1cf5d0c4af2d4..01e1a1fcf69bb 100644 --- a/lib/Analysis/AttrNonNullChecker.cpp +++ b/lib/Analysis/AttrNonNullChecker.cpp @@ -12,14 +12,28 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/Checkers/AttrNonNullChecker.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/PathSensitive/BugReporter.h" +#include "GRExprEngineInternalChecks.h" using namespace clang; -void *AttrNonNullChecker::getTag() { - static int x = 0; - return &x; +namespace { +class VISIBILITY_HIDDEN AttrNonNullChecker + : public CheckerVisitor<AttrNonNullChecker> { + BugType *BT; +public: + AttrNonNullChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +void clang::RegisterAttrNonNullChecker(GRExprEngine &Eng) { + Eng.registerCheck(new AttrNonNullChecker()); } void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, diff --git a/lib/Analysis/BadCallChecker.cpp b/lib/Analysis/BadCallChecker.cpp index 33bb5158d2b9c..7a7ea188ba599 100644 --- a/lib/Analysis/BadCallChecker.cpp +++ b/lib/Analysis/BadCallChecker.cpp @@ -12,14 +12,27 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/Checkers/BadCallChecker.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/PathSensitive/BugReporter.h" +#include "GRExprEngineInternalChecks.h" using namespace clang; -void *BadCallChecker::getTag() { - static int x = 0; - return &x; +namespace { +class VISIBILITY_HIDDEN BadCallChecker : public CheckerVisitor<BadCallChecker> { + BuiltinBug *BT; +public: + BadCallChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +void clang::RegisterBadCallChecker(GRExprEngine &Eng) { + Eng.registerCheck(new BadCallChecker()); } void BadCallChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { @@ -29,11 +42,11 @@ void BadCallChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { if (L.isUndef() || isa<loc::ConcreteInt>(L)) { if (ExplodedNode *N = C.GenerateNode(CE, true)) { if (!BT) - BT = new BuiltinBug(0, "Invalid function call", + BT = new BuiltinBug("Invalid function call", "Called function pointer is a null or undefined pointer value"); EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); + new EnhancedBugReport(*BT, BT->getDescription(), N); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetCalleeExpr(N)); diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp index 4781d5ec243e1..c2ecfa1f417f0 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -384,7 +384,7 @@ bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ if (!LV) return false; - const TypedRegion* R = dyn_cast<TypedRegion>(LV->getBaseRegion()); + const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); if (!R) return false; diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp index 888af9bd57a43..800a76fb0aee0 100644 --- a/lib/Analysis/BasicStore.cpp +++ b/lib/Analysis/BasicStore.cpp @@ -268,20 +268,6 @@ SValuator::CastResult BasicStoreManager::Retrieve(const GRState *state, case loc::MemRegionKind: { const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Just support void**, void***, intptr_t*, intptr_t**, etc., for now. - // This is needed to handle OSCompareAndSwapPtr() and friends. - ASTContext &Ctx = StateMgr.getContext(); - QualType T = ER->getLocationType(Ctx); - - if (!isHigherOrderRawPtr(T, Ctx)) - return SValuator::CastResult(state, UnknownVal()); - - // FIXME: Should check for element 0. - // Otherwise, strip the element region. - R = ER->getSuperRegion(); - } - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) return SValuator::CastResult(state, UnknownVal()); @@ -291,7 +277,8 @@ SValuator::CastResult BasicStoreManager::Retrieve(const GRState *state, if (!Val) break; - return CastRetrievedVal(*Val, state, cast<TypedRegion>(R), T); + return SValuator::CastResult(state, + CastRetrievedVal(*Val, cast<TypedRegion>(R), T)); } case loc::ConcreteIntKind: @@ -624,7 +611,7 @@ const GRState *BasicStoreManager::InvalidateRegion(const GRState *state, const Expr *E, unsigned Count, InvalidatedSymbols *IS) { - R = R->getBaseRegion(); + R = R->StripCasts(); if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) return state; diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp index 03614e83398f8..55e5f174cb9f4 100644 --- a/lib/Analysis/CFRefCount.cpp +++ b/lib/Analysis/CFRefCount.cpp @@ -308,7 +308,234 @@ public: } }; +//===----------------------------------------------------------------------===// +// Reference-counting logic (typestate + counts). +//===----------------------------------------------------------------------===// +class VISIBILITY_HIDDEN RefVal { +public: + enum Kind { + Owned = 0, // Owning reference. + NotOwned, // Reference is not owned by still valid (not freed). + Released, // Object has been released. + ReturnedOwned, // Returned object passes ownership to caller. + ReturnedNotOwned, // Return object does not pass ownership to caller. + ERROR_START, + ErrorDeallocNotOwned, // -dealloc called on non-owned object. + ErrorDeallocGC, // Calling -dealloc with GC enabled. + ErrorUseAfterRelease, // Object used after released. + ErrorReleaseNotOwned, // Release of an object that was not owned. + ERROR_LEAK_START, + ErrorLeak, // A memory leak due to excessive reference counts. + ErrorLeakReturned, // A memory leak due to the returning method not having + // the correct naming conventions. + ErrorGCLeakReturned, + ErrorOverAutorelease, + ErrorReturnedNotOwned + }; + +private: + Kind kind; + RetEffect::ObjKind okind; + unsigned Cnt; + unsigned ACnt; + QualType T; + + RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) + : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} + + RefVal(Kind k, unsigned cnt = 0) + : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {} + +public: + Kind getKind() const { return kind; } + + RetEffect::ObjKind getObjKind() const { return okind; } + + unsigned getCount() const { return Cnt; } + unsigned getAutoreleaseCount() const { return ACnt; } + unsigned getCombinedCounts() const { return Cnt + ACnt; } + void clearCounts() { Cnt = 0; ACnt = 0; } + void setCount(unsigned i) { Cnt = i; } + void setAutoreleaseCount(unsigned i) { ACnt = i; } + + QualType getType() const { return T; } + + // Useful predicates. + + static bool isError(Kind k) { return k >= ERROR_START; } + + static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; } + + bool isOwned() const { + return getKind() == Owned; + } + + bool isNotOwned() const { + return getKind() == NotOwned; + } + + bool isReturnedOwned() const { + return getKind() == ReturnedOwned; + } + + bool isReturnedNotOwned() const { + return getKind() == ReturnedNotOwned; + } + + bool isNonLeakError() const { + Kind k = getKind(); + return isError(k) && !isLeak(k); + } + + static RefVal makeOwned(RetEffect::ObjKind o, QualType t, + unsigned Count = 1) { + return RefVal(Owned, o, Count, 0, t); + } + + static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, + unsigned Count = 0) { + return RefVal(NotOwned, o, Count, 0, t); + } + + // Comparison, profiling, and pretty-printing. + + bool operator==(const RefVal& X) const { + return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; + } + + RefVal operator-(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() - i, + getAutoreleaseCount(), getType()); + } + + RefVal operator+(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() + i, + getAutoreleaseCount(), getType()); + } + + RefVal operator^(Kind k) const { + return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), + getType()); + } + + RefVal autorelease() const { + return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, + getType()); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned) kind); + ID.AddInteger(Cnt); + ID.AddInteger(ACnt); + ID.Add(T); + } + + void print(llvm::raw_ostream& Out) const; +}; + +void RefVal::print(llvm::raw_ostream& Out) const { + if (!T.isNull()) + Out << "Tracked Type:" << T.getAsString() << '\n'; + + switch (getKind()) { + default: assert(false); + case Owned: { + Out << "Owned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case NotOwned: { + Out << "NotOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case ReturnedOwned: { + Out << "ReturnedOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case ReturnedNotOwned: { + Out << "ReturnedNotOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case Released: + Out << "Released"; + break; + + case ErrorDeallocGC: + Out << "-dealloc (GC)"; + break; + + case ErrorDeallocNotOwned: + Out << "-dealloc (not-owned)"; + break; + + case ErrorLeak: + Out << "Leaked"; + break; + + case ErrorLeakReturned: + Out << "Leaked (Bad naming)"; + break; + + case ErrorGCLeakReturned: + Out << "Leaked (GC-ed at return)"; + break; + + case ErrorUseAfterRelease: + Out << "Use-After-Release [ERROR]"; + break; + + case ErrorReleaseNotOwned: + Out << "Release of Not-Owned [ERROR]"; + break; + + case RefVal::ErrorOverAutorelease: + Out << "Over autoreleased"; + break; + + case RefVal::ErrorReturnedNotOwned: + Out << "Non-owned object returned instead of owned"; + break; + } + + if (ACnt) { + Out << " [ARC +" << ACnt << ']'; + } +} +} //end anonymous namespace + +//===----------------------------------------------------------------------===// +// RefBindings - State used to track object reference counts. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings; + +namespace clang { + template<> + struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> { + static void* GDMIndex() { + static int RefBIndex = 0; + return &RefBIndex; + } + }; +} + +//===----------------------------------------------------------------------===// +// Summaries +//===----------------------------------------------------------------------===// + +namespace { class VISIBILITY_HIDDEN RetainSummary { /// Args - an ordered vector of (index, ArgEffect) pairs, where index /// specifies the argument (starting from 0). This can be sparsely @@ -757,7 +984,11 @@ public: RetainSummary* getSummary(FunctionDecl* FD); - RetainSummary* getInstanceMethodSummary(ObjCMessageExpr* ME, + RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, + const GRState *state, + const LocationContext *LC); + + RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME, const ObjCInterfaceDecl* ID) { return getInstanceMethodSummary(ME->getSelector(), ME->getClassName(), ID, ME->getMethodDecl(), ME->getType()); @@ -773,7 +1004,7 @@ public: const ObjCMethodDecl *MD, QualType RetTy); - RetainSummary *getClassMethodSummary(ObjCMessageExpr *ME) { + RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) { return getClassMethodSummary(ME->getSelector(), ME->getClassName(), ME->getClassInfo().first, ME->getMethodDecl(), ME->getType()); @@ -1306,7 +1537,7 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, E = MD->param_end(); I != E; ++I, ++i) if (ParmVarDecl *PD = *I) { QualType Ty = Ctx.getCanonicalType(PD->getType()); - if (Ty.getUnqualifiedType() == Ctx.VoidPtrTy) + if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) ScratchArgs = AF.Add(ScratchArgs, i, StopTracking); } } @@ -1350,6 +1581,61 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, } RetainSummary* +RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, + const GRState *state, + const LocationContext *LC) { + + // We need the type-information of the tracked receiver object + // Retrieve it from the state. + const Expr *Receiver = ME->getReceiver(); + const ObjCInterfaceDecl* ID = 0; + + // FIXME: Is this really working as expected? There are cases where + // we just use the 'ID' from the message expression. + SVal receiverV = state->getSValAsScalarOrLoc(Receiver); + + // FIXME: Eventually replace the use of state->get<RefBindings> with + // a generic API for reasoning about the Objective-C types of symbolic + // objects. + if (SymbolRef Sym = receiverV.getAsLocSymbol()) + if (const RefVal *T = state->get<RefBindings>(Sym)) + if (const ObjCObjectPointerType* PT = + T->getType()->getAs<ObjCObjectPointerType>()) + ID = PT->getInterfaceDecl(); + + // FIXME: this is a hack. This may or may not be the actual method + // that is called. + if (!ID) { + if (const ObjCObjectPointerType *PT = + Receiver->getType()->getAs<ObjCObjectPointerType>()) + ID = PT->getInterfaceDecl(); + } + + // FIXME: The receiver could be a reference to a class, meaning that + // we should use the class method. + RetainSummary *Summ = getInstanceMethodSummary(ME, ID); + + // Special-case: are we sending a mesage to "self"? + // This is a hack. When we have full-IP this should be removed. + if (isa<ObjCMethodDecl>(LC->getDecl())) { + if (const loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&receiverV)) { + // Get the region associated with 'self'. + if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) { + SVal SelfVal = state->getSVal(state->getRegion(SelfDecl, LC)); + if (L->StripCasts() == SelfVal.getAsRegion()) { + // Update the summary to make the default argument effect + // 'StopTracking'. + Summ = copySummary(Summ); + Summ->setDefaultArgEffect(StopTracking); + } + } + } + } + + return Summ ? Summ : getDefaultSummary(); +} + +RetainSummary* RetainSummaryManager::getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, const ObjCInterfaceDecl* ID, @@ -1569,230 +1855,6 @@ void RetainSummaryManager::InitializeMethodSummaries() { } //===----------------------------------------------------------------------===// -// Reference-counting logic (typestate + counts). -//===----------------------------------------------------------------------===// - -namespace { - -class VISIBILITY_HIDDEN RefVal { -public: - enum Kind { - Owned = 0, // Owning reference. - NotOwned, // Reference is not owned by still valid (not freed). - Released, // Object has been released. - ReturnedOwned, // Returned object passes ownership to caller. - ReturnedNotOwned, // Return object does not pass ownership to caller. - ERROR_START, - ErrorDeallocNotOwned, // -dealloc called on non-owned object. - ErrorDeallocGC, // Calling -dealloc with GC enabled. - ErrorUseAfterRelease, // Object used after released. - ErrorReleaseNotOwned, // Release of an object that was not owned. - ERROR_LEAK_START, - ErrorLeak, // A memory leak due to excessive reference counts. - ErrorLeakReturned, // A memory leak due to the returning method not having - // the correct naming conventions. - ErrorGCLeakReturned, - ErrorOverAutorelease, - ErrorReturnedNotOwned - }; - -private: - Kind kind; - RetEffect::ObjKind okind; - unsigned Cnt; - unsigned ACnt; - QualType T; - - RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) - : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} - - RefVal(Kind k, unsigned cnt = 0) - : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {} - -public: - Kind getKind() const { return kind; } - - RetEffect::ObjKind getObjKind() const { return okind; } - - unsigned getCount() const { return Cnt; } - unsigned getAutoreleaseCount() const { return ACnt; } - unsigned getCombinedCounts() const { return Cnt + ACnt; } - void clearCounts() { Cnt = 0; ACnt = 0; } - void setCount(unsigned i) { Cnt = i; } - void setAutoreleaseCount(unsigned i) { ACnt = i; } - - QualType getType() const { return T; } - - // Useful predicates. - - static bool isError(Kind k) { return k >= ERROR_START; } - - static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; } - - bool isOwned() const { - return getKind() == Owned; - } - - bool isNotOwned() const { - return getKind() == NotOwned; - } - - bool isReturnedOwned() const { - return getKind() == ReturnedOwned; - } - - bool isReturnedNotOwned() const { - return getKind() == ReturnedNotOwned; - } - - bool isNonLeakError() const { - Kind k = getKind(); - return isError(k) && !isLeak(k); - } - - static RefVal makeOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 1) { - return RefVal(Owned, o, Count, 0, t); - } - - static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 0) { - return RefVal(NotOwned, o, Count, 0, t); - } - - // Comparison, profiling, and pretty-printing. - - bool operator==(const RefVal& X) const { - return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; - } - - RefVal operator-(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() - i, - getAutoreleaseCount(), getType()); - } - - RefVal operator+(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() + i, - getAutoreleaseCount(), getType()); - } - - RefVal operator^(Kind k) const { - return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), - getType()); - } - - RefVal autorelease() const { - return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, - getType()); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned) kind); - ID.AddInteger(Cnt); - ID.AddInteger(ACnt); - ID.Add(T); - } - - void print(llvm::raw_ostream& Out) const; -}; - -void RefVal::print(llvm::raw_ostream& Out) const { - if (!T.isNull()) - Out << "Tracked Type:" << T.getAsString() << '\n'; - - switch (getKind()) { - default: assert(false); - case Owned: { - Out << "Owned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case NotOwned: { - Out << "NotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedOwned: { - Out << "ReturnedOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedNotOwned: { - Out << "ReturnedNotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case Released: - Out << "Released"; - break; - - case ErrorDeallocGC: - Out << "-dealloc (GC)"; - break; - - case ErrorDeallocNotOwned: - Out << "-dealloc (not-owned)"; - break; - - case ErrorLeak: - Out << "Leaked"; - break; - - case ErrorLeakReturned: - Out << "Leaked (Bad naming)"; - break; - - case ErrorGCLeakReturned: - Out << "Leaked (GC-ed at return)"; - break; - - case ErrorUseAfterRelease: - Out << "Use-After-Release [ERROR]"; - break; - - case ErrorReleaseNotOwned: - Out << "Release of Not-Owned [ERROR]"; - break; - - case RefVal::ErrorOverAutorelease: - Out << "Over autoreleased"; - break; - - case RefVal::ErrorReturnedNotOwned: - Out << "Non-owned object returned instead of owned"; - break; - } - - if (ACnt) { - Out << " [ARC +" << ACnt << ']'; - } -} - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// RefBindings - State used to track object reference counts. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings; -static int RefBIndex = 0; - -namespace clang { - template<> - struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> { - static inline void* GDMIndex() { return &RefBIndex; } - }; -} - -//===----------------------------------------------------------------------===// // AutoreleaseBindings - State used to track objects in autorelease pools. //===----------------------------------------------------------------------===// @@ -3004,69 +3066,14 @@ void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, ExplodedNode* Pred) { - RetainSummary* Summ = 0; - - if (Expr* Receiver = ME->getReceiver()) { - // We need the type-information of the tracked receiver object - // Retrieve it from the state. - const ObjCInterfaceDecl* ID = 0; - - // FIXME: Wouldn't it be great if this code could be reduced? It's just - // a chain of lookups. - // FIXME: Is this really working as expected? There are cases where - // we just use the 'ID' from the message expression. - const GRState* St = Builder.GetState(Pred); - SVal V = St->getSValAsScalarOrLoc(Receiver); - - SymbolRef Sym = V.getAsLocSymbol(); - - if (Sym) { - if (const RefVal* T = St->get<RefBindings>(Sym)) { - if (const ObjCObjectPointerType* PT = - T->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - } - } - - // FIXME: this is a hack. This may or may not be the actual method - // that is called. - if (!ID) { - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - } - - // FIXME: The receiver could be a reference to a class, meaning that - // we should use the class method. - Summ = Summaries.getInstanceMethodSummary(ME, ID); - - // Special-case: are we sending a mesage to "self"? - // This is a hack. When we have full-IP this should be removed. - if (isa<ObjCMethodDecl>(Pred->getLocationContext()->getDecl())) { - if (Expr* Receiver = ME->getReceiver()) { - SVal X = St->getSValAsScalarOrLoc(Receiver); - if (loc::MemRegionVal* L = dyn_cast<loc::MemRegionVal>(&X)) { - // Get the region associated with 'self'. - const LocationContext *LC = Pred->getLocationContext(); - if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) { - SVal SelfVal = St->getSVal(St->getRegion(SelfDecl, LC)); - if (L->getBaseRegion() == SelfVal.getAsRegion()) { - // Update the summary to make the default argument effect - // 'StopTracking'. - Summ = Summaries.copySummary(Summ); - Summ->setDefaultArgEffect(StopTracking); - } - } - } - } - } - } - else - Summ = Summaries.getClassMethodSummary(ME); - - if (!Summ) - Summ = Summaries.getDefaultSummary(); + + RetainSummary *Summ = + ME->getReceiver() + ? Summaries.getInstanceMethodSummary(ME, Builder.GetState(Pred), + Pred->getLocationContext()) + : Summaries.getClassMethodSummary(ME); + assert(Summ && "RetainSummary is null"); EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, ME->arg_begin(), ME->arg_end(), Pred); } diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index cd4697f2f3246..8e8c1e7b25ed2 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_NO_RTTI 1) add_clang_library(clangAnalysis AnalysisContext.cpp - AnalysisManager.cpp + ArrayBoundChecker.cpp AttrNonNullChecker.cpp BadCallChecker.cpp BasicConstraintManager.cpp @@ -15,27 +15,39 @@ add_clang_library(clangAnalysis CFRefCount.cpp CallGraph.cpp CallInliner.cpp + CastToStructChecker.cpp CheckDeadStores.cpp CheckObjCDealloc.cpp CheckObjCInstMethSignature.cpp CheckObjCUnusedIVars.cpp CheckSecuritySyntaxOnly.cpp + CheckSizeofPointer.cpp DereferenceChecker.cpp DivZeroChecker.cpp Environment.cpp ExplodedGraph.cpp + FixedAddressChecker.cpp GRBlockCounter.cpp GRCoreEngine.cpp GRExprEngine.cpp + GRExprEngineExperimentalChecks.cpp GRExprEngineInternalChecks.cpp GRState.cpp LiveVariables.cpp + MallocChecker.cpp + ManagerRegistry.cpp MemRegion.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp PathDiagnostic.cpp + PointerArithChecker.cpp + PointerSubChecker.cpp + PthreadLockChecker.cpp RangeConstraintManager.cpp RegionStore.cpp + ReturnPointerRangeChecker.cpp + ReturnStackAddressChecker.cpp + ReturnUndefChecker.cpp SVals.cpp SValuator.cpp SimpleConstraintManager.cpp @@ -43,10 +55,11 @@ add_clang_library(clangAnalysis Store.cpp SymbolManager.cpp UndefinedArgChecker.cpp + UndefinedArraySubscriptChecker.cpp UndefinedAssignmentChecker.cpp UninitializedValues.cpp - ValueManager.cpp VLASizeChecker.cpp + ValueManager.cpp ) add_dependencies(clangAnalysis ClangDiagnosticAnalysis) diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp index 17dc0685f82fc..06e3317691e36 100644 --- a/lib/Analysis/CallGraph.cpp +++ b/lib/Analysis/CallGraph.cpp @@ -117,7 +117,7 @@ void CallGraph::print(llvm::raw_ostream &os) { << " calls:\n"; for (CallGraphNode::iterator CI = I->second->begin(), CE = I->second->end(); CI != CE; ++CI) { - os << " " << CI->second->getName().c_str(); + os << " " << CI->second->getName(); } os << '\n'; } diff --git a/lib/Analysis/CastToStructChecker.cpp b/lib/Analysis/CastToStructChecker.cpp new file mode 100644 index 0000000000000..ccd4a3333e222 --- /dev/null +++ b/lib/Analysis/CastToStructChecker.cpp @@ -0,0 +1,77 @@ +//=== CastToStructChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines CastToStructChecker, a builtin checker that checks for +// assignment of a fixed address to a pointer. +// This check corresponds to CWE-588. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN CastToStructChecker + : public CheckerVisitor<CastToStructChecker> { + BuiltinBug *BT; +public: + CastToStructChecker() : BT(0) {} + static void *getTag(); + void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); +}; +} + +void *CastToStructChecker::getTag() { + static int x; + return &x; +} + +void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, + const CastExpr *CE) { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = C.getASTContext(); + QualType OrigTy = Ctx.getCanonicalType(E->getType()); + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + + PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); + PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); + + if (!ToPTy || !OrigPTy) + return; + + QualType OrigPointeeTy = OrigPTy->getPointeeType(); + QualType ToPointeeTy = ToPTy->getPointeeType(); + + if (!ToPointeeTy->isStructureType()) + return; + + // We allow cast from void*. + if (OrigPointeeTy->isVoidType()) + return; + + // Now the cast-to-type is struct pointer, the original type is not void*. + if (!OrigPointeeTy->isRecordType()) { + if (ExplodedNode *N = C.GenerateNode(CE)) { + if (!BT) + BT = new BuiltinBug("Cast from non-struct type to struct type", + "Casting a non-structure type to a structure type " + "and accessing a field can lead to memory access " + "errors or data corruption."); + RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); + R->addRange(CE->getSourceRange()); + C.EmitReport(R); + } + } +} + +void clang::RegisterCastToStructChecker(GRExprEngine &Eng) { + Eng.registerCheck(new CastToStructChecker()); +} diff --git a/lib/Analysis/CheckSecuritySyntaxOnly.cpp b/lib/Analysis/CheckSecuritySyntaxOnly.cpp index 9f0d059cb66e9..f1b9c2194f8be 100644 --- a/lib/Analysis/CheckSecuritySyntaxOnly.cpp +++ b/lib/Analysis/CheckSecuritySyntaxOnly.cpp @@ -23,6 +23,7 @@ namespace { class VISIBILITY_HIDDEN WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; IdentifierInfo *II_gets; + IdentifierInfo *II_getpw; enum { num_rands = 9 }; IdentifierInfo *II_rand[num_rands]; IdentifierInfo *II_random; @@ -31,7 +32,7 @@ class VISIBILITY_HIDDEN WalkAST : public StmtVisitor<WalkAST> { public: WalkAST(BugReporter &br) : BR(br), - II_gets(0), II_rand(), II_random(0), II_setid() {} + II_gets(0), II_getpw(0), II_rand(), II_random(0), II_setid() {} // Statement visitor methods. void VisitCallExpr(CallExpr *CE); @@ -47,6 +48,7 @@ public: // Checker-specific methods. void CheckLoopConditionForFloat(const ForStmt *FS); void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); void CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD); void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); void CheckUncheckedReturnValue(CallExpr *CE); @@ -77,6 +79,7 @@ void WalkAST::VisitChildren(Stmt *S) { void WalkAST::VisitCallExpr(CallExpr *CE) { if (const FunctionDecl *FD = CE->getDirectCallee()) { CheckCall_gets(CE, FD); + CheckCall_getpw(CE, FD); CheckCall_rand(CE, FD); CheckCall_random(CE, FD); } @@ -215,22 +218,23 @@ void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { // Check: Any use of 'gets' is insecure. // Originally: <rdar://problem/6335715> // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) +// CWE-242: Use of Inherently Dangerous Function //===----------------------------------------------------------------------===// void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { if (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) return; - const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FD->getType()); - if (!FTP) + const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType()); + if (!FPT) return; // Verify that the function takes a single argument. - if (FTP->getNumArgs() != 1) + if (FPT->getNumArgs() != 1) return; // Is the argument a 'char*'? - const PointerType *PT = dyn_cast<PointerType>(FTP->getArgType(0)); + const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); if (!PT) return; @@ -247,6 +251,44 @@ void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { } //===----------------------------------------------------------------------===// +// Check: Any use of 'getpwd' is insecure. +// CWE-477: Use of Obsolete Functions +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) + return; + + const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType()); + if (!FPT) + return; + + // Verify that the function takes two arguments. + if (FPT->getNumArgs() != 2) + return; + + // Verify the first argument type is integer. + if (!FPT->getArgType(0)->isIntegerType()) + return; + + // Verify the second argument type is char*. + const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(1)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", + "Security", + "The getpw() function is dangerous as it may overflow the " + "provided buffer. It is obsoleted by getpwuid().", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// // Check: Linear congruent random number generators should not be used // Originally: <rdar://problem/63371000> // CWE-338: Use of cryptographically weak prng diff --git a/lib/Analysis/CheckSizeofPointer.cpp b/lib/Analysis/CheckSizeofPointer.cpp new file mode 100644 index 0000000000000..174beefbca451 --- /dev/null +++ b/lib/Analysis/CheckSizeofPointer.cpp @@ -0,0 +1,72 @@ +//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- 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 a check for unintended use of sizeof() on pointer +// expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/LocalCheckers.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + +public: + WalkAST(BugReporter &br) : BR(br) {} + void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitChildren(Stmt *S); +}; +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +// CWE-467: Use of sizeof() on a Pointer Type +void WalkAST::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { + if (!E->isSizeOf()) + return; + + // If an explicit type is used in the code, usually the coder knows what he is + // doing. + if (E->isArgumentType()) + return; + + QualType T = E->getTypeOfArgument(); + if (T->isPointerType()) { + + // Many false positives have the form 'sizeof *p'. This is reasonable + // because people know what they are doing when they intentionally + // dereference the pointer. + Expr *ArgEx = E->getArgumentExpr(); + if (!isa<DeclRefExpr>(ArgEx->IgnoreParens())) + return; + + SourceRange R = ArgEx->getSourceRange(); + BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", + "Logic", + "The code calls sizeof() on a pointer type. " + "This can produce an unexpected result.", + E->getLocStart(), &R, 1); + } +} + +void clang::CheckSizeofPointer(const Decl *D, BugReporter &BR) { + WalkAST walker(BR); + walker.Visit(D->getBody()); +} diff --git a/lib/Analysis/DereferenceChecker.cpp b/lib/Analysis/DereferenceChecker.cpp index 33c85d5074634..c3aa8f3a2879c 100644 --- a/lib/Analysis/DereferenceChecker.cpp +++ b/lib/Analysis/DereferenceChecker.cpp @@ -13,100 +13,103 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" +#include "clang/Analysis/PathSensitive/Checker.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/BugReporter.h" +#include "GRExprEngineInternalChecks.h" using namespace clang; -void *NullDerefChecker::getTag() { - static int x = 0; - return &x; -} - -ExplodedNode *NullDerefChecker::CheckLocation(const Stmt *S, ExplodedNode *Pred, - const GRState *state, SVal V, - GRExprEngine &Eng) { - Loc *LV = dyn_cast<Loc>(&V); - - // If the value is not a location, don't touch the node. - if (!LV) - return Pred; - - const GRState *NotNullState = state->Assume(*LV, true); - const GRState *NullState = state->Assume(*LV, false); +namespace { +class VISIBILITY_HIDDEN DereferenceChecker : public Checker { + BuiltinBug *BT_null; + BuiltinBug *BT_undef; + llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes; +public: + DereferenceChecker() : BT_null(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void VisitLocation(CheckerContext &C, const Stmt *S, SVal location); - GRStmtNodeBuilder &Builder = Eng.getBuilder(); - BugReporter &BR = Eng.getBugReporter(); - - // The explicit NULL case. - if (NullState) { - // Use the GDM to mark in the state what lval was null. - const SVal *PersistentLV = Eng.getBasicVals().getPersistentSVal(*LV); - NullState = NullState->set<GRState::NullDerefTag>(PersistentLV); - - ExplodedNode *N = Builder.generateNode(S, NullState, Pred, - ProgramPoint::PostNullCheckFailedKind); - if (N) { - N->markAsSink(); - - if (!NotNullState) { // Explicit null case. - if (!BT) - BT = new BuiltinBug(NULL, "Null dereference", - "Dereference of null pointer"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); - - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - - BR.EmitReport(R); - - return 0; - } else // Implicit null case. - ImplicitNullDerefNodes.push_back(N); - } + std::pair<ExplodedNode * const*, ExplodedNode * const*> + getImplicitNodes() const { + return std::make_pair(ImplicitNullDerefNodes.data(), + ImplicitNullDerefNodes.data() + + ImplicitNullDerefNodes.size()); } - - if (!NotNullState) - return 0; +}; +} // end anonymous namespace - return Builder.generateNode(S, NotNullState, Pred, - ProgramPoint::PostLocationChecksSucceedKind); +void clang::RegisterDereferenceChecker(GRExprEngine &Eng) { + Eng.registerCheck(new DereferenceChecker()); } - -void *UndefDerefChecker::getTag() { - static int x = 0; - return &x; +std::pair<ExplodedNode * const *, ExplodedNode * const *> +clang::GetImplicitNullDereferences(GRExprEngine &Eng) { + DereferenceChecker *checker = Eng.getChecker<DereferenceChecker>(); + if (!checker) + return std::make_pair((ExplodedNode * const *) 0, + (ExplodedNode * const *) 0); + return checker->getImplicitNodes(); } -ExplodedNode *UndefDerefChecker::CheckLocation(const Stmt *S, - ExplodedNode *Pred, - const GRState *state, SVal V, - GRExprEngine &Eng) { - GRStmtNodeBuilder &Builder = Eng.getBuilder(); - BugReporter &BR = Eng.getBugReporter(); - - if (V.isUndef()) { - ExplodedNode *N = Builder.generateNode(S, state, Pred, - ProgramPoint::PostUndefLocationCheckFailedKind); +void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, + SVal l) { + // Check for dereference of an undefined value. + if (l.isUndef()) { + ExplodedNode *N = C.GenerateNode(S, true); if (N) { - N->markAsSink(); + if (!BT_undef) + BT_undef = new BuiltinBug("Dereference of undefined pointer value"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + C.EmitReport(report); + } + return; + } + + DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); + + // Check for null dereferences. + if (!isa<Loc>(location)) + return; + + const GRState *state = C.getState(); + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->Assume(location); + + // The explicit NULL case. + if (nullState) { + // Generate an error node. + ExplodedNode *N = C.GenerateNode(S, nullState, true); + if (N) { + if (!notNullState) { + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null = new BuiltinBug("Null pointer dereference", + "Dereference of null pointer"); - if (!BT) - BT = new BuiltinBug(0, "Undefined dereference", - "Dereference of undefined pointer value"); + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + + C.EmitReport(report); + return; + } - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - BR.EmitReport(R); + // Otherwise, we have the case where the location could either be + // null or not-null. Record the error node as an "implicit" null + // dereference. + ImplicitNullDerefNodes.push_back(N); } - return 0; } - - return Pred; + + // From this point forward, we know that the location is not null. + assert(notNullState); + C.addTransition(state != nullState ? C.GenerateNode(S, notNullState) : + C.getPredecessor()); } - diff --git a/lib/Analysis/DivZeroChecker.cpp b/lib/Analysis/DivZeroChecker.cpp index 9c2359f3b075c..a8630f10088ef 100644 --- a/lib/Analysis/DivZeroChecker.cpp +++ b/lib/Analysis/DivZeroChecker.cpp @@ -12,10 +12,25 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/Checkers/DivZeroChecker.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" using namespace clang; +namespace { +class VISIBILITY_HIDDEN DivZeroChecker : public CheckerVisitor<DivZeroChecker> { + BuiltinBug *BT; +public: + DivZeroChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void clang::RegisterDivZeroChecker(GRExprEngine &Eng) { + Eng.registerCheck(new DivZeroChecker()); +} + void *DivZeroChecker::getTag() { static int x; return &x; @@ -50,10 +65,10 @@ void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, if (stateZero && !stateNotZero) { if (ExplodedNode *N = C.GenerateNode(B, stateZero, true)) { if (!BT) - BT = new BuiltinBug(0, "Division by zero"); + BT = new BuiltinBug("Division by zero"); EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); + new EnhancedBugReport(*BT, BT->getDescription(), N); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, bugreporter::GetDenomExpr(N)); diff --git a/lib/Analysis/ExplodedGraph.cpp b/lib/Analysis/ExplodedGraph.cpp index 0dc81a4225a8b..3b339ffc0dfe2 100644 --- a/lib/Analysis/ExplodedGraph.cpp +++ b/lib/Analysis/ExplodedGraph.cpp @@ -273,7 +273,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, ExplodedNode* InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const { - llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::iterator I = + llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = M.find(N); return I == M.end() ? 0 : I->second; diff --git a/lib/Analysis/FixedAddressChecker.cpp b/lib/Analysis/FixedAddressChecker.cpp new file mode 100644 index 0000000000000..80096dcb70d03 --- /dev/null +++ b/lib/Analysis/FixedAddressChecker.cpp @@ -0,0 +1,70 @@ +//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines FixedAddressChecker, a builtin checker that checks for +// assignment of a fixed address to a pointer. +// This check corresponds to CWE-587. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN FixedAddressChecker + : public CheckerVisitor<FixedAddressChecker> { + BuiltinBug *BT; +public: + FixedAddressChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *FixedAddressChecker::getTag() { + static int x; + return &x; +} + +void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // Using a fixed address is not portable because that address will probably + // not be valid in all environments or platforms. + + if (B->getOpcode() != BinaryOperator::Assign) + return; + + QualType T = B->getType(); + if (!T->isPointerType()) + return; + + const GRState *state = C.getState(); + + SVal RV = state->getSVal(B->getRHS()); + + if (!RV.isConstant() || RV.isZeroConstant()) + return; + + if (ExplodedNode *N = C.GenerateNode(B)) { + if (!BT) + BT = new BuiltinBug("Use fixed address", + "Using a fixed address is not portable because that " + "address will probably not be valid in all " + "environments or platforms."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getRHS()->getSourceRange()); + C.EmitReport(R); + } +} + +void clang::RegisterFixedAddressChecker(GRExprEngine &Eng) { + Eng.registerCheck(new FixedAddressChecker()); +} diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp index 87472472fdeeb..b99ba4f257efe 100644 --- a/lib/Analysis/GRCoreEngine.cpp +++ b/lib/Analysis/GRCoreEngine.cpp @@ -418,51 +418,38 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { Eng.WList->Enqueue(Succ, B, Idx+1); } -static inline PostStmt GetPostLoc(const Stmt* S, ProgramPoint::Kind K, - const LocationContext *L, const void *tag) { +static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, + const LocationContext *LC, const void *tag){ switch (K) { default: - assert(false && "Invalid PostXXXKind."); - + assert(false && "Unhandled ProgramPoint kind"); + case ProgramPoint::PreStmtKind: + return PreStmt(S, LC, tag); case ProgramPoint::PostStmtKind: - return PostStmt(S, L, tag); - + return PostStmt(S, LC, tag); + case ProgramPoint::PreLoadKind: + return PreLoad(S, LC, tag); case ProgramPoint::PostLoadKind: - return PostLoad(S, L, tag); - - case ProgramPoint::PostUndefLocationCheckFailedKind: - return PostUndefLocationCheckFailed(S, L, tag); - - case ProgramPoint::PostLocationChecksSucceedKind: - return PostLocationChecksSucceed(S, L, tag); - - case ProgramPoint::PostOutOfBoundsCheckFailedKind: - return PostOutOfBoundsCheckFailed(S, L, tag); - - case ProgramPoint::PostNullCheckFailedKind: - return PostNullCheckFailed(S, L, tag); - + return PostLoad(S, LC, tag); + case ProgramPoint::PreStoreKind: + return PreStore(S, LC, tag); case ProgramPoint::PostStoreKind: - return PostStore(S, L, tag); - + return PostStore(S, LC, tag); case ProgramPoint::PostLValueKind: - return PostLValue(S, L, tag); - + return PostLValue(S, LC, tag); case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, L, tag); + return PostPurgeDeadSymbols(S, LC, tag); } } ExplodedNode* -GRStmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* State, +GRStmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, ExplodedNode* Pred, ProgramPoint::Kind K, const void *tag) { - return K == ProgramPoint::PreStmtKind - ? generateNodeInternal(PreStmt(S, Pred->getLocationContext(),tag), - State, Pred) - : generateNodeInternal(GetPostLoc(S, K, Pred->getLocationContext(), tag), - State, Pred); + + const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); + return generateNodeInternal(L, state, Pred); } ExplodedNode* diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 212fea3a6bcce..26331776141ff 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -293,9 +293,7 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { Builder = &builder; EntryNode = builder.getLastNode(); - // FIXME: Consolidate. CurrentStmt = S; - StateMgr.CurrentStmt = S; // Set up our simple checks. if (BatchAuditor) @@ -320,9 +318,32 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); Builder->PurgingDeadSymbols = true; - getTF().EvalDeadSymbols(Tmp, *this, *Builder, EntryNode, S, + // FIXME: This should soon be removed. + ExplodedNodeSet Tmp2; + getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, S, CleanedState, SymReaper); + if (Checkers.empty()) + Tmp = Tmp2; + else { + ExplodedNodeSet Tmp3; + ExplodedNodeSet *SrcSet = &Tmp2; + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + ExplodedNodeSet *DstSet = (I+1 == E) ? &Tmp + : (SrcSet == &Tmp2) ? &Tmp3 + : &Tmp2; + DstSet->clear(); + void *tag = I->first; + Checker *checker = I->second; + for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); + NI != NE; ++NI) + checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, S, *NI, + SymReaper, tag); + SrcSet = DstSet; + } + } + if (!Builder->BuildSinks && !Builder->HasGeneratedNode) Tmp.Add(EntryNode); } @@ -353,8 +374,6 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { CleanedState = NULL; EntryNode = NULL; - // FIXME: Consolidate. - StateMgr.CurrentStmt = 0; CurrentStmt = 0; Builder = NULL; @@ -878,6 +897,18 @@ void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); } +/// ProcessEndPath - Called by GRCoreEngine. Used to generate end-of-path +/// nodes when the control reaches the end of a function. +void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) { + getTF().EvalEndPath(*this, builder); + StateMgr.EndPath(builder.getState()); + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + void *tag = I->first; + Checker *checker = I->second; + checker->EvalEndPath(builder, tag, *this); + } +} + /// ProcessSwitch - Called by GRCoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { @@ -1080,7 +1111,10 @@ void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, ExplodedNodeSet Tmp2; Visit(Idx, *I1, Tmp2); // Evaluate the index. - for (ExplodedNodeSet::iterator I2=Tmp2.begin(),E2=Tmp2.end();I2!=E2; ++I2) { + ExplodedNodeSet Tmp3; + CheckerVisit(A, Tmp3, Tmp2, true); + + for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { const GRState* state = GetState(*I2); SVal V = state->getLValue(A->getType(), state->getSVal(Idx), state->getSVal(Base)); @@ -1190,112 +1224,87 @@ void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, assert(Builder && "GRStmtNodeBuilder must be defined."); // Evaluate the location (checks for bad dereferences). - Pred = EvalLocation(StoreE, Pred, state, location, tag); + ExplodedNodeSet Tmp; + EvalLocation(Tmp, StoreE, Pred, state, location, tag, false); - if (!Pred) + if (Tmp.empty()) return; - assert (!location.isUndef()); - state = GetState(Pred); + assert(!location.isUndef()); + SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind, + ProgramPoint::PostStoreKind); + SaveAndRestore<const void*> OldTag(Builder->Tag, tag); + // Proceed with the store. - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); - SaveAndRestore<const void*> OldTag(Builder->Tag); - Builder->PointKind = ProgramPoint::PostStoreKind; - Builder->Tag = tag; - EvalBind(Dst, AssignE, StoreE, Pred, state, location, Val); + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + EvalBind(Dst, AssignE, StoreE, *NI, GetState(*NI), location, Val); } -void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr* Ex, ExplodedNode* Pred, +void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, const GRState* state, SVal location, - const void *tag) { + const void *tag, QualType LoadTy) { // Evaluate the location (checks for bad dereferences). - Pred = EvalLocation(Ex, Pred, state, location, tag); + ExplodedNodeSet Tmp; + EvalLocation(Tmp, Ex, Pred, state, location, tag, true); - if (!Pred) + if (Tmp.empty()) return; - - state = GetState(Pred); + + assert(!location.isUndef()); + + SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); + SaveAndRestore<const void*> OldTag(Builder->Tag); // Proceed with the load. - ProgramPoint::Kind K = ProgramPoint::PostLoadKind; - - // FIXME: Currently symbolic analysis "generates" new symbols - // for the contents of values. We need a better approach. - - if (location.isUnknown()) { - // This is important. We must nuke the old binding. - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, UnknownVal()), - K, tag); - } - else { - SVal V = state->getSVal(cast<Loc>(location), Ex->getType()); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), K, tag); + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + state = GetState(*NI); + if (location.isUnknown()) { + // This is important. We must nuke the old binding. + MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), + ProgramPoint::PostLoadKind, tag); + } + else { + SVal V = state->getSVal(cast<Loc>(location), LoadTy.isNull() ? + Ex->getType() : LoadTy); + MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, V), ProgramPoint::PostLoadKind, + tag); + } } } -ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag) { - - SaveAndRestore<const void*> OldTag(Builder->Tag); - Builder->Tag = tag; +void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, bool isLoad) { - if (location.isUnknown() || Checkers.empty()) - return Pred; - - - for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) - { - Pred = I->second->CheckLocation(Ex, Pred, state, location, *this); - if (!Pred) - break; + if (location.isUnknown() || Checkers.empty()) { + Dst.Add(Pred); + return; } - return Pred; - - // FIXME: Temporarily disable out-of-bounds checking until we make - // the logic reflect recent changes to CastRegion and friends. -#if 0 - // Check for out-of-bound array access. - if (isa<loc::MemRegionVal>(LV)) { - const MemRegion* R = cast<loc::MemRegionVal>(LV).getRegion(); - if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { - // Get the index of the accessed element. - SVal Idx = ER->getIndex(); - // Get the extent of the array. - SVal NumElements = getStoreManager().getSizeInElements(StNotNull, - ER->getSuperRegion()); - - const GRState * StInBound = StNotNull->AssumeInBound(Idx, NumElements, - true); - const GRState* StOutBound = StNotNull->AssumeInBound(Idx, NumElements, - false); - - if (StOutBound) { - // Report warning. Make sink node manually. - ExplodedNode* OOBNode = - Builder->generateNode(Ex, StOutBound, Pred, - ProgramPoint::PostOutOfBoundsCheckFailedKind); - - if (OOBNode) { - OOBNode->markAsSink(); - - if (StInBound) - ImplicitOOBMemAccesses.insert(OOBNode); - else - ExplicitOOBMemAccesses.insert(OOBNode); - } - } - - if (!StInBound) - return NULL; - - StNotNull = StInBound; - } + ExplodedNodeSet Src, Tmp; + Src.Add(Pred); + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst + : (PrevSet == &Tmp) ? &Src : &Tmp; + + CurrSet->clear(); + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_VisitLocation(*CurrSet, *Builder, *this, S, *NI, state, + location, tag, isLoad); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; } -#endif } //===----------------------------------------------------------------------===// @@ -1311,8 +1320,7 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred, static bool EvalOSAtomicCompareAndSwap(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, - ExplodedNode* Pred) { + CallExpr* CE, ExplodedNode* Pred) { // Not enough arguments to match OSAtomicCompareAndSwap? if (CE->getNumArgs() != 3) @@ -1354,7 +1362,13 @@ static bool EvalOSAtomicCompareAndSwap(ExplodedNodeSet& Dst, const GRState *state = Pred->getState(); ExplodedNodeSet Tmp; SVal location = state->getSVal(theValueExpr); - Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag); + // Here we should use the value type of the region as the load type. + const MemRegion *R = location.getAsRegion(); + QualType LoadTy; + if (R) + LoadTy = cast<TypedRegion>(R)->getValueType(C); + Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag, + LoadTy); for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { @@ -1432,7 +1446,7 @@ static bool EvalOSAtomic(ExplodedNodeSet& Dst, // Check for compare and swap. if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 || strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0) - return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, L, Pred); + return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, Pred); // FIXME: Other atomics. return false; @@ -1521,17 +1535,6 @@ bool GRExprEngine::EvalBuiltinFunction(const FunctionDecl *FD, CallExpr *CE, return false; } -void GRExprEngine::EvalCall(ExplodedNodeSet& Dst, CallExpr* CE, SVal L, - ExplodedNode* Pred) { - assert (Builder && "GRStmtNodeBuilder must be defined."); - - // FIXME: Allow us to chain together transfer functions. - if (EvalOSAtomic(Dst, *this, *Builder, CE, L, Pred)) - return; - - getTF().EvalCall(Dst, *this, *Builder, CE, L, Pred); -} - void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, CallExpr::arg_iterator AI, CallExpr::arg_iterator AE, @@ -1609,17 +1612,25 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred, continue; // Dispatch to the plug-in transfer function. - - unsigned size = Dst.size(); SaveOr OldHasGen(Builder->HasGeneratedNode); - EvalCall(Dst, CE, L, *DI); + Pred = *DI; + + // Dispatch to transfer function logic to handle the call itself. + // FIXME: Allow us to chain together transfer functions. + assert(Builder && "GRStmtNodeBuilder must be defined."); + ExplodedNodeSet DstTmp; + + if (!EvalOSAtomic(DstTmp, *this, *Builder, CE, L, Pred)) + getTF().EvalCall(DstTmp, *this, *Builder, CE, L, Pred); // Handle the case where no nodes where generated. Auto-generate that // contains the updated state if we aren't generating sinks. - - if (!Builder->BuildSinks && Dst.size() == size && + if (!Builder->BuildSinks && DstTmp.empty() && !Builder->HasGeneratedNode) - MakeNode(Dst, CE, *DI, state); + MakeNode(DstTmp, CE, Pred, state); + + // Perform the post-condition check of the CallExpr. + CheckerVisit(CE, Dst, DstTmp, false); } } @@ -1749,46 +1760,47 @@ void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst, SVal ElementV) { - - - // Get the current state. Use 'EvalLocation' to determine if it is a null - // pointer, etc. + // Check if the location we are writing back to is a null pointer. Stmt* elem = S->getElement(); - - Pred = EvalLocation(elem, Pred, GetState(Pred), ElementV); - if (!Pred) + ExplodedNodeSet Tmp; + EvalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); + + if (Tmp.empty()) return; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + Pred = *NI; + const GRState *state = GetState(Pred); + + // Handle the case where the container still has elements. + SVal TrueV = ValMgr.makeTruthVal(1); + const GRState *hasElems = state->BindExpr(S, TrueV); + + // Handle the case where the container has no elements. + SVal FalseV = ValMgr.makeTruthVal(0); + const GRState *noElems = state->BindExpr(S, FalseV); + + if (loc::MemRegionVal* MV = dyn_cast<loc::MemRegionVal>(&ElementV)) + if (const TypedRegion* R = dyn_cast<TypedRegion>(MV->getRegion())) { + // FIXME: The proper thing to do is to really iterate over the + // container. We will do this with dispatch logic to the store. + // For now, just 'conjure' up a symbolic value. + QualType T = R->getValueType(getContext()); + assert(Loc::IsLocType(T)); + unsigned Count = Builder->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + SVal V = ValMgr.makeLoc(Sym); + hasElems = hasElems->bindLoc(ElementV, V); + + // Bind the location to 'nil' on the false branch. + SVal nilV = ValMgr.makeIntVal(0, T); + noElems = noElems->bindLoc(ElementV, nilV); + } - const GRState *state = GetState(Pred); - - // Handle the case where the container still has elements. - SVal TrueV = ValMgr.makeTruthVal(1); - const GRState *hasElems = state->BindExpr(S, TrueV); - - // Handle the case where the container has no elements. - SVal FalseV = ValMgr.makeTruthVal(0); - const GRState *noElems = state->BindExpr(S, FalseV); - - if (loc::MemRegionVal* MV = dyn_cast<loc::MemRegionVal>(&ElementV)) - if (const TypedRegion* R = dyn_cast<TypedRegion>(MV->getRegion())) { - // FIXME: The proper thing to do is to really iterate over the - // container. We will do this with dispatch logic to the store. - // For now, just 'conjure' up a symbolic value. - QualType T = R->getValueType(getContext()); - assert (Loc::IsLocType(T)); - unsigned Count = Builder->getCurrentBlockCount(); - SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); - SVal V = ValMgr.makeLoc(Sym); - hasElems = hasElems->bindLoc(ElementV, V); - - // Bind the location to 'nil' on the false branch. - SVal nilV = ValMgr.makeIntVal(0, T); - noElems = noElems->bindLoc(ElementV, nilV); - } - - // Create the new nodes. - MakeNode(Dst, S, Pred, hasElems); - MakeNode(Dst, S, Pred, noElems); + // Create the new nodes. + MakeNode(Dst, S, Pred, hasElems); + MakeNode(Dst, S, Pred, noElems); + } } //===----------------------------------------------------------------------===// @@ -2035,7 +2047,8 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, // Transfer functions: Miscellaneous statements. //===----------------------------------------------------------------------===// -void GRExprEngine::VisitCast(Expr* CastE, Expr* Ex, ExplodedNode* Pred, ExplodedNodeSet& Dst){ +void GRExprEngine::VisitCast(Expr* CastE, Expr* Ex, ExplodedNode* Pred, + ExplodedNodeSet& Dst){ ExplodedNodeSet S1; QualType T = CastE->getType(); QualType ExTy = Ex->getType(); @@ -2048,16 +2061,18 @@ void GRExprEngine::VisitCast(Expr* CastE, Expr* Ex, ExplodedNode* Pred, Exploded else Visit(Ex, Pred, S1); + ExplodedNodeSet S2; + CheckerVisit(CastE, S2, S1, true); + // Check for casting to "void". if (T->isVoidType()) { - for (ExplodedNodeSet::iterator I1 = S1.begin(), E1 = S1.end(); I1 != E1; ++I1) - Dst.Add(*I1); - + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) + Dst.Add(*I); return; } - for (ExplodedNodeSet::iterator I1 = S1.begin(), E1 = S1.end(); I1 != E1; ++I1) { - ExplodedNode* N = *I1; + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + ExplodedNode* N = *I; const GRState* state = GetState(N); SVal V = state->getSVal(Ex); const SValuator::CastResult &Res = SVator.EvalCast(V, state, T, ExTy); @@ -2107,23 +2122,12 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, else Tmp.Add(Pred); - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + ExplodedNodeSet Tmp2; + CheckerVisit(DS, Tmp2, Tmp, true); + + for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { ExplodedNode *N = *I; - const GRState *state; - - for (CheckersOrdered::iterator CI = Checkers.begin(), CE = Checkers.end(); - CI != CE; ++CI) { - state = GetState(N); - N = CI->second->CheckType(getContext().getCanonicalType(VD->getType()), - N, state, DS, *this); - if (!N) - break; - } - - if (!N) - continue; - - state = GetState(N); + const GRState *state = GetState(N); // Decls without InitExpr are not initialized explicitly. const LocationContext *LC = N->getLocationContext(); @@ -2628,63 +2632,37 @@ void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); } -void GRExprEngine::EvalReturn(ExplodedNodeSet& Dst, ReturnStmt* S, - ExplodedNode* Pred) { - assert (Builder && "GRStmtNodeBuilder must be defined."); - - unsigned size = Dst.size(); - - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - getTF().EvalReturn(Dst, *this, *Builder, S, Pred); - - // Handle the case where no nodes where generated. - - if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode) - MakeNode(Dst, S, Pred, GetState(Pred)); -} - -void GRExprEngine::VisitReturnStmt(ReturnStmt* S, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - Expr* R = S->getRetValue(); - - if (!R) { - EvalReturn(Dst, S, Pred); - return; +void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + ExplodedNodeSet Src; + if (Expr *RetE = RS->getRetValue()) { + Visit(RetE, Pred, Src); } + else { + Src.Add(Pred); + } + + ExplodedNodeSet CheckedSet; + CheckerVisit(RS, CheckedSet, Src, true); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I != E; ++I) { - ExplodedNodeSet Tmp; - Visit(R, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - SVal X = (*I)->getState()->getSVal(R); - - // Check if we return the address of a stack variable. - if (isa<loc::MemRegionVal>(X)) { - // Determine if the value is on the stack. - const MemRegion* R = cast<loc::MemRegionVal>(&X)->getRegion(); - - if (R && R->hasStackStorage()) { - // Create a special node representing the error. - if (ExplodedNode* N = Builder->generateNode(S, GetState(*I), *I)) { - N->markAsSink(); - RetsStackAddr.insert(N); - } - continue; - } - } - // Check if we return an undefined value. - else if (X.isUndef()) { - if (ExplodedNode* N = Builder->generateNode(S, GetState(*I), *I)) { - N->markAsSink(); - RetsUndef.insert(N); - } - continue; - } - - EvalReturn(Dst, S, *I); + assert(Builder && "GRStmtNodeBuilder must be defined."); + + Pred = *I; + unsigned size = Dst.size(); + + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + getTF().EvalReturn(Dst, *this, *Builder, RS, Pred); + + // Handle the case where no nodes where generated. + if (!Builder->BuildSinks && Dst.size() == size && + !Builder->HasGeneratedNode) + MakeNode(Dst, RS, Pred, GetState(Pred)); } } @@ -2878,7 +2856,7 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, //===----------------------------------------------------------------------===// Checker *GRExprEngine::lookupChecker(void *tag) const { - CheckerMap::iterator I = CheckerM.find(tag); + CheckerMap::const_iterator I = CheckerM.find(tag); return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; } @@ -2898,6 +2876,9 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : // work. static std::string getNodeAttributes(const ExplodedNode* N, void*) { +#if 0 + // FIXME: Replace with a general scheme to tell if the node is + // an error node. if (GraphPrintCheckerState->isImplicitNullDeref(N) || GraphPrintCheckerState->isExplicitNullDeref(N) || GraphPrintCheckerState->isUndefDeref(N) || @@ -2907,6 +2888,7 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : GraphPrintCheckerState->isBadCall(N) || GraphPrintCheckerState->isUndefArg(N)) return "color=\"red\",style=\"filled\""; +#endif if (GraphPrintCheckerState->isNoReturnCall(N)) return "color=\"blue\",style=\"filled\""; @@ -2957,11 +2939,10 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : Out << "\\lPostStore\\l"; else if (isa<PostLValue>(Loc)) Out << "\\lPostLValue\\l"; - else if (isa<PostLocationChecksSucceed>(Loc)) - Out << "\\lPostLocationChecksSucceed\\l"; - else if (isa<PostNullCheckFailed>(Loc)) - Out << "\\lPostNullCheckFailed\\l"; +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. if (GraphPrintCheckerState->isImplicitNullDeref(N)) Out << "\\|Implicit-Null Dereference.\\l"; else if (GraphPrintCheckerState->isExplicitNullDeref(N)) @@ -2978,6 +2959,7 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : Out << "\\|Call to NULL/Undefined."; else if (GraphPrintCheckerState->isUndefArg(N)) Out << "\\|Argument in call is undefined"; +#endif break; } @@ -3039,9 +3021,13 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : Out << "\\l"; } +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. if (GraphPrintCheckerState->isUndefControlFlow(N)) { Out << "\\|Control-flow based on\\lUndefined value.\\l"; } +#endif } } diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.cpp b/lib/Analysis/GRExprEngineExperimentalChecks.cpp new file mode 100644 index 0000000000000..2fb7e9fa482f9 --- /dev/null +++ b/lib/Analysis/GRExprEngineExperimentalChecks.cpp @@ -0,0 +1,38 @@ +//=-- GRExprEngineExperimentalChecks.h ------------------------------*- 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 functions to instantiate and register experimental +// checks in GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "GRExprEngineExperimentalChecks.h" +#include "clang/Analysis/LocalCheckers.h" + +using namespace clang; + +void clang::RegisterExperimentalChecks(GRExprEngine &Eng) { + // These are checks that never belong as internal checks + // within GRExprEngine. + RegisterPthreadLockChecker(Eng); + RegisterMallocChecker(Eng); +} + +void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { + // These are internal checks that should eventually migrate to + // RegisterInternalChecks() once they have been further tested. + + // Note that this must be registered after ReturnStackAddresEngsChecker. + RegisterReturnPointerRangeChecker(Eng); + RegisterPointerSubChecker(Eng); + RegisterPointerArithChecker(Eng); + RegisterCastToStructChecker(Eng); + RegisterArrayBoundChecker(Eng); +} diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.h b/lib/Analysis/GRExprEngineExperimentalChecks.h new file mode 100644 index 0000000000000..9a9da32e556e6 --- /dev/null +++ b/lib/Analysis/GRExprEngineExperimentalChecks.h @@ -0,0 +1,26 @@ +//=-- GRExprEngineExperimentalChecks.h ------------------------------*- 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 functions to instantiate and register experimental +// checks in GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS +#define LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS + +namespace clang { + +class GRExprEngine; + +void RegisterPthreadLockChecker(GRExprEngine &Eng); +void RegisterMallocChecker(GRExprEngine &Eng); + +} // end clang namespace +#endif diff --git a/lib/Analysis/GRExprEngineInternalChecks.cpp b/lib/Analysis/GRExprEngineInternalChecks.cpp index 695f0b02e5971..d0f60fde5b1ba 100644 --- a/lib/Analysis/GRExprEngineInternalChecks.cpp +++ b/lib/Analysis/GRExprEngineInternalChecks.cpp @@ -12,16 +12,11 @@ // //===----------------------------------------------------------------------===// +#include "GRExprEngineInternalChecks.h" #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" -#include "clang/Analysis/PathSensitive/Checkers/DivZeroChecker.h" -#include "clang/Analysis/PathSensitive/Checkers/BadCallChecker.h" -#include "clang/Analysis/PathSensitive/Checkers/UndefinedArgChecker.h" #include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h" -#include "clang/Analysis/PathSensitive/Checkers/AttrNonNullChecker.h" -#include "clang/Analysis/PathSensitive/Checkers/VLASizeChecker.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/Compiler.h" @@ -290,79 +285,6 @@ public: } }; -class VISIBILITY_HIDDEN RetStack : public BuiltinBug { -public: - RetStack(GRExprEngine* eng) - : BuiltinBug(eng, "Return of address to stack-allocated memory") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::ret_stackaddr_iterator I=Eng.ret_stackaddr_begin(), - End = Eng.ret_stackaddr_end(); I!=End; ++I) { - - ExplodedNode* N = *I; - const Stmt *S = cast<PostStmt>(N->getLocation()).getStmt(); - const Expr* E = cast<ReturnStmt>(S)->getRetValue(); - assert(E && "Return expression cannot be NULL"); - - // Get the value associated with E. - loc::MemRegionVal V = cast<loc::MemRegionVal>(N->getState()->getSVal(E)); - - // Generate a report for this bug. - std::string buf; - llvm::raw_string_ostream os(buf); - SourceRange R; - - // Check if the region is a compound literal. - if (const CompoundLiteralRegion* CR = - dyn_cast<CompoundLiteralRegion>(V.getRegion())) { - - const CompoundLiteralExpr* CL = CR->getLiteralExpr(); - os << "Address of stack memory associated with a compound literal " - "declared on line " - << BR.getSourceManager() - .getInstantiationLineNumber(CL->getLocStart()) - << " returned."; - - R = CL->getSourceRange(); - } - else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(V.getRegion())) { - const Expr* ARE = AR->getExpr(); - SourceLocation L = ARE->getLocStart(); - R = ARE->getSourceRange(); - - os << "Address of stack memory allocated by call to alloca() on line " - << BR.getSourceManager().getInstantiationLineNumber(L) - << " returned."; - } - else { - os << "Address of stack memory associated with local variable '" - << V.getRegion()->getString() << "' returned."; - } - - RangedBugReport *report = new RangedBugReport(*this, os.str().c_str(), N); - report->addRange(E->getSourceRange()); - if (R.isValid()) report->addRange(R); - BR.EmitReport(report); - } - } -}; - -class VISIBILITY_HIDDEN RetUndef : public BuiltinBug { -public: - RetUndef(GRExprEngine* eng) : BuiltinBug(eng, "Garbage return value", - "Undefined or garbage value returned to caller") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - Emit(BR, Eng.ret_undef_begin(), Eng.ret_undef_end()); - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetRetValExpr(N), N); - } -}; - class VISIBILITY_HIDDEN UndefBranch : public BuiltinBug { struct VISIBILITY_HIDDEN FindUndefExpr { GRStateManager& VM; @@ -439,17 +361,6 @@ public: } }; -class VISIBILITY_HIDDEN OutOfBoundMemoryAccess : public BuiltinBug { -public: - OutOfBoundMemoryAccess(GRExprEngine* eng) - : BuiltinBug(eng,"Out-of-bounds memory access", - "Load or store into an out-of-bound memory position.") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - Emit(BR, Eng.explicit_oob_memacc_begin(), Eng.explicit_oob_memacc_end()); - } -}; - } // end clang namespace //===----------------------------------------------------------------------===// @@ -464,11 +375,8 @@ void GRExprEngine::RegisterInternalChecks() { // to 'FlushReports' from BugReporter. BR.Register(new UndefBranch(this)); BR.Register(new UndefResult(this)); - BR.Register(new RetStack(this)); - BR.Register(new RetUndef(this)); BR.Register(new BadMsgExprArg(this)); BR.Register(new BadReceiver(this)); - BR.Register(new OutOfBoundMemoryAccess(this)); BR.Register(new NilReceiverStructRet(this)); BR.Register(new NilReceiverLargerThanVoidPtrRet(this)); @@ -476,14 +384,17 @@ void GRExprEngine::RegisterInternalChecks() { // explicitly registered with the BugReporter. If they issue any BugReports, // their associated BugType will get registered with the BugReporter // automatically. Note that the check itself is owned by the GRExprEngine - // object. - registerCheck(new AttrNonNullChecker()); - registerCheck(new UndefinedArgChecker()); + // object. registerCheck(new UndefinedAssignmentChecker()); - registerCheck(new BadCallChecker()); - registerCheck(new DivZeroChecker()); - registerCheck(new UndefDerefChecker()); - registerCheck(new NullDerefChecker()); - registerCheck(new UndefSizedVLAChecker()); - registerCheck(new ZeroSizedVLAChecker()); + + RegisterAttrNonNullChecker(*this); + RegisterUndefinedArgChecker(*this); + RegisterBadCallChecker(*this); + RegisterDereferenceChecker(*this); + RegisterVLASizeChecker(*this); + RegisterDivZeroChecker(*this); + RegisterReturnStackAddressChecker(*this); + RegisterReturnUndefChecker(*this); + RegisterFixedAddressChecker(*this); + RegisterUndefinedArraySubscriptChecker(*this); } diff --git a/lib/Analysis/GRExprEngineInternalChecks.h b/lib/Analysis/GRExprEngineInternalChecks.h new file mode 100644 index 0000000000000..a9077bf75715b --- /dev/null +++ b/lib/Analysis/GRExprEngineInternalChecks.h @@ -0,0 +1,39 @@ +//=-- GRExprEngineInternalChecks.h- 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 functions to instantiate and register the "built-in" +// checks in GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS +#define LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS + +namespace clang { + +class GRExprEngine; + +void RegisterAttrNonNullChecker(GRExprEngine &Eng); +void RegisterBadCallChecker(GRExprEngine &Eng); +void RegisterDereferenceChecker(GRExprEngine &Eng); +void RegisterDivZeroChecker(GRExprEngine &Eng); +void RegisterReturnPointerRangeChecker(GRExprEngine &Eng); +void RegisterReturnStackAddressChecker(GRExprEngine &Eng); +void RegisterReturnUndefChecker(GRExprEngine &Eng); +void RegisterVLASizeChecker(GRExprEngine &Eng); +void RegisterPointerSubChecker(GRExprEngine &Eng); +void RegisterPointerArithChecker(GRExprEngine &Eng); +void RegisterFixedAddressChecker(GRExprEngine &Eng); +void RegisterCastToStructChecker(GRExprEngine &Eng); +void RegisterUndefinedArgChecker(GRExprEngine &Eng); +void RegisterArrayBoundChecker(GRExprEngine &Eng); +void RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng); + +} // end clang namespace +#endif diff --git a/lib/Analysis/GRState.cpp b/lib/Analysis/GRState.cpp index f269824d5477c..23ee0b2258bd7 100644 --- a/lib/Analysis/GRState.cpp +++ b/lib/Analysis/GRState.cpp @@ -332,9 +332,3 @@ bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, uint64_t x) { return isEqual(state, Ex, getBasicVals().getValue(x, Ex->getType())); } - -//===----------------------------------------------------------------------===// -// Persistent values for indexing into the Generic Data Map. - -int GRState::NullDerefTag::TagInt = 0; - diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp index ae78d1f35ff64..2510445a7f311 100644 --- a/lib/Analysis/LiveVariables.cpp +++ b/lib/Analysis/LiveVariables.cpp @@ -18,6 +18,7 @@ #include "clang/Analysis/CFG.h" #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" +#include "clang/Analysis/Support/SaveAndRestore.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Compiler.h" @@ -301,10 +302,9 @@ void LiveVariables::runOnAllBlocks(const CFG& cfg, LiveVariables::ObserverTy* Obs, bool recordStmtValues) { Solver S(*this); - ObserverTy* OldObserver = getAnalysisData().Observer; - getAnalysisData().Observer = Obs; + SaveAndRestore<LiveVariables::ObserverTy*> SRObs(getAnalysisData().Observer, + Obs); S.runOnAllBlocks(cfg, recordStmtValues); - getAnalysisData().Observer = OldObserver; } //===----------------------------------------------------------------------===// @@ -333,7 +333,7 @@ bool LiveVariables::isLive(const Stmt* Loc, const VarDecl* D) const { // printing liveness state for debugging // -void LiveVariables::dumpLiveness(const ValTy& V, SourceManager& SM) const { +void LiveVariables::dumpLiveness(const ValTy& V, const SourceManager& SM) const { const AnalysisDataTy& AD = getAnalysisData(); for (AnalysisDataTy::decl_iterator I = AD.begin_decl(), @@ -345,8 +345,8 @@ void LiveVariables::dumpLiveness(const ValTy& V, SourceManager& SM) const { } } -void LiveVariables::dumpBlockLiveness(SourceManager& M) const { - for (BlockDataMapTy::iterator I = getBlockDataMap().begin(), +void LiveVariables::dumpBlockLiveness(const SourceManager& M) const { + for (BlockDataMapTy::const_iterator I = getBlockDataMap().begin(), E = getBlockDataMap().end(); I!=E; ++I) { llvm::errs() << "\n[ B" << I->first->getBlockID() << " (live variables at block exit) ]\n"; diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp new file mode 100644 index 0000000000000..93e708332ed76 --- /dev/null +++ b/lib/Analysis/MallocChecker.cpp @@ -0,0 +1,218 @@ +//=== MallocChecker.cpp - A malloc/free checker -------------------*- 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 malloc/free checker, which checks for potential memory +// leaks, double free, and use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineExperimentalChecks.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Analysis/PathSensitive/GRState.h" +#include "clang/Analysis/PathSensitive/GRStateTrait.h" +#include "clang/Analysis/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +using namespace clang; + +namespace { + +struct RefState { + enum Kind { Allocated, Released, Escaped } K; + const Stmt *S; + + RefState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isAllocated() const { return K == Allocated; } + bool isReleased() const { return K == Released; } + bool isEscaped() const { return K == Escaped; } + + bool operator==(const RefState &X) const { + return K == X.K && S == X.S; + } + + static RefState getAllocated(const Stmt *s) { return RefState(Allocated, s); } + static RefState getReleased(const Stmt *s) { return RefState(Released, s); } + static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + +class VISIBILITY_HIDDEN RegionState {}; + +class VISIBILITY_HIDDEN MallocChecker : public CheckerVisitor<MallocChecker> { + BuiltinBug *BT_DoubleFree; + BuiltinBug *BT_Leak; + IdentifierInfo *II_malloc; + IdentifierInfo *II_free; + +public: + MallocChecker() : BT_DoubleFree(0), BT_Leak(0), II_malloc(0), II_free(0) {} + static void *getTag(); + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper); + void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); +private: + void MallocMem(CheckerContext &C, const CallExpr *CE); + void FreeMem(CheckerContext &C, const CallExpr *CE); +}; +} + +namespace clang { + template <> + struct GRStateTrait<RegionState> + : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, RefState> > { + static void *GDMIndex() { return MallocChecker::getTag(); } + }; +} + +void clang::RegisterMallocChecker(GRExprEngine &Eng) { + Eng.registerCheck(new MallocChecker()); +} + +void *MallocChecker::getTag() { + static int x; + return &x; +} + +void MallocChecker::PostVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + ASTContext &Ctx = C.getASTContext(); + if (!II_malloc) + II_malloc = &Ctx.Idents.get("malloc"); + if (!II_free) + II_free = &Ctx.Idents.get("free"); + + if (FD->getIdentifier() == II_malloc) { + MallocMem(C, CE); + return; + } + + if (FD->getIdentifier() == II_free) { + FreeMem(C, CE); + return; + } +} + +void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + SVal CallVal = state->getSVal(CE); + SymbolRef Sym = CallVal.getAsLocSymbol(); + assert(Sym); + // Set the symbol's state to Allocated. + const GRState *AllocState + = state->set<RegionState>(Sym, RefState::getAllocated(CE)); + C.addTransition(C.GenerateNode(CE, AllocState)); +} + +void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + SVal ArgVal = state->getSVal(CE->getArg(0)); + SymbolRef Sym = ArgVal.getAsLocSymbol(); + assert(Sym); + + const RefState *RS = state->get<RegionState>(Sym); + assert(RS); + + // Check double free. + if (RS->isReleased()) { + ExplodedNode *N = C.GenerateNode(CE, true); + if (N) { + if (!BT_DoubleFree) + BT_DoubleFree = 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); + C.EmitReport(R); + } + return; + } + + // Normal free. + const GRState *FreedState + = state->set<RegionState>(Sym, RefState::getReleased(CE)); + C.addTransition(C.GenerateNode(CE, FreedState)); +} + +void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, + SymbolReaper &SymReaper) { + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + const GRState *state = C.getState(); + const RefState *RS = state->get<RegionState>(Sym); + if (!RS) + return; + + if (RS->isAllocated()) { + ExplodedNode *N = C.GenerateNode(S, true); + if (N) { + if (!BT_Leak) + BT_Leak = 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); + } + } + } +} + +void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) { + const GRState *state = B.getState(); + typedef llvm::ImmutableMap<SymbolRef, RefState> SymMap; + SymMap M = state->get<RegionState>(); + + for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + RefState RS = I->second; + if (RS.isAllocated()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_Leak) + BT_Leak = 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); + } + } + } +} + +void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = C.getState(); + + SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); + + if (!Sym) + return; + + const RefState *RS = state->get<RegionState>(Sym); + if (!RS) + return; + + // FIXME: check other cases. + if (RS->isAllocated()) + state = state->set<RegionState>(Sym, RefState::getEscaped(S)); + + ExplodedNode *N = C.GenerateNode(S, state); + if (N) + C.addTransition(N); +} diff --git a/lib/Analysis/ManagerRegistry.cpp b/lib/Analysis/ManagerRegistry.cpp new file mode 100644 index 0000000000000..8943db2a23438 --- /dev/null +++ b/lib/Analysis/ManagerRegistry.cpp @@ -0,0 +1,20 @@ +//===- ManagerRegistry.cpp - Pluggble Analyzer module creators --*- 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 pluggable analyzer module creators. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/ManagerRegistry.h" + +using namespace clang; + +StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; + +ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp index 353e63240294b..8c0b85c0c7296 100644 --- a/lib/Analysis/MemRegion.cpp +++ b/lib/Analysis/MemRegion.cpp @@ -378,11 +378,29 @@ bool MemRegion::hasGlobalsOrParametersStorage() const { return false; } +// getBaseRegion strips away all elements and fields, and get the base region +// of them. +const MemRegion *MemRegion::getBaseRegion() const { + const MemRegion *R = this; + while (true) { + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + R = ER->getSuperRegion(); + continue; + } + if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { + R = FR->getSuperRegion(); + continue; + } + break; + } + return R; +} + //===----------------------------------------------------------------------===// // View handling. //===----------------------------------------------------------------------===// -const MemRegion *MemRegion::getBaseRegion() const { +const MemRegion *MemRegion::StripCasts() const { const MemRegion *R = this; while (true) { if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { diff --git a/lib/Analysis/NSErrorChecker.cpp b/lib/Analysis/NSErrorChecker.cpp index 307686ff57b33..93b617b115d92 100644 --- a/lib/Analysis/NSErrorChecker.cpp +++ b/lib/Analysis/NSErrorChecker.cpp @@ -209,15 +209,12 @@ void NSErrorChecker::CheckParamDeref(const VarDecl *Param, return; // Iterate over the implicit-null dereferences. - NullDerefChecker *Checker = Eng.getChecker<NullDerefChecker>(); - assert(Checker && "NullDerefChecker not exist."); - for (NullDerefChecker::iterator I = Checker->implicit_nodes_begin(), - E = Checker->implicit_nodes_end(); I != E; ++I) { - + ExplodedNode *const* I, *const* E; + llvm::tie(I, E) = GetImplicitNullDereferences(Eng); + for ( ; I != E; ++I) { const GRState *state = (*I)->getState(); - const SVal* X = state->get<GRState::NullDerefTag>(); - - if (!X || X->getAsSymbol() != ParamSym) + SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt()); + if (location.getAsSymbol() != ParamSym) continue; // Emit an error. diff --git a/lib/Analysis/PointerArithChecker.cpp b/lib/Analysis/PointerArithChecker.cpp new file mode 100644 index 0000000000000..93823484e1d02 --- /dev/null +++ b/lib/Analysis/PointerArithChecker.cpp @@ -0,0 +1,71 @@ +//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerArithChecker, a builtin checker that checks for +// pointer arithmetic on locations other than array elements. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN PointerArithChecker + : public CheckerVisitor<PointerArithChecker> { + BuiltinBug *BT; +public: + PointerArithChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerArithChecker::getTag() { + static int x; + return &x; +} + +void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + if (B->getOpcode() != BinaryOperator::Sub && + B->getOpcode() != BinaryOperator::Add) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + + if (!LR || !RV.isConstant()) + return; + + // If pointer arithmetic is done on variables of non-array type, this often + // means behavior rely on memory organization, which is dangerous. + if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || + isa<CompoundLiteralRegion>(LR)) { + + if (ExplodedNode *N = C.GenerateNode(B)) { + if (!BT) + BT = new BuiltinBug("Dangerous pointer arithmetic", + "Pointer arithmetic done on non-array variables " + "means reliance on memory layout, which is " + "dangerous."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } + } +} + +void clang::RegisterPointerArithChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PointerArithChecker()); +} diff --git a/lib/Analysis/PointerSubChecker.cpp b/lib/Analysis/PointerSubChecker.cpp new file mode 100644 index 0000000000000..4c7906f4beba8 --- /dev/null +++ b/lib/Analysis/PointerSubChecker.cpp @@ -0,0 +1,77 @@ +//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerSubChecker, a builtin checker that checks for +// pointer subtractions on two pointers pointing to different memory chunks. +// This check corresponds to CWE-469. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN PointerSubChecker + : public CheckerVisitor<PointerSubChecker> { + BuiltinBug *BT; +public: + PointerSubChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerSubChecker::getTag() { + static int x; + return &x; +} + +void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // When doing pointer subtraction, if the two pointers do not point to the + // same memory chunk, emit a warning. + if (B->getOpcode() != BinaryOperator::Sub) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + const MemRegion *RR = RV.getAsRegion(); + + if (!(LR && RR)) + return; + + const MemRegion *BaseLR = LR->getBaseRegion(); + const MemRegion *BaseRR = RR->getBaseRegion(); + + if (BaseLR == BaseRR) + return; + + // Allow arithmetic on different symbolic regions. + if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) + return; + + if (ExplodedNode *N = C.GenerateNode(B)) { + if (!BT) + BT = new BuiltinBug("Pointer subtraction", + "Subtraction of two pointers that do not point to " + "the same memory chunk may cause incorrect result."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } +} + +void clang::RegisterPointerSubChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PointerSubChecker()); +} diff --git a/lib/Analysis/PthreadLockChecker.cpp b/lib/Analysis/PthreadLockChecker.cpp new file mode 100644 index 0000000000000..66206616b0089 --- /dev/null +++ b/lib/Analysis/PthreadLockChecker.cpp @@ -0,0 +1,141 @@ +//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with GRExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/GRStateTrait.h" +#include "GRExprEngineExperimentalChecks.h" +#include "llvm/ADT/ImmutableSet.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN PthreadLockChecker + : public CheckerVisitor<PthreadLockChecker> { + BugType *BT; +public: + PthreadLockChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + void AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock); + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock); + +}; +} // end anonymous namespace + +// GDM Entry for tracking lock state. +namespace { class VISIBILITY_HIDDEN LockSet {}; } +namespace clang { +template <> struct GRStateTrait<LockSet> : + public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { + static void* GDMIndex() { return PthreadLockChecker::getTag(); } +}; +} // end clang namespace + +void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PthreadLockChecker()); +} + + +void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const CodeTextRegion *R = + dyn_cast_or_null<CodeTextRegion>(state->getSVal(Callee).getAsRegion()); + + if (!R) + return; + + llvm::StringRef FName = R->getDecl()->getName(); + + if (FName == "pthread_mutex_lock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); + } + else if (FName == "pthread_mutex_trylock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); + } + else if (FName == "pthread_mutex_unlock") { + if (CE->getNumArgs() != 1) + return; + ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); + } +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + SVal X = state->getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = cast<DefinedSVal>(X); + const GRState *lockSucc = state; + + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const GRState *lockFail; + llvm::tie(lockFail, lockSucc) = state->Assume(retVal); + assert(lockFail && lockSucc); + C.addTransition(C.GenerateNode(CE, lockFail)); + } + else { + // Assume that the return value was 0. + lockSucc = state->Assume(retVal, false); + assert(lockSucc); + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add<LockSet>(lockR); + + C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) : + C.getPredecessor()); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + // Record that the lock was released. + // FIXME: Handle unlocking locks that were never acquired. This may + // require IPA for wrappers. + const GRState *unlockState = state->remove<LockSet>(lockR); + + if (state == unlockState) + return; + + C.addTransition(C.GenerateNode(CE, unlockState)); +} diff --git a/lib/Analysis/RangeConstraintManager.cpp b/lib/Analysis/RangeConstraintManager.cpp index 73b445e6ab36b..f5cae698f9243 100644 --- a/lib/Analysis/RangeConstraintManager.cpp +++ b/lib/Analysis/RangeConstraintManager.cpp @@ -16,7 +16,7 @@ #include "clang/Analysis/PathSensitive/GRState.h" #include "clang/Analysis/PathSensitive/GRStateTrait.h" #include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "clang/Frontend/ManagerRegistry.h" +#include "clang/Analysis/ManagerRegistry.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/ADT/FoldingSet.h" diff --git a/lib/Analysis/RegionStore.cpp b/lib/Analysis/RegionStore.cpp index dbf8c42d273e9..ae3fa14c2a268 100644 --- a/lib/Analysis/RegionStore.cpp +++ b/lib/Analysis/RegionStore.cpp @@ -164,7 +164,7 @@ public: ~RegionStoreSubRegionMap() {} bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { - Map::iterator I = M.find(Parent); + Map::const_iterator I = M.find(Parent); if (I == M.end()) return true; @@ -360,7 +360,8 @@ public: //===------------------------------------------------------------------===// const GRState *setExtent(const GRState *state, const MemRegion* R, SVal Extent); - SVal getSizeInElements(const GRState *state, const MemRegion* R); + DefinedOrUnknownSVal getSizeInElements(const GRState *state, + const MemRegion* R); //===------------------------------------------------------------------===// // Utility methods. @@ -461,7 +462,7 @@ const GRState *RegionStoreManager::InvalidateRegion(const GRState *state, ASTContext& Ctx = StateMgr.getContext(); // Strip away casts. - R = R->getBaseRegion(); + R = R->StripCasts(); // Get the mapping of regions -> subregions. llvm::OwningPtr<RegionStoreSubRegionMap> @@ -696,8 +697,8 @@ SVal RegionStoreManager::getLValueElement(QualType elementType, SVal Offset, // Extents for regions. //===----------------------------------------------------------------------===// -SVal RegionStoreManager::getSizeInElements(const GRState *state, - const MemRegion *R) { +DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, + const MemRegion *R) { switch (R->getKind()) { case MemRegion::MemSpaceRegionKind: @@ -1028,16 +1029,20 @@ RegionStoreManager::Retrieve(const GRState *state, Loc L, QualType T) { return SValuator::CastResult(state, UnknownVal()); if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return CastRetrievedVal(RetrieveField(state, FR), state, FR, T); + return SValuator::CastResult(state, + CastRetrievedVal(RetrieveField(state, FR), FR, T)); if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) - return CastRetrievedVal(RetrieveElement(state, ER), state, ER, T); + return SValuator::CastResult(state, + CastRetrievedVal(RetrieveElement(state, ER), ER, T)); if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) - return CastRetrievedVal(RetrieveObjCIvar(state, IVR), state, IVR, T); + return SValuator::CastResult(state, + CastRetrievedVal(RetrieveObjCIvar(state, IVR), IVR, T)); if (const VarRegion *VR = dyn_cast<VarRegion>(R)) - return CastRetrievedVal(RetrieveVar(state, VR), state, VR, T); + return SValuator::CastResult(state, + CastRetrievedVal(RetrieveVar(state, VR), VR, T)); RegionBindings B = GetRegionBindings(state->getStore()); RegionBindings::data_type* V = B.lookup(R); @@ -1109,7 +1114,7 @@ SVal RegionStoreManager::RetrieveElement(const GRState* state, // FIXME: Handle loads from strings where the literal is treated as // an integer, e.g., *((unsigned int*)"hello") ASTContext &Ctx = getContext(); - QualType T = StrR->getValueType(Ctx)->getAs<ArrayType>()->getElementType(); + QualType T = Ctx.getAsArrayType(StrR->getValueType(Ctx))->getElementType(); if (T != Ctx.getCanonicalType(R->getElementType())) return UnknownVal(); diff --git a/lib/Analysis/ReturnPointerRangeChecker.cpp b/lib/Analysis/ReturnPointerRangeChecker.cpp new file mode 100644 index 0000000000000..44887b2625da4 --- /dev/null +++ b/lib/Analysis/ReturnPointerRangeChecker.cpp @@ -0,0 +1,97 @@ +//== ReturnPointerRangeChecker.cpp ------------------------------*- 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 ReturnPointerRangeChecker, which is a path-sensitive check +// which looks for an out-of-bound pointer being returned to callers. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN ReturnPointerRangeChecker : + public CheckerVisitor<ReturnPointerRangeChecker> { + BuiltinBug *BT; +public: + ReturnPointerRangeChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void clang::RegisterReturnPointerRangeChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ReturnPointerRangeChecker()); +} + +void *ReturnPointerRangeChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + const GRState *state = C.getState(); + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = state->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + if (!R) + return; + + R = R->StripCasts(); + if (!R) + return; + + const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); + if (!ER) + return; + + DefinedOrUnknownSVal &Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + + // FIXME: All of this out-of-bounds checking should eventually be refactored + // into a common place. + + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion()); + + const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.GenerateNode(RS, StOutBound, true); + + if (!N) + return; + + // FIXME: This bug correspond to CWE-466. Eventually we should have bug + // types explicitly reference such exploit categories (when applicable). + if (!BT) + BT = new BuiltinBug("Return of pointer value outside of expected range", + "Returned pointer value points outside the original object " + "(potential buffer overflow)"); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + + // Generate a report for this bug. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + + C.EmitReport(report); + } +} diff --git a/lib/Analysis/ReturnStackAddressChecker.cpp b/lib/Analysis/ReturnStackAddressChecker.cpp new file mode 100644 index 0000000000000..e4be8712d09b3 --- /dev/null +++ b/lib/Analysis/ReturnStackAddressChecker.cpp @@ -0,0 +1,97 @@ +//== ReturnStackAddressChecker.cpp ------------------------------*- 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 ReturnStackAddressChecker, which is a path-sensitive +// check which looks for the addresses of stack variables being returned to +// callers. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN ReturnStackAddressChecker : + public CheckerVisitor<ReturnStackAddressChecker> { + BuiltinBug *BT; +public: + ReturnStackAddressChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void clang::RegisterReturnStackAddressChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ReturnStackAddressChecker()); +} + +void *ReturnStackAddressChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnStackAddressChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = C.getState()->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + if (!R || !R->hasStackStorage()) + return; + + ExplodedNode *N = C.GenerateNode(RS, C.getState(), true); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Return of address to stack-allocated memory"); + + // Generate a report for this bug. + llvm::SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range; + + // Check if the region is a compound literal. + if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { + const CompoundLiteralExpr* CL = CR->getLiteralExpr(); + os << "Address of stack memory associated with a compound literal " + "declared on line " + << C.getSourceManager().getInstantiationLineNumber(CL->getLocStart()) + << " returned to caller"; + range = CL->getSourceRange(); + } + else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { + const Expr* ARE = AR->getExpr(); + SourceLocation L = ARE->getLocStart(); + range = ARE->getSourceRange(); + os << "Address of stack memory allocated by call to alloca() on line " + << C.getSourceManager().getInstantiationLineNumber(L) + << " returned to caller"; + } + else { + os << "Address of stack memory associated with local variable '" + << R->getString() << "' returned."; + } + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(RS->getSourceRange()); + if (range.isValid()) + report->addRange(range); + + C.EmitReport(report); +} diff --git a/lib/Analysis/ReturnUndefChecker.cpp b/lib/Analysis/ReturnUndefChecker.cpp new file mode 100644 index 0000000000000..796c7608c86d6 --- /dev/null +++ b/lib/Analysis/ReturnUndefChecker.cpp @@ -0,0 +1,68 @@ +//== ReturnUndefChecker.cpp -------------------------------------*- 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 ReturnUndefChecker, which is a path-sensitive +// check which looks for undefined or garbage values being returned to the +// caller. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN ReturnUndefChecker : + public CheckerVisitor<ReturnUndefChecker> { + BuiltinBug *BT; +public: + ReturnUndefChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void clang::RegisterReturnUndefChecker(GRExprEngine &Eng) { + Eng.registerCheck(new ReturnUndefChecker()); +} + +void *ReturnUndefChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + if (!C.getState()->getSVal(RetE).isUndef()) + return; + + ExplodedNode *N = C.GenerateNode(RS, C.getState(), true); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Garbage return value", + "Undefined or garbage value returned to caller"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); + + C.EmitReport(report); +} diff --git a/lib/Analysis/SVals.cpp b/lib/Analysis/SVals.cpp index 688b7ff6e1e34..d5d36e3b90909 100644 --- a/lib/Analysis/SVals.cpp +++ b/lib/Analysis/SVals.cpp @@ -63,7 +63,7 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? SymbolRef SVal::getAsLocSymbol() const { if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) { - const MemRegion *R = X->getBaseRegion(); + const MemRegion *R = X->StripCasts(); if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) return SymR->getSymbol(); } @@ -100,9 +100,9 @@ const MemRegion *SVal::getAsRegion() const { return 0; } -const MemRegion *loc::MemRegionVal::getBaseRegion() const { +const MemRegion *loc::MemRegionVal::StripCasts() const { const MemRegion *R = getRegion(); - return R ? R->getBaseRegion() : NULL; + return R ? R->StripCasts() : NULL; } bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const { @@ -173,6 +173,10 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { // Useful predicates. //===----------------------------------------------------------------------===// +bool SVal::isConstant() const { + return isa<nonloc::ConcreteInt>(this) || isa<loc::ConcreteInt>(this); +} + bool SVal::isZeroConstant() const { if (isa<loc::ConcreteInt>(*this)) return cast<loc::ConcreteInt>(*this).getValue() == 0; diff --git a/lib/Analysis/SValuator.cpp b/lib/Analysis/SValuator.cpp index 573cac315b3a5..ac727b0ac696c 100644 --- a/lib/Analysis/SValuator.cpp +++ b/lib/Analysis/SValuator.cpp @@ -62,8 +62,7 @@ SValuator::CastResult SValuator::EvalCast(SVal val, const GRState *state, ASTContext &C = ValMgr.getContext(); // For const casts, just propagate the value. - if (C.getCanonicalType(castTy).getUnqualifiedType() == - C.getCanonicalType(originalTy).getUnqualifiedType()) + if (C.hasSameUnqualifiedType(castTy, originalTy)) return CastResult(state, val); // Check for casts from pointers to integers. diff --git a/lib/Analysis/Store.cpp b/lib/Analysis/Store.cpp index 4b4ae6580820f..2fd72ac0a148c 100644 --- a/lib/Analysis/Store.cpp +++ b/lib/Analysis/Store.cpp @@ -21,7 +21,7 @@ StoreManager::StoreManager(GRStateManager &stateMgr) MRMgr(ValMgr.getRegionManager()) {} const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, - QualType EleTy, uint64_t index) { + QualType EleTy, uint64_t index) { SVal idx = ValMgr.makeArrayIndex(index); return MRMgr.getElementRegion(EleTy, idx, Base, ValMgr.getContext()); } @@ -43,7 +43,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) // Handle casts to Objective-C objects. if (CastToTy->isObjCObjectPointerType()) - return R->getBaseRegion(); + return R->StripCasts(); if (CastToTy->isBlockPointerType()) { // FIXME: We may need different solutions, depending on the symbol @@ -64,7 +64,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); // Handle casts to void*. We just pass the region through. - if (CanonPointeeTy.getUnqualifiedType() == Ctx.VoidTy) + if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy) return R; // Handle casts from compatible types. @@ -192,14 +192,14 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) /// CastRetrievedVal - Used by subclasses of StoreManager to implement /// implicit casts that arise from loads from regions that are reinterpreted /// as another region. -SValuator::CastResult StoreManager::CastRetrievedVal(SVal V, - const GRState *state, - const TypedRegion *R, - QualType castTy) { - if (castTy.isNull()) - return SValuator::CastResult(state, V); - +SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, + QualType castTy) { ASTContext &Ctx = ValMgr.getContext(); - return ValMgr.getSValuator().EvalCast(V, state, castTy, R->getValueType(Ctx)); + + if (castTy.isNull()) + return V; + + assert(Ctx.hasSameUnqualifiedType(castTy, R->getValueType(Ctx))); + return V; } diff --git a/lib/Analysis/UndefinedArgChecker.cpp b/lib/Analysis/UndefinedArgChecker.cpp index a229f55ff6d9e..923a7e1bed0bd 100644 --- a/lib/Analysis/UndefinedArgChecker.cpp +++ b/lib/Analysis/UndefinedArgChecker.cpp @@ -12,14 +12,28 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/Checkers/UndefinedArgChecker.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/PathSensitive/BugReporter.h" +#include "GRExprEngineInternalChecks.h" using namespace clang; -void *UndefinedArgChecker::getTag() { - static int x = 0; - return &x; +namespace { +class VISIBILITY_HIDDEN UndefinedArgChecker + : public CheckerVisitor<UndefinedArgChecker> { + BugType *BT; +public: + UndefinedArgChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +void clang::RegisterUndefinedArgChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefinedArgChecker()); } void UndefinedArgChecker::PreVisitCallExpr(CheckerContext &C, @@ -29,11 +43,10 @@ void UndefinedArgChecker::PreVisitCallExpr(CheckerContext &C, if (C.getState()->getSVal(*I).isUndef()) { if (ExplodedNode *N = C.GenerateNode(CE, true)) { if (!BT) - BT = new BugType("Pass-by-value argument in function call is " - "undefined", "Logic error"); + BT = new BuiltinBug("Pass-by-value argument in function call is " + "undefined"); // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName().c_str(), - N); + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); R->addRange((*I)->getSourceRange()); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); C.EmitReport(R); diff --git a/lib/Analysis/UndefinedArraySubscriptChecker.cpp b/lib/Analysis/UndefinedArraySubscriptChecker.cpp new file mode 100644 index 0000000000000..887c7755fe45a --- /dev/null +++ b/lib/Analysis/UndefinedArraySubscriptChecker.cpp @@ -0,0 +1,56 @@ +//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedArraySubscriptChecker, a builtin check in GRExprEngine +// that performs checks for undefined array subscripts. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN UndefinedArraySubscriptChecker + : public CheckerVisitor<UndefinedArraySubscriptChecker> { + BugType *BT; +public: + UndefinedArraySubscriptChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A); +}; +} // end anonymous namespace + +void clang::RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefinedArraySubscriptChecker()); +} + +void +UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A) { + if (C.getState()->getSVal(A->getIdx()).isUndef()) { + if (ExplodedNode *N = C.GenerateNode(A, true)) { + if (!BT) + BT = new BuiltinBug("Array subscript is undefined"); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addRange(A->getIdx()->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + A->getIdx()); + C.EmitReport(R); + } + } +} diff --git a/lib/Analysis/UndefinedAssignmentChecker.cpp b/lib/Analysis/UndefinedAssignmentChecker.cpp index 2e3ac34913ab8..b8062f3595622 100644 --- a/lib/Analysis/UndefinedAssignmentChecker.cpp +++ b/lib/Analysis/UndefinedAssignmentChecker.cpp @@ -36,11 +36,10 @@ void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, return; if (!BT) - BT = new BugType("Assigned value is garbage or undefined", - "Logic error"); + BT = new BuiltinBug("Assigned value is garbage or undefined"); // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName().c_str(), N); + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); if (AssignE) { const Expr *ex = 0; diff --git a/lib/Analysis/VLASizeChecker.cpp b/lib/Analysis/VLASizeChecker.cpp index 0e731902f4bb4..799a73e293c88 100644 --- a/lib/Analysis/VLASizeChecker.cpp +++ b/lib/Analysis/VLASizeChecker.cpp @@ -7,96 +7,91 @@ // //===----------------------------------------------------------------------===// // -// This defines two VLASizeCheckers, a builtin check in GRExprEngine that +// This defines VLASizeChecker, a builtin check in GRExprEngine that // performs checks for declaration of VLA of undefined or zero size. // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/Checkers/VLASizeChecker.h" +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/BugReporter.h" using namespace clang; -void *UndefSizedVLAChecker::getTag() { - static int x = 0; - return &x; +namespace { +class VISIBILITY_HIDDEN VLASizeChecker : public CheckerVisitor<VLASizeChecker> { + BugType *BT_zero; + BugType *BT_undef; + +public: + VLASizeChecker() : BT_zero(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); +}; +} // end anonymous namespace + +void clang::RegisterVLASizeChecker(GRExprEngine &Eng) { + Eng.registerCheck(new VLASizeChecker()); } -ExplodedNode *UndefSizedVLAChecker::CheckType(QualType T, ExplodedNode *Pred, - const GRState *state, - Stmt *S, GRExprEngine &Eng) { - GRStmtNodeBuilder &Builder = Eng.getBuilder(); - BugReporter &BR = Eng.getBugReporter(); - - if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) { - // FIXME: Handle multi-dimensional VLAs. - Expr* SE = VLA->getSizeExpr(); - SVal Size_untested = state->getSVal(SE); - - if (Size_untested.isUndef()) { - if (ExplodedNode* N = Builder.generateNode(S, state, Pred)) { - N->markAsSink(); - if (!BT) - BT = new BugType("Declared variable-length array (VLA) uses a garbage" - " value as its size", "Logic error"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getName().c_str(), N); - R->addRange(SE->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); - BR.EmitReport(R); - } - return 0; - } +void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + if (!DS->isSingleDecl()) + return; + + const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return; + + const VariableArrayType *VLA + = C.getASTContext().getAsVariableArrayType(VD->getType()); + if (!VLA) + return; + + // FIXME: Handle multi-dimensional VLAs. + const Expr* SE = VLA->getSizeExpr(); + const GRState *state = C.getState(); + SVal sizeV = state->getSVal(SE); + + if (sizeV.isUndef()) { + // Generate an error node. + ExplodedNode *N = C.GenerateNode(DS, true); + if (!N) + return; + + if (!BT_undef) + BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " + "garbage value as its size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + return; } - return Pred; -} - -void *ZeroSizedVLAChecker::getTag() { - static int x; - return &x; -} - -ExplodedNode *ZeroSizedVLAChecker::CheckType(QualType T, ExplodedNode *Pred, - const GRState *state, Stmt *S, - GRExprEngine &Eng) { - GRStmtNodeBuilder &Builder = Eng.getBuilder(); - BugReporter &BR = Eng.getBugReporter(); - - if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) { - // FIXME: Handle multi-dimensional VLAs. - Expr* SE = VLA->getSizeExpr(); - SVal Size_untested = state->getSVal(SE); - - DefinedOrUnknownSVal *Size = dyn_cast<DefinedOrUnknownSVal>(&Size_untested); - // Undefined size is checked in another checker. - if (!Size) - return Pred; - - const GRState *zeroState = state->Assume(*Size, false); - state = state->Assume(*Size, true); - - if (zeroState && !state) { - if (ExplodedNode* N = Builder.generateNode(S, zeroState, Pred)) { - N->markAsSink(); - if (!BT) - BT = new BugType("Declared variable-length array (VLA) has zero size", - "Logic error"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getName().c_str(), N); - R->addRange(SE->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); - BR.EmitReport(R); - } - } - if (!state) - return 0; - - return Builder.generateNode(S, state, Pred); + + // Check if the size is zero. + DefinedOrUnknownSVal sizeD = cast<DefinedOrUnknownSVal>(sizeV); + + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = state->Assume(sizeD); + + if (stateZero && !stateNotZero) { + ExplodedNode* N = C.GenerateNode(DS, stateZero, true); + if (!BT_zero) + BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " + "size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + return; } - else - return Pred; + + // From this point on, assume that the size is not zero. + if (state != stateNotZero) + C.addTransition(C.GenerateNode(DS, stateNotZero)); } - |