diff options
Diffstat (limited to 'lib/StaticAnalyzer')
73 files changed, 6213 insertions, 3937 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index ab66e98..c582cfc 100644 --- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -15,6 +15,7 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -23,40 +24,32 @@ using namespace ento; namespace { class AttrNonNullChecker - : public Checker< check::PreStmt<CallExpr> > { + : public Checker< check::PreCall > { mutable OwningPtr<BugType> BT; public: - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; }; } // end anonymous namespace -void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, +void AttrNonNullChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); - - // Check if the callee has a 'nonnull' attribute. - SVal X = state->getSVal(CE->getCallee(), LCtx); - - const FunctionDecl *FD = X.getAsFunctionDecl(); + const Decl *FD = Call.getDecl(); if (!FD) return; - const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); + const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); if (!Att) return; - // Iterate through the arguments of CE and check them for null. - unsigned idx = 0; - - for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; - ++I, ++idx) { + ProgramStateRef state = C.getState(); + // Iterate through the arguments of CE and check them for null. + for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) { if (!Att->isNonNull(idx)) continue; - SVal V = state->getSVal(*I, LCtx); + SVal V = Call.getArgSVal(idx); DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); // If the value is unknown or undefined, we can't perform this check. @@ -65,11 +58,16 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, if (!isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential - // transparent_unoin GCC extension. - QualType T = (*I)->getType(); + // transparent_union GCC extension. + const Expr *ArgE = Call.getArgExpr(idx); + if (!ArgE) + continue; + + QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; + if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) { nonloc::CompoundVal::iterator CSV_I = CSV->begin(); assert(CSV_I != CSV->end()); @@ -78,8 +76,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, assert(++CSV_I == CSV->end()); if (!DV) continue; - } - else { + } else { // FIXME: Handle LazyCompoundVals? continue; } @@ -106,10 +103,9 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. - const Expr *arg = *I; - R->addRange(arg->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode, - arg, R)); + R->addRange(Call.getArgSourceRange(idx)); + if (const Expr *ArgE = Call.getArgExpr(idx)) + bugreporter::addTrackNullOrUndefValueVisitor(errorNode, ArgE, R); // Emit the bug report. C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 6dd0a8c..955e79a 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -17,18 +17,20 @@ #include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtObjC.h" #include "clang/AST/ASTContext.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" using namespace clang; using namespace ento; @@ -44,21 +46,40 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static const char* GetReceiverNameType(const ObjCMessage &msg) { +static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) - return ID->getIdentifier()->getNameStart(); - return 0; + return ID->getIdentifier()->getName(); + return StringRef(); } -static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID, - StringRef ClassName) { - if (ID->getIdentifier()->getName() == ClassName) - return true; +enum FoundationClass { + FC_None, + FC_NSArray, + FC_NSDictionary, + FC_NSEnumerator, + FC_NSOrderedSet, + FC_NSSet, + FC_NSString +}; + +static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) { + static llvm::StringMap<FoundationClass> Classes; + if (Classes.empty()) { + Classes["NSArray"] = FC_NSArray; + Classes["NSDictionary"] = FC_NSDictionary; + Classes["NSEnumerator"] = FC_NSEnumerator; + Classes["NSOrderedSet"] = FC_NSOrderedSet; + Classes["NSSet"] = FC_NSSet; + Classes["NSString"] = FC_NSString; + } - if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) - return isReceiverClassOrSuperclass(Super, ClassName); + // FIXME: Should we cache this at all? + FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); + if (result == FC_None) + if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) + return findKnownClass(Super); - return false; + return result; } static inline bool isNil(SVal X) { @@ -74,15 +95,15 @@ namespace { mutable OwningPtr<APIMisuse> BT; void WarnNilArg(CheckerContext &C, - const ObjCMessage &msg, unsigned Arg) const; + const ObjCMethodCall &msg, unsigned Arg) const; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; }; } void NilArgChecker::WarnNilArg(CheckerContext &C, - const ObjCMessage &msg, + const ObjCMethodCall &msg, unsigned int Arg) const { if (!BT) @@ -91,7 +112,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(msg) << "' method '" + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; BugReport *R = new BugReport(*BT, os.str(), N); @@ -100,13 +121,13 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, } } -void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, +void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; - if (isReceiverClassOrSuperclass(ID, "NSString")) { + if (findKnownClass(ID) == FC_NSString) { Selector S = msg.getSelector(); if (S.isUnarySelector()) @@ -130,7 +151,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { - if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState()))) + if (isNil(msg.getArgSVal(0))) WarnNilArg(C, msg, 0); } } @@ -411,8 +432,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Arg, report); C.EmitReport(report); return; } @@ -434,11 +454,11 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> { mutable OwningPtr<BugType> BT; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } -void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, +void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { @@ -490,18 +510,18 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { mutable Selector initWithObjectsAndKeysS; mutable OwningPtr<BugType> BT; - bool isVariadicMessage(const ObjCMessage &msg) const; + bool isVariadicMessage(const ObjCMethodCall &msg) const; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } /// isVariadicMessage - Returns whether the given message is a variadic message, /// where all arguments must be Objective-C types. bool -VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { - const ObjCMethodDecl *MD = msg.getMethodDecl(); +VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { + const ObjCMethodDecl *MD = msg.getDecl(); if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) return false; @@ -517,53 +537,35 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { // gains that this analysis gives. const ObjCInterfaceDecl *Class = MD->getClassInterface(); - // -[NSArray initWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSArray") && - S == initWithObjectsS) - return true; - - // -[NSDictionary initWithObjectsAndKeys:] - if (isReceiverClassOrSuperclass(Class, "NSDictionary") && - S == initWithObjectsAndKeysS) - return true; - - // -[NSSet initWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSSet") && - S == initWithObjectsS) - return true; - - // -[NSOrderedSet initWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") && - S == initWithObjectsS) - return true; + switch (findKnownClass(Class)) { + case FC_NSArray: + case FC_NSOrderedSet: + case FC_NSSet: + return S == initWithObjectsS; + case FC_NSDictionary: + return S == initWithObjectsAndKeysS; + default: + return false; + } } else { const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); - // -[NSArray arrayWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSArray") && - S == arrayWithObjectsS) - return true; - - // -[NSDictionary dictionaryWithObjectsAndKeys:] - if (isReceiverClassOrSuperclass(Class, "NSDictionary") && - S == dictionaryWithObjectsAndKeysS) - return true; - - // -[NSSet setWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSSet") && - S == setWithObjectsS) - return true; - - // -[NSOrderedSet orderedSetWithObjects:] - if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") && - S == orderedSetWithObjectsS) - return true; + switch (findKnownClass(Class)) { + case FC_NSArray: + return S == arrayWithObjectsS; + case FC_NSOrderedSet: + return S == orderedSetWithObjectsS; + case FC_NSSet: + return S == setWithObjectsS; + case FC_NSDictionary: + return S == dictionaryWithObjectsAndKeysS; + default: + return false; + } } - - return false; } -void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, +void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " @@ -599,7 +601,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { - QualType ArgTy = msg.getArgType(I); + QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; @@ -608,8 +610,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Ignore pointer constants. - if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(), - state))) + if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) continue; // Ignore pointer types annotated with 'NSObject' attribute. @@ -621,9 +622,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Generate only one error node to use for all bug reports. - if (!errorNode.hasValue()) { + if (!errorNode.hasValue()) errorNode = C.addTransition(); - } if (!errorNode.getValue()) continue; @@ -631,23 +631,93 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - if (const char *TypeName = GetReceiverNameType(msg)) + StringRef TypeName = GetReceiverInterfaceName(msg); + if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() - << "' should be an Objective-C pointer type, not '" - << ArgTy.getAsString() << "'"; + << "' should be an Objective-C pointer type, not '"; + ArgTy.print(os, C.getLangOpts()); + os << "'"; - BugReport *R = new BugReport(*BT, os.str(), - errorNode.getValue()); + BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.EmitReport(R); } } //===----------------------------------------------------------------------===// +// Improves the modeling of loops over Cocoa collections. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCLoopChecker + : public Checker<check::PostStmt<ObjCForCollectionStmt> > { + +public: + void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; +}; +} + +static bool isKnownNonNilCollectionType(QualType T) { + const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + if (!ID) + return false; + + switch (findKnownClass(ID)) { + case FC_NSArray: + case FC_NSDictionary: + case FC_NSEnumerator: + case FC_NSOrderedSet: + case FC_NSSet: + return true; + default: + return false; + } +} + +void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Check if this is the branch for the end of the loop. + SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); + if (CollectionSentinel.isZeroConstant()) + return; + + // See if the collection is one where we /know/ the elements are non-nil. + const Expr *Collection = FCS->getCollection(); + if (!isKnownNonNilCollectionType(Collection->getType())) + return; + + // FIXME: Copied from ExprEngineObjC. + const Stmt *Element = FCS->getElement(); + SVal ElementVar; + if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { + const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); + assert(ElemDecl->getInit() == 0); + ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); + } else { + ElementVar = State->getSVal(Element, C.getLocationContext()); + } + + if (!isa<Loc>(ElementVar)) + return; + + // Go ahead and assume the value is non-nil. + SVal Val = State->getSVal(cast<Loc>(ElementVar)); + State = State->assume(cast<DefinedOrUnknownSVal>(Val), true); + C.addTransition(State); +} + + +//===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// @@ -670,3 +740,7 @@ void ento::registerClassReleaseChecker(CheckerManager &mgr) { void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { mgr.registerChecker<VariadicMethodTypeChecker>(); } + +void ento::registerObjCLoopChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCLoopChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index a377ca9..7fe51d3 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -3,8 +3,6 @@ clang_tablegen(Checkers.inc -gen-clang-sa-checkers SOURCE Checkers.td TARGET ClangSACheckers) -set(LLVM_USED_LIBS clangBasic clangAST clangStaticAnalyzerCore) - add_clang_library(clangStaticAnalyzerCheckers AdjustedReturnValueChecker.cpp AnalyzerStatsChecker.cpp @@ -31,10 +29,11 @@ add_clang_library(clangStaticAnalyzerCheckers DebugCheckers.cpp DereferenceChecker.cpp DivZeroChecker.cpp + DynamicTypePropagation.cpp + ExprInspectionChecker.cpp FixedAddressChecker.cpp GenericTaintChecker.cpp IdempotentOperationChecker.cpp - IteratorsChecker.cpp LLVMConventionsChecker.cpp MacOSKeychainAPIChecker.cpp MacOSXAPIChecker.cpp @@ -59,6 +58,7 @@ add_clang_library(clangStaticAnalyzerCheckers StackAddrEscapeChecker.cpp StreamChecker.cpp TaintTesterChecker.cpp + TraversalChecker.cpp UndefBranchChecker.cpp UndefCapturedBlockVarChecker.cpp UndefResultChecker.cpp @@ -74,7 +74,15 @@ add_dependencies(clangStaticAnalyzerCheckers clangStaticAnalyzerCore ClangAttrClasses ClangAttrList + ClangCommentNodes ClangDeclNodes + ClangDiagnosticCommon ClangStmtNodes ClangSACheckers ) + +target_link_libraries(clangStaticAnalyzerCheckers + clangBasic + clangAST + clangStaticAnalyzerCore + ) diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 9eb7edf..483082a 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -66,7 +66,7 @@ public: const StoreManager::InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const; + const CallEvent *Call) const; typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *) const; @@ -252,8 +252,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(S->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, S, report); C.EmitReport(report); return NULL; } @@ -901,9 +900,10 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // If the size is zero, there won't be any actual memory access, so // just bind the return value to the destination buffer and return. - if (stateZeroSize) { + if (stateZeroSize && !stateNonZeroSize) { stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal); C.addTransition(stateZeroSize); + return; } // If the size can be nonzero, we have to check the other arguments. @@ -1403,6 +1403,24 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // For strncpy, this is just checking that lenVal <= sizeof(dst) // (Yes, strncpy and strncat differ in how they treat termination. // strncat ALWAYS terminates, but strncpy doesn't.) + + // We need a special case for when the copy size is zero, in which + // case strncpy will do no work at all. Our bounds check uses n-1 + // as the last element accessed, so n == 0 is problematic. + ProgramStateRef StateZeroSize, StateNonZeroSize; + llvm::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, state, *lenValNL, sizeTy); + + // If the size is known to be zero, we're done. + if (StateZeroSize && !StateNonZeroSize) { + StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal); + C.addTransition(StateZeroSize); + return; + } + + // Otherwise, go ahead and figure out the last element we'll touch. + // We don't record the non-zero assumption here because we can't + // be sure. We won't warn on a possible zero. NonLoc one = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy)); maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy); @@ -1876,7 +1894,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); if (Entries.isEmpty()) return state; diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index f601431..30f45c7 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -15,8 +15,8 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/TargetInfo.h" @@ -27,35 +27,37 @@ using namespace ento; namespace { class CallAndMessageChecker - : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > { + : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage, + check::PreCall > { mutable OwningPtr<BugType> BT_call_null; mutable OwningPtr<BugType> BT_call_undef; + mutable OwningPtr<BugType> BT_cxx_call_null; + mutable OwningPtr<BugType> BT_cxx_call_undef; mutable OwningPtr<BugType> BT_call_arg; mutable OwningPtr<BugType> BT_msg_undef; mutable OwningPtr<BugType> BT_objc_prop_undef; + mutable OwningPtr<BugType> BT_objc_subscript_undef; mutable OwningPtr<BugType> BT_msg_arg; mutable OwningPtr<BugType> BT_msg_ret; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: - static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg, - const char *BT_desc, OwningPtr<BugType> &BT); - static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange, - const Expr *argEx, - const bool checkUninitFields, - const char *BT_desc, - OwningPtr<BugType> &BT); - - static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); - void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, + static bool PreVisitProcessArg(CheckerContext &C, SVal V, + SourceRange argRange, const Expr *argEx, + bool IsFirstArgument, bool checkUninitFields, + const CallEvent &Call, OwningPtr<BugType> &BT); + + static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); + void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, ExplodedNode *N) const; void HandleNilReceiver(CheckerContext &C, ProgramStateRef state, - ObjCMessage msg) const; + const ObjCMethodCall &msg) const; static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) { if (!BT) @@ -64,55 +66,63 @@ private: }; } // end anonymous namespace -void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, - const CallExpr *CE) { +void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, + const Expr *BadE) { ExplodedNode *N = C.generateSink(); if (!N) return; BugReport *R = new BugReport(*BT, BT->getName(), N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetCalleeExpr(N), R)); + if (BadE) { + R->addRange(BadE->getSourceRange()); + bugreporter::addTrackNullOrUndefValueVisitor(N, BadE, R); + } C.EmitReport(R); } -void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C, - CallOrObjCMessage callOrMsg, - const char *BT_desc, - OwningPtr<BugType> &BT) { - // Don't check for uninitialized field values in arguments if the - // caller has a body that is available and we have the chance to inline it. - // This is a hack, but is a reasonable compromise betweens sometimes warning - // and sometimes not depending on if we decide to inline a function. - const Decl *D = callOrMsg.getDecl(); - const bool checkUninitFields = - !(C.getAnalysisManager().shouldInlineCall() && - (D && D->getBody())); - - for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i) - if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i), - callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i), - checkUninitFields, - BT_desc, BT)) - return; +StringRef describeUninitializedArgumentInCall(const CallEvent &Call, + bool IsFirstArgument) { + switch (Call.getKind()) { + case CE_ObjCMessage: { + const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); + switch (Msg.getMessageKind()) { + case OCM_Message: + return "Argument in message expression is an uninitialized value"; + case OCM_PropertyAccess: + assert(Msg.isSetter() && "Getters have no args"); + return "Argument for property setter is an uninitialized value"; + case OCM_Subscript: + if (Msg.isSetter() && IsFirstArgument) + return "Argument for subscript setter is an uninitialized value"; + return "Subscript index is an uninitialized value"; + } + llvm_unreachable("Unknown message kind."); + } + case CE_Block: + return "Block call argument is an uninitialized value"; + default: + return "Function call argument is an uninitialized value"; + } } bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, - const bool checkUninitFields, - const char *BT_desc, + bool IsFirstArgument, + bool checkUninitFields, + const CallEvent &Call, OwningPtr<BugType> &BT) { if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { - LazyInit_BT(BT_desc, BT); + LazyInit_BT("Uninitialized argument value", BT); // Generate a report for this bug. - BugReport *R = new BugReport(*BT, BT->getName(), N); + StringRef Desc = describeUninitializedArgumentInCall(Call, + IsFirstArgument); + BugReport *R = new BugReport(*BT, Desc, N); R->addRange(argRange); if (argEx) - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx, - R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, argEx, R); C.EmitReport(R); } return true; @@ -128,14 +138,13 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, public: SmallVector<const FieldDecl *, 10> FieldChain; private: - ASTContext &C; StoreManager &StoreMgr; MemRegionManager &MrMgr; Store store; public: - FindUninitializedField(ASTContext &c, StoreManager &storeMgr, + FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, Store s) - : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} + : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} bool Find(const TypedValueRegion *R) { QualType T = R->getValueType(); @@ -146,7 +155,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, RD->field_begin(), E = RD->field_end(); I!=E; ++I) { const FieldRegion *FR = MrMgr.getFieldRegion(*I, R); FieldChain.push_back(*I); - T = (*I)->getType(); + T = I->getType(); if (T->getAsStructureType()) { if (Find(FR)) return true; @@ -165,14 +174,13 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, }; const LazyCompoundValData *D = LV->getCVData(); - FindUninitializedField F(C.getASTContext(), - C.getState()->getStateManager().getStoreManager(), + FindUninitializedField F(C.getState()->getStateManager().getStoreManager(), C.getSValBuilder().getRegionManager(), D->getStore()); if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { - LazyInit_BT(BT_desc, BT); + LazyInit_BT("Uninitialized argument value", BT); SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; @@ -212,87 +220,137 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const{ const Expr *Callee = CE->getCallee()->IgnoreParens(); + ProgramStateRef State = C.getState(); const LocationContext *LCtx = C.getLocationContext(); - SVal L = C.getState()->getSVal(Callee, LCtx); + SVal L = State->getSVal(Callee, LCtx); if (L.isUndef()) { if (!BT_call_undef) BT_call_undef.reset(new BuiltinBug("Called function pointer is an " "uninitalized pointer value")); - EmitBadCall(BT_call_undef.get(), C, CE); + emitBadCall(BT_call_undef.get(), C, Callee); return; } - if (isa<loc::ConcreteInt>(L)) { + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(L)); + + // FIXME: Do we want to record the non-null assumption here? + if (StNull && !StNonNull) { if (!BT_call_null) BT_call_null.reset( new BuiltinBug("Called function pointer is null (null dereference)")); - EmitBadCall(BT_call_null.get(), C, CE); + emitBadCall(BT_call_null.get(), C, Callee); + } +} + +void CallAndMessageChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + // If this is a call to a C++ method, check if the callee is null or + // undefined. + if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { + SVal V = CC->getCXXThisVal(); + if (V.isUndef()) { + if (!BT_cxx_call_undef) + BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is " + "uninitialized")); + emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); + return; + } + + ProgramStateRef State = C.getState(); + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); + + // FIXME: Do we want to record the non-null assumption here? + if (StNull && !StNonNull) { + if (!BT_cxx_call_null) + BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " + "is null")); + emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); + return; + } } - PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState(), LCtx), - "Function call argument is an uninitialized value", - BT_call_arg); + // Don't check for uninitialized field values in arguments if the + // caller has a body that is available and we have the chance to inline it. + // This is a hack, but is a reasonable compromise betweens sometimes warning + // and sometimes not depending on if we decide to inline a function. + const Decl *D = Call.getDecl(); + const bool checkUninitFields = + !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); + + OwningPtr<BugType> *BT; + if (isa<ObjCMethodCall>(Call)) + BT = &BT_msg_arg; + else + BT = &BT_call_arg; + + for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) + if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), + Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, + checkUninitFields, Call, *BT)) + return; } -void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, +void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { + SVal recVal = msg.getReceiverSVal(); + if (recVal.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + BugType *BT = 0; + switch (msg.getMessageKind()) { + case OCM_Message: + if (!BT_msg_undef) + BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " + "is an uninitialized value")); + BT = BT_msg_undef.get(); + break; + case OCM_PropertyAccess: + if (!BT_objc_prop_undef) + BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " + "uninitialized object " + "pointer")); + BT = BT_objc_prop_undef.get(); + break; + case OCM_Subscript: + if (!BT_objc_subscript_undef) + BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an " + "uninitialized object " + "pointer")); + BT = BT_objc_subscript_undef.get(); + break; + } + assert(BT && "Unknown message kind."); - ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); + BugReport *R = new BugReport(*BT, BT->getName(), N); + const ObjCMessageExpr *ME = msg.getOriginExpr(); + R->addRange(ME->getReceiverRange()); - // FIXME: Handle 'super'? - if (const Expr *receiver = msg.getInstanceReceiver()) { - SVal recVal = state->getSVal(receiver, LCtx); - if (recVal.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { - BugType *BT = 0; - if (msg.isPureMessageExpr()) { - if (!BT_msg_undef) - BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " - "is an uninitialized value")); - BT = BT_msg_undef.get(); - } - else { - if (!BT_objc_prop_undef) - BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " - "uninitialized object pointer")); - BT = BT_objc_prop_undef.get(); - } - BugReport *R = - new BugReport(*BT, BT->getName(), N); - R->addRange(receiver->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver, - R)); - C.EmitReport(R); - } + // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. + if (const Expr *ReceiverE = ME->getInstanceReceiver()) + bugreporter::addTrackNullOrUndefValueVisitor(N, ReceiverE, R); + C.EmitReport(R); + } + return; + } else { + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); + + ProgramStateRef state = C.getState(); + ProgramStateRef notNilState, nilState; + llvm::tie(notNilState, nilState) = state->assume(receiverVal); + + // Handle receiver must be nil. + if (nilState && !notNilState) { + HandleNilReceiver(C, state, msg); return; - } else { - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - - ProgramStateRef notNilState, nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); - - // Handle receiver must be nil. - if (nilState && !notNilState) { - HandleNilReceiver(C, state, msg); - return; - } } } - - const char *bugDesc = msg.isPropertySetter() ? - "Argument for property setter is an uninitialized value" - : "Argument in message expression is an uninitialized value"; - // Check for any arguments that are uninitialized/undefined. - PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx), - bugDesc, BT_msg_arg); } void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, - const ObjCMessage &msg, + const ObjCMethodCall &msg, ExplodedNode *N) const { if (!BT_msg_ret) @@ -300,18 +358,20 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, new BuiltinBug("Receiver in message expression is " "'nil' and returns a garbage value")); + const ObjCMessageExpr *ME = msg.getOriginExpr(); + SmallString<200> buf; llvm::raw_svector_ostream os(buf); - os << "The receiver of message '" << msg.getSelector().getAsString() - << "' is nil and returns a value of type '" - << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; + os << "The receiver of message '" << ME->getSelector().getAsString() + << "' is nil and returns a value of type '"; + msg.getResultType().print(os, C.getLangOpts()); + os << "' that will be garbage"; BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); - if (const Expr *receiver = msg.getInstanceReceiver()) { - report->addRange(receiver->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver, - report)); + report->addRange(ME->getReceiverRange()); + // FIXME: This won't track "self" in messages to super. + if (const Expr *receiver = ME->getInstanceReceiver()) { + bugreporter::addTrackNullOrUndefValueVisitor(N, receiver, report); } C.EmitReport(report); } @@ -324,25 +384,25 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) { void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, ProgramStateRef state, - ObjCMessage msg) const { + const ObjCMethodCall &Msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. - QualType RetTy = msg.getType(Ctx); + QualType RetTy = Msg.getResultType(); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); const LocationContext *LCtx = C.getLocationContext(); if (CanRetTy->isStructureOrClassType()) { // Structure returns are safe since the compiler zeroes them out. - SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); return; } // Other cases: check if sizeof(return type) > sizeof(void*) if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() - .isConsumedExpr(msg.getMessageExpr())) { + .isConsumedExpr(Msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); @@ -355,7 +415,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode *N = C.generateSink(state)) - emitNilReceiverBug(C, msg, N); + emitNilReceiverBug(C, Msg, N); return; } @@ -372,8 +432,8 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // - SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); return; } diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 133204a..7a25865 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -215,10 +215,10 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, E = D->propimpl_end(); I!=E; ++I) { // We can only check the synthesized properties - if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) continue; - ObjCIvarDecl *ID = (*I)->getPropertyIvarDecl(); + ObjCIvarDecl *ID = I->getPropertyIvarDecl(); if (!ID) continue; @@ -226,7 +226,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars continue; - const ObjCPropertyDecl *PD = (*I)->getPropertyDecl(); + const ObjCPropertyDecl *PD = I->getPropertyDecl(); if (!PD) continue; @@ -261,7 +261,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, } PathDiagnosticLocation SDLoc = - PathDiagnosticLocation::createBegin((*I), BR.getSourceManager()); + PathDiagnosticLocation::createBegin(*I, BR.getSourceManager()); BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, os.str(), SDLoc); diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index dde9071..b8b7c36 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -31,6 +31,7 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) { T.getOS() == llvm::Triple::FreeBSD || T.getOS() == llvm::Triple::NetBSD || T.getOS() == llvm::Triple::OpenBSD || + T.getOS() == llvm::Triple::Bitrig || T.getOS() == llvm::Triple::DragonFly; } diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 843502f..0e9efaa 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -37,6 +37,8 @@ class CheckerDocumentation : public Checker< check::PreStmt<DeclStmt>, check::PostStmt<CallExpr>, check::PreObjCMessage, check::PostObjCMessage, + check::PreCall, + check::PostCall, check::BranchCondition, check::Location, check::Bind, @@ -72,14 +74,41 @@ public: /// which does not include the control flow statements such as IfStmt. The /// callback can be specialized to be called with any subclass of Stmt. /// - /// check::PostStmt<DeclStmt> + /// check::PostStmt<CallExpr> void checkPostStmt(const CallExpr *DS, CheckerContext &C) const; - /// \brief Pre-visit the Objective C messages. - void checkPreObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + /// \brief Pre-visit the Objective C message. + /// + /// This will be called before the analyzer core processes the method call. + /// This is called for any action which produces an Objective-C message send, + /// including explicit message syntax and property access. + /// + /// check::PreObjCMessage + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} + + /// \brief Post-visit the Objective C message. + /// \sa checkPreObjCMessage() + /// + /// check::PostObjCMessage + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} - /// \brief Post-visit the Objective C messages. - void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + /// \brief Pre-visit an abstract "call" event. + /// + /// This is used for checkers that want to check arguments or attributed + /// behavior for functions and methods no matter how they are being invoked. + /// + /// Note that this includes ALL cross-body invocations, so if you want to + /// limit your checks to, say, function calls, you can either test for that + /// or fall back to the explicit callback (i.e. check::PreStmt). + /// + /// check::PreCall + void checkPreCall(const CallEvent &Call, CheckerContext &C) const {} + + /// \brief Post-visit an abstract "call" event. + /// \sa checkPreObjCMessage() + /// + /// check::PostCall + void checkPostCall(const CallEvent &Call, CheckerContext &C) const {} /// \brief Pre-visit of the condition statement of a branch (such as IfStmt). void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {} @@ -94,7 +123,7 @@ public: /// /// check::Location void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, - CheckerContext &C) const {} + CheckerContext &) const {} /// \brief Called on binding of a value to a location. /// @@ -103,7 +132,7 @@ public: /// \param S The bind is performed while processing the statement S. /// /// check::Bind - void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {} + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {} /// \brief Called whenever a symbol becomes dead. @@ -187,24 +216,26 @@ public: bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; } - /// check::RegionChanges - /// Allows tracking regions which get invalidated. - /// \param state The current program state. - /// \param invalidated A set of all symbols potentially touched by the change. + /// \brief Allows tracking regions which get invalidated. + /// + /// \param State The current program state. + /// \param Invalidated A set of all symbols potentially touched by the change. /// \param ExplicitRegions The regions explicitly requested for invalidation. /// For example, in the case of a function call, these would be arguments. /// \param Regions The transitive closure of accessible regions, /// i.e. all regions that may have been touched by this change. - /// \param The call expression wrapper if the regions are invalidated by a - /// call, 0 otherwise. - /// Note, in order to be notified, the checker should also implement + /// \param Call The call expression wrapper if the regions are invalidated + /// by a call, 0 otherwise. + /// Note, in order to be notified, the checker should also implement the /// wantsRegionChangeUpdate callback. + /// + /// check::RegionChanges ProgramStateRef checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *, + const StoreManager::InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { return State; } diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 96a8d26..8110bd0 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -84,6 +84,10 @@ def StackAddrEscapeChecker : Checker<"StackAddressEscape">, HelpText<"Check that addresses to stack memory do not escape the function">, DescFile<"StackAddrEscapeChecker.cpp">; +def DynamicTypePropagation : Checker<"DynamicTypePropagation">, + HelpText<"Generate dynamic type information">, + DescFile<"DynamicTypePropagation.cpp">; + } // end "core" let ParentPackage = CoreExperimental in { @@ -168,10 +172,6 @@ def ReturnUndefChecker : Checker<"UndefReturn">, let ParentPackage = CplusplusExperimental in { -def IteratorsChecker : Checker<"Iterators">, - HelpText<"Check improper uses of STL vector iterators">, - DescFile<"IteratorsChecker.cpp">; - def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; @@ -283,6 +283,10 @@ def MallocPessimistic : Checker<"Malloc">, HelpText<"Check for memory leaks, double free, and use-after-free problems.">, DescFile<"MallocChecker.cpp">; +def MallocSizeofChecker : Checker<"MallocSizeof">, + HelpText<"Check for dubious malloc arguments involving sizeof">, + DescFile<"MallocSizeofChecker.cpp">; + } // end "unix" let ParentPackage = UnixExperimental in { @@ -295,10 +299,6 @@ def MallocOptimistic : Checker<"MallocWithAnnotations">, HelpText<"Check for memory leaks, double free, and use-after-free problems. Assumes that all user-defined functions which might free a pointer are annotated.">, DescFile<"MallocChecker.cpp">; -def MallocSizeofChecker : Checker<"MallocSizeof">, - HelpText<"Check for dubious malloc arguments involving sizeof">, - DescFile<"MallocSizeofChecker.cpp">; - def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, DescFile<"PthreadLockChecker.cpp">; @@ -361,7 +361,7 @@ def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">, let ParentPackage = Cocoa in { def ObjCAtSyncChecker : Checker<"AtSync">, - HelpText<"Check for null pointers used as mutexes for @synchronized">, + HelpText<"Check for nil pointers used as mutexes for @synchronized">, DescFile<"ObjCAtSyncChecker.cpp">; def NilArgChecker : Checker<"NilArg">, @@ -373,8 +373,8 @@ def ClassReleaseChecker : Checker<"ClassRelease">, DescFile<"BasicObjCFoundationChecks.cpp">; def VariadicMethodTypeChecker : Checker<"VariadicMethodTypes">, - HelpText<"Check for passing non-Objective-C types to variadic methods that expect " - "only Objective-C types">, + HelpText<"Check for passing non-Objective-C types to variadic collection " + "initialization methods that expect only Objective-C types">, DescFile<"BasicObjCFoundationChecks.cpp">; def NSAutoreleasePoolChecker : Checker<"NSAutoreleasePool">, @@ -393,6 +393,10 @@ def ObjCSelfInitChecker : Checker<"SelfInit">, HelpText<"Check that 'self' is properly initialized inside an initializer method">, DescFile<"ObjCSelfInitChecker.cpp">; +def ObjCLoopChecker : Checker<"Loops">, + HelpText<"Improved modeling of loops using Cocoa collection types">, + DescFile<"BasicObjCFoundationChecks.cpp">; + def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, DescFile<"NSErrorChecker.cpp">; @@ -401,7 +405,7 @@ def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, DescFile<"RetainCountChecker.cpp">; -} // end "cocoa" +} // end "osx.cocoa" let ParentPackage = CocoaExperimental in { @@ -475,6 +479,14 @@ def CallGraphDumper : Checker<"DumpCallGraph">, HelpText<"Display Call Graph">, DescFile<"DebugCheckers.cpp">; +def TraversalDumper : Checker<"DumpTraversal">, + HelpText<"Print branch conditions as they are traversed by the engine">, + DescFile<"TraversalChecker.cpp">; + +def CallDumper : Checker<"DumpCalls">, + HelpText<"Print calls as they are traversed by the engine">, + DescFile<"TraversalChecker.cpp">; + def AnalyzerStatsChecker : Checker<"Stats">, HelpText<"Emit warnings with analyzer statistics">, DescFile<"AnalyzerStatsChecker.cpp">; @@ -483,5 +495,9 @@ def TaintTesterChecker : Checker<"TaintTest">, HelpText<"Mark tainted symbols as such.">, DescFile<"TaintTesterChecker.cpp">; +def ExprInspectionChecker : Checker<"ExprInspection">, + HelpText<"Check the analyzer's understanding of expressions">, + DescFile<"ExprInspectionChecker.cpp">; + } // end "debug" diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 81a2745..e98c131 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -26,13 +26,18 @@ using namespace ento; namespace { class DereferenceChecker : public Checker< check::Location, - EventDispatcher<ImplicitNullDerefEvent> > { + check::Bind, + EventDispatcher<ImplicitNullDerefEvent> > { mutable OwningPtr<BuiltinBug> BT_null; mutable OwningPtr<BuiltinBug> BT_undef; + void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, + bool IsBind = false) const; + public: void checkLocation(SVal location, bool isLoad, const Stmt* S, CheckerContext &C) const; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; static const MemRegion *AddDerefSource(raw_ostream &os, SmallVectorImpl<SourceRange> &Ranges, @@ -76,6 +81,106 @@ DereferenceChecker::AddDerefSource(raw_ostream &os, return sourceR; } +void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, + CheckerContext &C, bool IsBind) const { + // Generate an error node. + ExplodedNode *N = C.generateSink(State); + if (!N) + return; + + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null.reset(new BuiltinBug("Dereference of null pointer")); + + SmallString<100> buf; + SmallVector<SourceRange, 2> Ranges; + + // Walk through lvalue casts to get the original expression + // that syntactically caused the load. + if (const Expr *expr = dyn_cast<Expr>(S)) + S = expr->IgnoreParenLValueCasts(); + + const MemRegion *sourceR = 0; + + if (IsBind) { + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) { + if (BO->isAssignmentOp()) + S = BO->getRHS(); + } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + assert(DS->isSingleDecl() && "We process decls one by one"); + if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) + if (const Expr *Init = VD->getAnyInitializer()) + S = Init; + } + } + + switch (S->getStmtClass()) { + case Stmt::ArraySubscriptExprClass: { + llvm::raw_svector_ostream os(buf); + os << "Array access"; + const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); + sourceR = AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), + State.getPtr(), N->getLocationContext()); + os << " results in a null pointer dereference"; + break; + } + case Stmt::UnaryOperatorClass: { + llvm::raw_svector_ostream os(buf); + os << "Dereference of null pointer"; + const UnaryOperator *U = cast<UnaryOperator>(S); + sourceR = AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), + State.getPtr(), N->getLocationContext(), true); + break; + } + case Stmt::MemberExprClass: { + const MemberExpr *M = cast<MemberExpr>(S); + if (M->isArrow()) { + llvm::raw_svector_ostream os(buf); + os << "Access to field '" << M->getMemberNameInfo() + << "' results in a dereference of a null pointer"; + sourceR = AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), + State.getPtr(), N->getLocationContext(), true); + } + break; + } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); + if (const DeclRefExpr *DR = + dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + llvm::raw_svector_ostream os(buf); + os << "Instance variable access (via '" << VD->getName() + << "') results in a null pointer dereference"; + } + } + Ranges.push_back(IV->getSourceRange()); + break; + } + default: + break; + } + + BugReport *report = + new BugReport(*BT_null, + buf.empty() ? BT_null->getDescription() : buf.str(), + N); + + bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), + report); + + for (SmallVectorImpl<SourceRange>::iterator + I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) + report->addRange(*I); + + if (sourceR) { + report->markInteresting(sourceR); + report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR))); + } + + C.EmitReport(report); +} + void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. @@ -86,8 +191,10 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDerefExpr(N), report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, + bugreporter::GetDerefExpr(N), + report); + report->disablePathPruning(); C.EmitReport(report); } return; @@ -100,115 +207,81 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, return; ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); + ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { - // Generate an error node. - ExplodedNode *N = C.generateSink(nullState); - if (!N) - return; - - // We know that 'location' cannot be non-null. This is what - // we call an "explicit" null dereference. - if (!BT_null) - BT_null.reset(new BuiltinBug("Dereference of null pointer")); - - SmallString<100> buf; - SmallVector<SourceRange, 2> Ranges; - - // Walk through lvalue casts to get the original expression - // that syntactically caused the load. - if (const Expr *expr = dyn_cast<Expr>(S)) - S = expr->IgnoreParenLValueCasts(); - - const MemRegion *sourceR = 0; - - switch (S->getStmtClass()) { - case Stmt::ArraySubscriptExprClass: { - llvm::raw_svector_ostream os(buf); - os << "Array access"; - const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); - sourceR = - AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), - state.getPtr(), LCtx); - os << " results in a null pointer dereference"; - break; - } - case Stmt::UnaryOperatorClass: { - llvm::raw_svector_ostream os(buf); - os << "Dereference of null pointer"; - const UnaryOperator *U = cast<UnaryOperator>(S); - sourceR = - AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), - state.getPtr(), LCtx, true); - break; - } - case Stmt::MemberExprClass: { - const MemberExpr *M = cast<MemberExpr>(S); - if (M->isArrow()) { - llvm::raw_svector_ostream os(buf); - os << "Access to field '" << M->getMemberNameInfo() - << "' results in a dereference of a null pointer"; - sourceR = - AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), - state.getPtr(), LCtx, true); - } - break; - } - case Stmt::ObjCIvarRefExprClass: { - const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); - if (const DeclRefExpr *DR = - dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - llvm::raw_svector_ostream os(buf); - os << "Instance variable access (via '" << VD->getName() - << "') results in a null pointer dereference"; - } - } - Ranges.push_back(IV->getSourceRange()); - break; - } - default: - break; - } + reportBug(nullState, S, C); + return; + } - BugReport *report = - new BugReport(*BT_null, - buf.empty() ? BT_null->getDescription():buf.str(), - N); + // Otherwise, we have the case where the location could either be + // null or not-null. Record the error node as an "implicit" null + // dereference. + if (ExplodedNode *N = C.generateSink(nullState)) { + ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; + dispatchEvent(event); + } + } - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDerefExpr(N), report)); + // From this point forward, we know that the location is not null. + C.addTransition(notNullState); +} - for (SmallVectorImpl<SourceRange>::iterator - I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) - report->addRange(*I); +void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + // If we're binding to a reference, check if the value is known to be null. + if (V.isUndef()) + return; - if (sourceR) { - report->markInteresting(sourceR); - report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR))); - } + const MemRegion *MR = L.getAsRegion(); + const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); + if (!TVR) + return; - C.EmitReport(report); + if (!TVR->getValueType()->isReferenceType()) + return; + + ProgramStateRef State = C.getState(); + + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); + + if (StNull) { + if (!StNonNull) { + reportBug(StNull, S, C, /*isBind=*/true); return; } - else { - // Otherwise, we have the case where the location could either be - // null or not-null. Record the error node as an "implicit" null - // dereference. - if (ExplodedNode *N = C.generateSink(nullState)) { - ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; - dispatchEvent(event); - } + + // At this point the value could be either null or non-null. + // Record this as an "implicit" null dereference. + if (ExplodedNode *N = C.generateSink(StNull)) { + ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, + &C.getBugReporter() }; + dispatchEvent(event); } } - // From this point forward, we know that the location is not null. - C.addTransition(notNullState); + // Unlike a regular null dereference, initializing a reference with a + // dereferenced null pointer does not actually cause a runtime exception in + // Clang's implementation of references. + // + // int &r = *p; // safe?? + // if (p != NULL) return; // uh-oh + // r = 5; // trap here + // + // The standard says this is invalid as soon as we try to create a "null + // reference" (there is no such thing), but turning this into an assumption + // that 'p' is never null will not match our actual runtime behavior. + // So we do not record this assumption, allowing us to warn on the last line + // of this example. + // + // We do need to add a transition because we may have generated a sink for + // the "implicit" null dereference. + C.addTransition(State, this); } void ento::registerDereferenceChecker(CheckerManager &mgr) { diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 2627f0c..dcf6a86 100644 --- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -42,8 +42,9 @@ void DivZeroChecker::reportBug(const char *Msg, BugReport *R = new BugReport(*BT, Msg, N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDenomExpr(N), R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, + bugreporter::GetDenomExpr(N), + R); C.EmitReport(R); } } @@ -57,8 +58,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, Op != BO_RemAssign) return; - if (!B->getRHS()->getType()->isIntegerType() || - !B->getRHS()->getType()->isScalarType()) + if (!B->getRHS()->getType()->isScalarType()) return; SVal Denom = C.getState()->getSVal(B->getRHS(), C.getLocationContext()); diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp new file mode 100644 index 0000000..fea5733 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -0,0 +1,179 @@ +//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker defines the rules for dynamic type gathering and propagation. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { +class DynamicTypePropagation: + public Checker< check::PostCall, + check::PostStmt<ImplicitCastExpr> > { + const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const; + + /// \brief Return a better dynamic type if one can be derived from the cast. + const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, + CheckerContext &C) const; +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; +}; +} + +void DynamicTypePropagation::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // We can obtain perfect type info for return values from some calls. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { + + // Get the returned value if it's a region. + SVal Result = C.getSVal(Call.getOriginExpr()); + const MemRegion *RetReg = Result.getAsRegion(); + if (!RetReg) + return; + + ProgramStateRef State = C.getState(); + + switch (Msg->getMethodFamily()) { + default: + break; + + // We assume that the type of the object returned by alloc and new are the + // pointer to the object of the class specified in the receiver of the + // message. + case OMF_alloc: + case OMF_new: { + // Get the type of object that will get created. + const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); + const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); + if (!ObjTy) + return; + QualType DynResTy = + C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); + C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); + break; + } + case OMF_init: { + // Assume, the result of the init method has the same dynamic type as + // the receiver and propagate the dynamic type info. + const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); + if (!RecReg) + return; + DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); + C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); + break; + } + } + } +} + +void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, + CheckerContext &C) const { + // We only track dynamic type info for regions. + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + if (!ToR) + return; + + switch (CastE->getCastKind()) { + default: + break; + case CK_BitCast: + // Only handle ObjCObjects for now. + if (const Type *NewTy = getBetterObjCType(CastE, C)) + C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0))); + break; + } + return; +} + +const ObjCObjectType * +DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const { + if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) { + if (const ObjCObjectType *ObjTy + = MsgE->getClassReceiver()->getAs<ObjCObjectType>()) + return ObjTy; + } + + if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) { + if (const ObjCObjectType *ObjTy + = MsgE->getSuperType()->getAs<ObjCObjectType>()) + return ObjTy; + } + + const Expr *RecE = MsgE->getInstanceReceiver(); + if (!RecE) + return 0; + + RecE= RecE->IgnoreParenImpCasts(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) { + const StackFrameContext *SFCtx = C.getStackFrame(); + // Are we calling [self alloc]? If this is self, get the type of the + // enclosing ObjC class. + if (DRE->getDecl() == SFCtx->getSelfDecl()) { + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl())) + if (const ObjCObjectType *ObjTy = + dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl())) + return ObjTy; + } + } + return 0; +} + +// Return a better dynamic type if one can be derived from the cast. +// Compare the current dynamic type of the region and the new type to which we +// are casting. If the new type is lower in the inheritance hierarchy, pick it. +const ObjCObjectPointerType * +DynamicTypePropagation::getBetterObjCType(const Expr *CastE, + CheckerContext &C) const { + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + assert(ToR); + + // Get the old and new types. + const ObjCObjectPointerType *NewTy = + CastE->getType()->getAs<ObjCObjectPointerType>(); + if (!NewTy) + return 0; + QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType(); + if (OldDTy.isNull()) { + return NewTy; + } + const ObjCObjectPointerType *OldTy = + OldDTy->getAs<ObjCObjectPointerType>(); + if (!OldTy) + return 0; + + // Id the old type is 'id', the new one is more precise. + if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) + return NewTy; + + // Return new if it's a subclass of old. + const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); + const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); + if (ToI && FromI && FromI->isSuperClassOf(ToI)) + return NewTy; + + return 0; +} + +void ento::registerDynamicTypePropagation(CheckerManager &mgr) { + mgr.registerChecker<DynamicTypePropagation>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp new file mode 100644 index 0000000..7acf223 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -0,0 +1,122 @@ +//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class ExprInspectionChecker : public Checker< eval::Call > { + mutable OwningPtr<BugType> BT; + + void analyzerEval(const CallExpr *CE, CheckerContext &C) const; + void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; + + typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, + CheckerContext &C) const; + +public: + bool evalCall(const CallExpr *CE, CheckerContext &C) const; +}; +} + +bool ExprInspectionChecker::evalCall(const CallExpr *CE, + CheckerContext &C) const { + // These checks should have no effect on the surrounding environment + // (globals should not be invalidated, etc), hence the use of evalCall. + FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) + .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) + .Case("clang_analyzer_checkInlined", + &ExprInspectionChecker::analyzerCheckInlined) + .Default(0); + + if (!Handler) + return false; + + (this->*Handler)(CE, C); + return true; +} + +static const char *getArgumentValueString(const CallExpr *CE, + CheckerContext &C) { + if (CE->getNumArgs() == 0) + return "Missing assertion argument"; + + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + ProgramStateRef State = N->getState(); + + const Expr *Assertion = CE->getArg(0); + SVal AssertionVal = State->getSVal(Assertion, LC); + + if (AssertionVal.isUndef()) + return "UNDEFINED"; + + ProgramStateRef StTrue, StFalse; + llvm::tie(StTrue, StFalse) = + State->assume(cast<DefinedOrUnknownSVal>(AssertionVal)); + + if (StTrue) { + if (StFalse) + return "UNKNOWN"; + else + return "TRUE"; + } else { + if (StFalse) + return "FALSE"; + else + llvm_unreachable("Invalid constraint; neither true or false."); + } +} + +void ExprInspectionChecker::analyzerEval(const CallExpr *CE, + CheckerContext &C) const { + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + + // A specific instantiation of an inlined function may have more constrained + // values than can generally be assumed. Skip the check. + if (LC->getCurrentStackFrame()->getParent() != 0) + return; + + if (!BT) + BT.reset(new BugType("Checking analyzer assumptions", "debug")); + + BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); + C.EmitReport(R); +} + +void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, + CheckerContext &C) const { + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + + // An inlined function could conceivably also be analyzed as a top-level + // function. We ignore this case and only emit a message (TRUE or FALSE) + // when we are analyzing it as an inlined function. This means that + // clang_analyzer_checkInlined(true) should always print TRUE, but + // clang_analyzer_checkInlined(false) should never actually print anything. + if (LC->getCurrentStackFrame()->getParent() == 0) + return; + + if (!BT) + BT.reset(new BugType("Checking analyzer assumptions", "debug")); + + BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); + C.EmitReport(R); +} + +void ento::registerExprInspectionChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ExprInspectionChecker>(); +} + diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 135b81d..afb862c 100644 --- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -273,7 +273,7 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( // Skipping the following functions, since they might be used for cleansing // or smart memory copy: - // - memccpy - copying untill hitting a special character. + // - memccpy - copying until hitting a special character. return TaintPropagationRule(); } @@ -299,6 +299,9 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = 0; const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return; + StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return; @@ -372,7 +375,11 @@ void GenericTaintChecker::addSourcesPost(const CallExpr *CE, CheckerContext &C) const { // Define the attack surface. // Set the evaluation function by switching on the callee name. - StringRef Name = C.getCalleeName(CE); + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return; + + StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return; FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) @@ -406,6 +413,9 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{ return true; const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return false; + StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return false; @@ -549,7 +559,6 @@ ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE, if (CE->getNumArgs() < 2) return State; - SVal x = State->getSVal(CE->getArg(1), C.getLocationContext()); // All arguments except for the very first one should get taint. for (unsigned int i = 1; i < CE->getNumArgs(); ++i) { // The arguments are pointer arguments. The data they are pointing at is diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index c08f163..9d0b83f 100644 --- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -106,6 +106,7 @@ private: typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData> AssumptionMap; mutable AssumptionMap hash; + mutable OwningPtr<BugType> BT; }; } @@ -343,7 +344,9 @@ void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B, void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { - BugType *BT = new BugType("Idempotent operation", "Dead code"); + if (!BT) + BT.reset(new BugType("Idempotent operation", "Dead code")); + // Iterate over the hash to see if we have any paths with definite // idempotent operations. for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { diff --git a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp deleted file mode 100644 index b0bac33..0000000 --- a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp +++ /dev/null @@ -1,603 +0,0 @@ -//=== IteratorsChecker.cpp - Check for Invalidated Iterators ------*- C++ -*---- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines IteratorsChecker, a number of small checks for conditions -// leading to invalid iterators being used. -// FIXME: Currently only supports 'vector' and 'deque' -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/DeclTemplate.h" -#include "clang/Basic/SourceManager.h" -#include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/ExprCXX.h" -#include "clang/AST/Type.h" -#include "clang/AST/PrettyPrinter.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/StringSwitch.h" - - -using namespace clang; -using namespace ento; - -// This is the state associated with each iterator which includes both the -// kind of state and the instance used to initialize it. -// FIXME: add location where invalidated for better error reporting. -namespace { -class RefState { - enum Kind { BeginValid, EndValid, Invalid, Undefined, Unknown } K; - const void *VR; - -public: - RefState(Kind k, const void *vr) : K(k), VR(vr) {} - - bool isValid() const { return K == BeginValid || K == EndValid; } - bool isInvalid() const { return K == Invalid; } - bool isUndefined() const { return K == Undefined; } - bool isUnknown() const { return K == Unknown; } - const MemRegion *getMemRegion() const { - if (K == BeginValid || K == EndValid) - return(const MemRegion *)VR; - return 0; - } - const MemberExpr *getMemberExpr() const { - if (K == Invalid) - return(const MemberExpr *)VR; - return 0; - } - - bool operator==(const RefState &X) const { - return K == X.K && VR == X.VR; - } - - static RefState getBeginValid(const MemRegion *vr) { - assert(vr); - return RefState(BeginValid, vr); - } - static RefState getEndValid(const MemRegion *vr) { - assert(vr); - return RefState(EndValid, vr); - } - static RefState getInvalid( const MemberExpr *ME ) { - return RefState(Invalid, ME); - } - static RefState getUndefined( void ) { - return RefState(Undefined, 0); - } - static RefState getUnknown( void ) { - return RefState(Unknown, 0); - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(K); - ID.AddPointer(VR); - } -}; - -enum RefKind { NoKind, VectorKind, VectorIteratorKind }; - -class IteratorsChecker : - public Checker<check::PreStmt<CXXOperatorCallExpr>, - check::PreStmt<DeclStmt>, - check::PreStmt<CXXMemberCallExpr>, - check::PreStmt<CallExpr> > - { - // Used when parsing iterators and vectors and deques. - BuiltinBug *BT_Invalid, *BT_Undefined, *BT_Incompatible; - -public: - IteratorsChecker() : - BT_Invalid(0), BT_Undefined(0), BT_Incompatible(0) - {} - static void *getTag() { static int tag; return &tag; } - - // Checker entry points. - void checkPreStmt(const CXXOperatorCallExpr *OCE, - CheckerContext &C) const; - - void checkPreStmt(const DeclStmt *DS, - CheckerContext &C) const; - - void checkPreStmt(const CXXMemberCallExpr *MCE, - CheckerContext &C) const; - - void checkPreStmt(const CallExpr *CE, - CheckerContext &C) const; - -private: - ProgramStateRef handleAssign(ProgramStateRef state, - const Expr *lexp, - const Expr *rexp, - const LocationContext *LC) const; - - ProgramStateRef handleAssign(ProgramStateRef state, - const MemRegion *MR, - const Expr *rexp, - const LocationContext *LC) const; - - ProgramStateRef invalidateIterators(ProgramStateRef state, - const MemRegion *MR, - const MemberExpr *ME) const; - - void checkExpr(CheckerContext &C, const Expr *E) const; - - void checkArgs(CheckerContext &C, const CallExpr *CE) const; - - const MemRegion *getRegion(ProgramStateRef state, - const Expr *E, - const LocationContext *LC) const; - - const DeclRefExpr *getDeclRefExpr(const Expr *E) const; -}; - -class IteratorState { -public: - typedef llvm::ImmutableMap<const MemRegion *, RefState> EntryMap; -}; -} //end anonymous namespace - -namespace clang { - namespace ento { - template <> - struct ProgramStateTrait<IteratorState> - : public ProgramStatePartialTrait<IteratorState::EntryMap> { - static void *GDMIndex() { return IteratorsChecker::getTag(); } - }; - } -} - -void ento::registerIteratorsChecker(CheckerManager &mgr) { - mgr.registerChecker<IteratorsChecker>(); -} - -// =============================================== -// Utility functions used by visitor functions -// =============================================== - -// check a templated type for std::vector or std::deque -static RefKind getTemplateKind(const NamedDecl *td) { - const DeclContext *dc = td->getDeclContext(); - const NamespaceDecl *nameSpace = dyn_cast<NamespaceDecl>(dc); - if (!nameSpace || !isa<TranslationUnitDecl>(nameSpace->getDeclContext()) - || nameSpace->getName() != "std") - return NoKind; - - StringRef name = td->getName(); - return llvm::StringSwitch<RefKind>(name) - .Cases("vector", "deque", VectorKind) - .Default(NoKind); -} - -static RefKind getTemplateKind(const DeclContext *dc) { - if (const ClassTemplateSpecializationDecl *td = - dyn_cast<ClassTemplateSpecializationDecl>(dc)) - return getTemplateKind(cast<NamedDecl>(td)); - return NoKind; -} - -static RefKind getTemplateKind(const TypedefType *tdt) { - const TypedefNameDecl *td = tdt->getDecl(); - RefKind parentKind = getTemplateKind(td->getDeclContext()); - if (parentKind == VectorKind) { - return llvm::StringSwitch<RefKind>(td->getName()) - .Cases("iterator", - "const_iterator", - "reverse_iterator", VectorIteratorKind) - .Default(NoKind); - } - return NoKind; -} - -static RefKind getTemplateKind(const TemplateSpecializationType *tsp) { - const TemplateName &tname = tsp->getTemplateName(); - TemplateDecl *td = tname.getAsTemplateDecl(); - if (!td) - return NoKind; - return getTemplateKind(td); -} - -static RefKind getTemplateKind(QualType T) { - if (const TemplateSpecializationType *tsp = - T->getAs<TemplateSpecializationType>()) { - return getTemplateKind(tsp); - } - if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(T)) { - QualType namedType = ET->getNamedType(); - if (const TypedefType *tdt = namedType->getAs<TypedefType>()) - return getTemplateKind(tdt); - if (const TemplateSpecializationType *tsp = - namedType->getAs<TemplateSpecializationType>()) { - return getTemplateKind(tsp); - } - } - return NoKind; -} - -// Iterate through our map and invalidate any iterators that were -// initialized fromt the specified instance MemRegion. -ProgramStateRef IteratorsChecker::invalidateIterators(ProgramStateRef state, - const MemRegion *MR, const MemberExpr *ME) const { - IteratorState::EntryMap Map = state->get<IteratorState>(); - if (Map.isEmpty()) - return state; - - // Loop over the entries in the current state. - // The key doesn't change, so the map iterators won't change. - for (IteratorState::EntryMap::iterator I = Map.begin(), E = Map.end(); - I != E; ++I) { - RefState RS = I.getData(); - if (RS.getMemRegion() == MR) - state = state->set<IteratorState>(I.getKey(), RefState::getInvalid(ME)); - } - - return state; -} - -// Handle assigning to an iterator where we don't have the LValue MemRegion. -ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state, - const Expr *lexp, const Expr *rexp, const LocationContext *LC) const { - // Skip the cast if present. - if (const MaterializeTemporaryExpr *M - = dyn_cast<MaterializeTemporaryExpr>(lexp)) - lexp = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(lexp)) - lexp = ICE->getSubExpr(); - SVal sv = state->getSVal(lexp, LC); - const MemRegion *MR = sv.getAsRegion(); - if (!MR) - return state; - RefKind kind = getTemplateKind(lexp->getType()); - - // If assigning to a vector, invalidate any iterators currently associated. - if (kind == VectorKind) - return invalidateIterators(state, MR, 0); - - // Make sure that we are assigning to an iterator. - if (getTemplateKind(lexp->getType()) != VectorIteratorKind) - return state; - return handleAssign(state, MR, rexp, LC); -} - -// handle assigning to an iterator -ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state, - const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const { - // Assume unknown until we find something definite. - state = state->set<IteratorState>(MR, RefState::getUnknown()); - if (const MaterializeTemporaryExpr *M - = dyn_cast<MaterializeTemporaryExpr>(rexp)) - rexp = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(rexp)) - rexp = ICE->getSubExpr(); - // Need to handle three cases: MemberCall, copy, copy with addition. - if (const CallExpr *CE = dyn_cast<CallExpr>(rexp)) { - // Handle MemberCall. - if (const MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee())) { - const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase()); - if (!DRE) - return state; - // Verify that the type is std::vector<T>. - if (getTemplateKind(DRE->getType()) != VectorKind) - return state; - // Now get the MemRegion associated with the instance. - const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) - return state; - const MemRegion *IMR = state->getRegion(VD, LC); - if (!IMR) - return state; - // Finally, see if it is one of the calls that will create - // a valid iterator and mark it if so, else mark as Unknown. - StringRef mName = ME->getMemberDecl()->getName(); - - if (llvm::StringSwitch<bool>(mName) - .Cases("begin", "insert", "erase", true).Default(false)) { - return state->set<IteratorState>(MR, RefState::getBeginValid(IMR)); - } - if (mName == "end") - return state->set<IteratorState>(MR, RefState::getEndValid(IMR)); - - return state->set<IteratorState>(MR, RefState::getUnknown()); - } - } - // Handle straight copy from another iterator. - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(rexp)) { - if (getTemplateKind(DRE->getType()) != VectorIteratorKind) - return state; - // Now get the MemRegion associated with the instance. - const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) - return state; - const MemRegion *IMR = state->getRegion(VD, LC); - if (!IMR) - return state; - // Get the RefState of the iterator being copied. - const RefState *RS = state->get<IteratorState>(IMR); - if (!RS) - return state; - // Use it to set the state of the LValue. - return state->set<IteratorState>(MR, *RS); - } - // If we have operator+ or operator- ... - if (const CXXOperatorCallExpr *OCE = dyn_cast<CXXOperatorCallExpr>(rexp)) { - OverloadedOperatorKind Kind = OCE->getOperator(); - if (Kind == OO_Plus || Kind == OO_Minus) { - // Check left side of tree for a valid value. - state = handleAssign( state, MR, OCE->getArg(0), LC); - const RefState *RS = state->get<IteratorState>(MR); - // If found, return it. - if (!RS->isUnknown()) - return state; - // Otherwise return what we find in the right side. - return handleAssign(state, MR, OCE->getArg(1), LC); - } - } - // Fall through if nothing matched. - return state; -} - -// Iterate through the arguments looking for an Invalid or Undefined iterator. -void IteratorsChecker::checkArgs(CheckerContext &C, const CallExpr *CE) const { - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) { - checkExpr(C, *I); - } -} - -// Get the DeclRefExpr associated with the expression. -const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const { - // If it is a CXXConstructExpr, need to get the subexpression. - if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) { - if (CE->getNumArgs()== 1) { - CXXConstructorDecl *CD = CE->getConstructor(); - if (CD->isTrivial()) - E = CE->getArg(0); - } - } - if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E)) - E = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) - E = ICE->getSubExpr(); - // If it isn't one of our types, don't do anything. - if (getTemplateKind(E->getType()) != VectorIteratorKind) - return NULL; - return dyn_cast<DeclRefExpr>(E); -} - -// Get the MemRegion associated with the expresssion. -const MemRegion *IteratorsChecker::getRegion(ProgramStateRef state, - const Expr *E, const LocationContext *LC) const { - const DeclRefExpr *DRE = getDeclRefExpr(E); - if (!DRE) - return NULL; - const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) - return NULL; - // return the MemRegion associated with the iterator - return state->getRegion(VD, LC); -} - -// Check the expression and if it is an iterator, generate a diagnostic -// if the iterator is not valid. -// FIXME: this method can generate new nodes, and subsequent logic should -// use those nodes. We also cannot create multiple nodes at one ProgramPoint -// with the same tag. -void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { - ProgramStateRef state = C.getState(); - const MemRegion *MR = getRegion(state, E, C.getLocationContext()); - if (!MR) - return; - - // Get the state associated with the iterator. - const RefState *RS = state->get<IteratorState>(MR); - if (!RS) - return; - if (RS->isInvalid()) { - if (ExplodedNode *N = C.addTransition()) { - if (!BT_Invalid) - // FIXME: We are eluding constness here. - const_cast<IteratorsChecker*>(this)->BT_Invalid = new BuiltinBug(""); - - std::string msg; - const MemberExpr *ME = RS->getMemberExpr(); - if (ME) { - std::string name = ME->getMemberNameInfo().getAsString(); - msg = "Attempt to use an iterator made invalid by call to '" + - name + "'"; - } - else { - msg = "Attempt to use an iterator made invalid by copying another " - "container to its container"; - } - - BugReport *R = new BugReport(*BT_Invalid, msg, N); - R->addRange(getDeclRefExpr(E)->getSourceRange()); - C.EmitReport(R); - } - } - else if (RS->isUndefined()) { - if (ExplodedNode *N = C.addTransition()) { - if (!BT_Undefined) - // FIXME: We are eluding constness here. - const_cast<IteratorsChecker*>(this)->BT_Undefined = - new BuiltinBug("Use of iterator that is not defined"); - - BugReport *R = new BugReport(*BT_Undefined, - BT_Undefined->getDescription(), N); - R->addRange(getDeclRefExpr(E)->getSourceRange()); - C.EmitReport(R); - } - } -} - -// =============================================== -// Path analysis visitor functions -// =============================================== - -// For a generic Call, just check the args for bad iterators. -void IteratorsChecker::checkPreStmt(const CallExpr *CE, - CheckerContext &C) const{ - - // FIXME: These checks are to currently work around a bug - // in CheckerManager. - if (isa<CXXOperatorCallExpr>(CE)) - return; - if (isa<CXXMemberCallExpr>(CE)) - return; - - checkArgs(C, CE); -} - -// Handle operator calls. First, if it is operator=, check the argument, -// and handle assigning and set target state appropriately. Otherwise, for -// other operators, check the args for bad iterators and handle comparisons. -void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, - CheckerContext &C) const -{ - const LocationContext *LC = C.getLocationContext(); - ProgramStateRef state = C.getState(); - OverloadedOperatorKind Kind = OCE->getOperator(); - if (Kind == OO_Equal) { - checkExpr(C, OCE->getArg(1)); - state = handleAssign(state, OCE->getArg(0), OCE->getArg(1), LC); - C.addTransition(state); - return; - } - else { - checkArgs(C, OCE); - // If it is a compare and both are iterators, ensure that they are for - // the same container. - if (Kind == OO_EqualEqual || Kind == OO_ExclaimEqual || - Kind == OO_Less || Kind == OO_LessEqual || - Kind == OO_Greater || Kind == OO_GreaterEqual) { - const MemRegion *MR0, *MR1; - MR0 = getRegion(state, OCE->getArg(0), LC); - if (!MR0) - return; - MR1 = getRegion(state, OCE->getArg(1), LC); - if (!MR1) - return; - const RefState *RS0, *RS1; - RS0 = state->get<IteratorState>(MR0); - if (!RS0) - return; - RS1 = state->get<IteratorState>(MR1); - if (!RS1) - return; - if (RS0->getMemRegion() != RS1->getMemRegion()) { - if (ExplodedNode *N = C.addTransition()) { - if (!BT_Incompatible) - const_cast<IteratorsChecker*>(this)->BT_Incompatible = - new BuiltinBug( - "Cannot compare iterators from different containers"); - - BugReport *R = new BugReport(*BT_Incompatible, - BT_Incompatible->getDescription(), N); - R->addRange(OCE->getSourceRange()); - C.EmitReport(R); - } - } - } - } -} - -// Need to handle DeclStmts to pick up initializing of iterators and to mark -// uninitialized ones as Undefined. -void IteratorsChecker::checkPreStmt(const DeclStmt *DS, - CheckerContext &C) const { - const Decl *D = *DS->decl_begin(); - const VarDecl *VD = dyn_cast<VarDecl>(D); - // Only care about iterators. - if (getTemplateKind(VD->getType()) != VectorIteratorKind) - return; - - // Get the MemRegion associated with the iterator and mark it as Undefined. - ProgramStateRef state = C.getState(); - Loc VarLoc = state->getLValue(VD, C.getLocationContext()); - const MemRegion *MR = VarLoc.getAsRegion(); - if (!MR) - return; - state = state->set<IteratorState>(MR, RefState::getUndefined()); - - // if there is an initializer, handle marking Valid if a proper initializer - const Expr *InitEx = VD->getInit(); - if (InitEx) { - // FIXME: This is too syntactic. Since 'InitEx' will be analyzed first - // it should resolve to an SVal that we can check for validity - // *semantically* instead of walking through the AST. - if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(InitEx)) { - if (CE->getNumArgs() == 1) { - const Expr *E = CE->getArg(0); - if (const MaterializeTemporaryExpr *M - = dyn_cast<MaterializeTemporaryExpr>(E)) - E = M->GetTemporaryExpr(); - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) - InitEx = ICE->getSubExpr(); - state = handleAssign(state, MR, InitEx, C.getLocationContext()); - } - } - } - C.addTransition(state); -} - - -namespace { struct CalledReserved {}; } -namespace clang { namespace ento { -template<> struct ProgramStateTrait<CalledReserved> - : public ProgramStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { - static void *GDMIndex() { static int index = 0; return &index; } -}; -}} - -// on a member call, first check the args for any bad iterators -// then, check to see if it is a call to a function that will invalidate -// the iterators -void IteratorsChecker::checkPreStmt(const CXXMemberCallExpr *MCE, - CheckerContext &C) const { - // Check the arguments. - checkArgs(C, MCE); - const MemberExpr *ME = dyn_cast<MemberExpr>(MCE->getCallee()); - if (!ME) - return; - // Make sure we have the right kind of container. - const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase()); - if (!DRE || getTemplateKind(DRE->getType()) != VectorKind) - return; - SVal tsv = C.getState()->getSVal(DRE, C.getLocationContext()); - // Get the MemRegion associated with the container instance. - const MemRegion *MR = tsv.getAsRegion(); - if (!MR) - return; - // If we are calling a function that invalidates iterators, mark them - // appropriately by finding matching instances. - ProgramStateRef state = C.getState(); - StringRef mName = ME->getMemberDecl()->getName(); - if (llvm::StringSwitch<bool>(mName) - .Cases("insert", "reserve", "push_back", true) - .Cases("erase", "pop_back", "clear", "resize", true) - .Default(false)) { - // If there was a 'reserve' call, assume iterators are good. - if (!state->contains<CalledReserved>(MR)) - state = invalidateIterators(state, MR, ME); - } - // Keep track of instances that have called 'reserve' - // note: do this after we invalidate any iterators by calling - // 'reserve' itself. - if (mName == "reserve") - state = state->add<CalledReserved>(MR); - - if (state != C.getState()) - C.addTransition(state); -} - diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index cb976e0..969f2dd 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -290,7 +290,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, unsigned idx = InvalidIdx; ProgramStateRef State = C.getState(); - StringRef funName = C.getCalleeName(CE); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; @@ -446,7 +450,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = C.getState(); - StringRef funName = C.getCalleeName(CE); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); // If a value has been allocated, add it to the set for tracking. unsigned idx = getTrackedFunctionIndex(funName, true); @@ -528,9 +536,11 @@ MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N, } ProgramPoint P = AllocNode->getLocation(); - if (!isa<StmtPoint>(P)) - return 0; - return cast<clang::PostStmt>(P).getStmt(); + if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + return Exit->getCalleeContext()->getCallSite(); + if (clang::PostStmt *PS = dyn_cast<clang::PostStmt>(&P)) + return PS->getStmt(); + return 0; } BugReport *MacOSKeychainAPIChecker:: diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 8bce88a..dfcedf6 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -18,7 +18,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" @@ -34,15 +34,21 @@ using namespace ento; namespace { class RefState { - enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped, + enum Kind { // Reference to allocated memory. + Allocated, + // Reference to released/freed memory. + Released, + // The responsibility for freeing resources has transfered from + // this reference. A relinquished symbol should not be freed. Relinquished } K; const Stmt *S; public: RefState(Kind k, const Stmt *s) : K(k), S(s) {} - bool isAllocated() const { return K == AllocateUnchecked; } + bool isAllocated() const { return K == Allocated; } bool isReleased() const { return K == Released; } + bool isRelinquished() const { return K == Relinquished; } const Stmt *getStmt() const { return S; } @@ -50,14 +56,10 @@ public: return K == X.K && S == X.S; } - static RefState getAllocateUnchecked(const Stmt *s) { - return RefState(AllocateUnchecked, s); - } - static RefState getAllocateFailed() { - return RefState(AllocateFailed, 0); + 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); } static RefState getRelinquished(const Stmt *s) { return RefState(Relinquished, s); } @@ -90,6 +92,7 @@ class MallocChecker : public Checker<check::DeadSymbols, check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, check::PostStmt<BlockExpr>, + check::PreObjCMessage, check::Location, check::Bind, eval::Assume, @@ -117,6 +120,7 @@ public: void checkPreStmt(const CallExpr *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndPath(CheckerContext &C) const; @@ -132,17 +136,22 @@ public: const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const; + const CallEvent *Call) const; bool wantsRegionChangeUpdate(ProgramStateRef state) const { return true; } + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const; + private: void initIdentifierInfo(ASTContext &C) const; /// Check if this is one of the functions which can allocate/reallocate memory /// pointed to by one of its arguments. bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const; static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, @@ -167,20 +176,26 @@ private: ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att) const; ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, - ProgramStateRef state, unsigned Num, - bool Hold) const; + ProgramStateRef state, unsigned Num, + bool Hold) const; + ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg, + const Expr *ParentExpr, + ProgramStateRef state, + bool Hold) const; ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE, bool FreesMemOnFailure) const; static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE); - bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const; + ///\brief Check if the memory associated with this symbol was released. + bool isReleased(SymbolRef Sym, CheckerContext &C) const; + bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S = 0) const; /// Check if the function is not known to us. So, for example, we could /// conservatively assume it can free/reallocate it's pointer arguments. - bool doesNotFreeMemory(const CallOrObjCMessage *Call, + bool doesNotFreeMemory(const CallEvent *Call, ProgramStateRef State) const; static bool SummarizeValue(raw_ostream &os, SVal V); @@ -207,15 +222,17 @@ private: // The allocated region symbol tracked by the main analysis. SymbolRef Sym; - // The mode we are in, i.e. what kind of diagnostics will be emitted. - NotificationMode Mode; + // The mode we are in, i.e. what kind of diagnostics will be emitted. + NotificationMode Mode; - // A symbol from when the primary region should have been reallocated. - SymbolRef FailedReallocSymbol; + // A symbol from when the primary region should have been reallocated. + SymbolRef FailedReallocSymbol; - public: - MallocBugVisitor(SymbolRef S) - : Sym(S), Mode(Normal), FailedReallocSymbol(0) {} + bool IsLeak; + + public: + MallocBugVisitor(SymbolRef S, bool isLeak = false) + : Sym(S), Mode(Normal), FailedReallocSymbol(0), IsLeak(isLeak) {} virtual ~MallocBugVisitor() {} @@ -239,6 +256,15 @@ private: (S && S->isReleased()) && (!SPrev || !SPrev->isReleased())); } + inline bool isRelinquished(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> relinquished. Other state (allocated) -> relinquished. + return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || + isa<ObjCPropertyRefExpr>(Stmt)) && + (S && S->isRelinquished()) && + (!SPrev || !SPrev->isRelinquished())); + } + inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, const Stmt *Stmt) { // If the expression is not a call, and the state change is @@ -253,6 +279,20 @@ private: const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR); + + PathDiagnosticPiece* getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) { + if (!IsLeak) + return 0; + + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode, + BRC.getSourceManager()); + // Do not add the statement itself as a range in case of leak. + return new PathDiagnosticEventPiece(L, BR.getDescription(), false); + } + private: class StackHintGeneratorForReallocationFailed : public StackHintGeneratorForSymbol { @@ -315,44 +355,73 @@ public: } // end anonymous namespace void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { - if (!II_malloc) - II_malloc = &Ctx.Idents.get("malloc"); - if (!II_free) - II_free = &Ctx.Idents.get("free"); - if (!II_realloc) - II_realloc = &Ctx.Idents.get("realloc"); - if (!II_reallocf) - II_reallocf = &Ctx.Idents.get("reallocf"); - if (!II_calloc) - II_calloc = &Ctx.Idents.get("calloc"); - if (!II_valloc) - II_valloc = &Ctx.Idents.get("valloc"); - if (!II_strdup) - II_strdup = &Ctx.Idents.get("strdup"); - if (!II_strndup) - II_strndup = &Ctx.Idents.get("strndup"); + if (II_malloc) + return; + II_malloc = &Ctx.Idents.get("malloc"); + II_free = &Ctx.Idents.get("free"); + II_realloc = &Ctx.Idents.get("realloc"); + II_reallocf = &Ctx.Idents.get("reallocf"); + II_calloc = &Ctx.Idents.get("calloc"); + II_valloc = &Ctx.Idents.get("valloc"); + II_strdup = &Ctx.Idents.get("strdup"); + II_strndup = &Ctx.Idents.get("strndup"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { + if (isFreeFunction(FD, C)) + return true; + + if (isAllocationFunction(FD, C)) + return true; + + return false; +} + +bool MallocChecker::isAllocationFunction(const FunctionDecl *FD, + ASTContext &C) const { if (!FD) return false; - IdentifierInfo *FunI = FD->getIdentifier(); - if (!FunI) - return false; - initIdentifierInfo(C); + if (FD->getKind() == Decl::Function) { + IdentifierInfo *FunI = FD->getIdentifier(); + initIdentifierInfo(C); - if (FunI == II_malloc || FunI == II_free || FunI == II_realloc || - FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || - FunI == II_strdup || FunI == II_strndup) - return true; + if (FunI == II_malloc || FunI == II_realloc || + FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || + FunI == II_strdup || FunI == II_strndup) + return true; + } - if (Filter.CMallocOptimistic && FD->hasAttrs() && - FD->specific_attr_begin<OwnershipAttr>() != - FD->specific_attr_end<OwnershipAttr>()) - return true; + if (Filter.CMallocOptimistic && FD->hasAttrs()) + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) + if ((*i)->getOwnKind() == OwnershipAttr::Returns) + return true; + return false; +} + +bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const { + if (!FD) + return false; + + if (FD->getKind() == Decl::Function) { + IdentifierInfo *FunI = FD->getIdentifier(); + initIdentifierInfo(C); + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) + return true; + } + if (Filter.CMallocOptimistic && FD->hasAttrs()) + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) + if ((*i)->getOwnKind() == OwnershipAttr::Takes || + (*i)->getOwnKind() == OwnershipAttr::Holds) + return true; return false; } @@ -361,29 +430,32 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { if (!FD) return; - initIdentifierInfo(C.getASTContext()); - IdentifierInfo *FunI = FD->getIdentifier(); - if (!FunI) - return; - ProgramStateRef State = C.getState(); - if (FunI == II_malloc || FunI == II_valloc) { - if (CE->getNumArgs() < 1) - return; - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - } else if (FunI == II_realloc) { - State = ReallocMem(C, CE, false); - } else if (FunI == II_reallocf) { - State = ReallocMem(C, CE, true); - } else if (FunI == II_calloc) { - State = CallocMem(C, CE); - } else if (FunI == II_free) { - State = FreeMemAux(C, CE, C.getState(), 0, false); - } else if (FunI == II_strdup) { - State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_strndup) { - State = MallocUpdateRefState(C, CE, State); - } else if (Filter.CMallocOptimistic) { + + if (FD->getKind() == Decl::Function) { + initIdentifierInfo(C.getASTContext()); + IdentifierInfo *FunI = FD->getIdentifier(); + + if (FunI == II_malloc || FunI == II_valloc) { + if (CE->getNumArgs() < 1) + return; + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } else if (FunI == II_realloc) { + State = ReallocMem(C, CE, false); + } else if (FunI == II_reallocf) { + State = ReallocMem(C, CE, true); + } else if (FunI == II_calloc) { + State = CallocMem(C, CE); + } else if (FunI == II_free) { + State = FreeMemAux(C, CE, State, 0, false); + } else if (FunI == II_strdup) { + State = MallocUpdateRefState(C, CE, State); + } else if (FunI == II_strndup) { + State = MallocUpdateRefState(C, CE, State); + } + } + + if (Filter.CMallocOptimistic) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -405,6 +477,34 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { C.addTransition(State); } +static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) { + Selector S = Call.getSelector(); + for (unsigned i = 1; i < S.getNumArgs(); ++i) + if (S.getNameForSlot(i).equals("freeWhenDone")) + if (Call.getArgSVal(i).isConstant(0)) + return true; + + return false; +} + +void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call, + CheckerContext &C) const { + // If the first selector is dataWithBytesNoCopy, assume that the memory will + // be released with 'free' by the new object. + // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; + // Unless 'freeWhenDone' param set to 0. + // TODO: Check that the memory was allocated with malloc. + Selector S = Call.getSelector(); + if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" || + S.getNameForSlot(0) == "initWithBytesNoCopy" || + S.getNameForSlot(0) == "initWithCharactersNoCopy") && + !isFreeWhenDoneSetToZero(Call)){ + unsigned int argIdx = 0; + C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx), + Call.getOriginExpr(), C.getState(), true)); + } +} + ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att) { @@ -422,19 +522,27 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, ProgramStateRef state) { - // Get the return value. - SVal retVal = state->getSVal(CE, C.getLocationContext()); + + // Bind the return value to the symbolic value from the heap region. + // TODO: We could rewrite post visit to eval call; 'malloc' does not have + // side effects other than what we model here. + unsigned Count = C.getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); + DefinedSVal RetVal = + cast<DefinedSVal>(svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)); + state = state->BindExpr(CE, C.getLocationContext(), RetVal); // We expect the malloc functions to return a pointer. - if (!isa<Loc>(retVal)) + if (!isa<Loc>(RetVal)) return 0; // Fill the region with the initialization value. - state = state->bindDefault(retVal, Init); + state = state->bindDefault(RetVal, Init); // Set the region's extent equal to the Size parameter. const SymbolicRegion *R = - dyn_cast_or_null<SymbolicRegion>(retVal.getAsRegion()); + dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion()); if (!R) return 0; if (isa<DefinedOrUnknownSVal>(Size)) { @@ -465,7 +573,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, assert(Sym); // Set the symbol's state to Allocated. - return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE)); + return state->set<RegionState>(Sym, RefState::getAllocated(CE)); } @@ -495,7 +603,15 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, if (CE->getNumArgs() < (Num + 1)) return 0; - const Expr *ArgExpr = CE->getArg(Num); + return FreeMemAux(C, CE->getArg(Num), CE, state, Hold); +} + +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, + const Expr *ArgExpr, + const Expr *ParentExpr, + ProgramStateRef state, + bool Hold) const { + SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext()); if (!isa<DefinedOrUnknownSVal>(ArgVal)) return 0; @@ -558,20 +674,15 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, SymbolRef Sym = SR->getSymbol(); const RefState *RS = state->get<RegionState>(Sym); - // If the symbol has not been tracked, return. This is possible when free() is - // called on a pointer that does not get its pointee directly from malloc(). - // Full support of this requires inter-procedural analysis. - if (!RS) - return 0; - // Check double free. - if (RS->isReleased()) { + if (RS && (RS->isReleased() || RS->isRelinquished())) { if (ExplodedNode *N = C.generateSink()) { if (!BT_DoubleFree) BT_DoubleFree.reset( new BugType("Double free", "Memory Error")); BugReport *R = new BugReport(*BT_DoubleFree, - "Attempt to free released memory", N); + (RS->isReleased() ? "Attempt to free released memory" : + "Attempt to free non-owned memory"), N); R->addRange(ArgExpr->getSourceRange()); R->markInteresting(Sym); R->addVisitor(new MallocBugVisitor(Sym)); @@ -582,8 +693,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // Normal free. if (Hold) - return state->set<RegionState>(Sym, RefState::getRelinquished(CE)); - return state->set<RegionState>(Sym, RefState::getReleased(CE)); + return state->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr)); + return state->set<RegionState>(Sym, RefState::getReleased(ParentExpr)); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -780,10 +891,8 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){ // The semantics of the return value are: // If size was equal to 0, either NULL or a pointer suitable to be passed - // to free() is returned. - stateFree = stateFree->set<ReallocPairs>(ToPtr, - ReallocPair(FromPtr, FreesOnFail)); - C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); + // to free() is returned. We just free the input pointer and do not add + // any constrains on the output pointer. return stateFree; } @@ -851,8 +960,10 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, ProgramPoint P = AllocNode->getLocation(); const Stmt *AllocationStmt = 0; - if (isa<StmtPoint>(P)) - AllocationStmt = cast<StmtPoint>(P).getStmt(); + if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + AllocationStmt = Exit->getCalleeContext()->getCallSite(); + else if (StmtPoint *SP = dyn_cast<StmtPoint>(&P)) + AllocationStmt = SP->getStmt(); return LeakInfo(AllocationStmt, ReferenceRegion); } @@ -884,15 +995,15 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "Memory is never released; potential leak"; - if (Region) { + if (Region && Region->canPrintPretty()) { os << " of memory pointed to by '"; - Region->dumpPretty(os); - os <<'\''; + Region->printPretty(os); + os << '\''; } BugReport *R = new BugReport(*BT_Leak, os.str(), N, LocUsedForUniqueing); R->markInteresting(Sym); - R->addVisitor(new MallocBugVisitor(Sym)); + R->addVisitor(new MallocBugVisitor(Sym, true)); C.EmitReport(R); } @@ -960,23 +1071,9 @@ void MallocChecker::checkEndPath(CheckerContext &C) const { } } -bool MallocChecker::checkEscape(SymbolRef Sym, const Stmt *S, - CheckerContext &C) const { - ProgramStateRef state = C.getState(); - const RefState *RS = state->get<RegionState>(Sym); - if (!RS) - return false; - - if (RS->isAllocated()) { - state = state->set<RegionState>(Sym, RefState::getEscaped(S)); - C.addTransition(state); - return true; - } - return false; -} - void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - if (isMemFunction(C.getCalleeDecl(CE), C.getASTContext())) + // We will check for double free in the post visit. + if (isFreeFunction(C.getCalleeDecl(CE), C.getASTContext())) return; // Check use after free, when a freed pointer is passed to a call. @@ -1000,7 +1097,8 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { return; // Check if we are returning a symbol. - SVal RetVal = C.getState()->getSVal(E, C.getLocationContext()); + ProgramStateRef State = C.getState(); + SVal RetVal = State->getSVal(E, C.getLocationContext()); SymbolRef Sym = RetVal.getAsSymbol(); if (!Sym) // If we are returning a field of the allocated struct or an array element, @@ -1011,16 +1109,18 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { if (const SymbolicRegion *BMR = dyn_cast<SymbolicRegion>(MR->getBaseRegion())) Sym = BMR->getSymbol(); - if (!Sym) - return; // Check if we are returning freed memory. - if (checkUseAfterFree(Sym, C, E)) - return; + if (Sym) + if (checkUseAfterFree(Sym, C, E)) + return; - // If this function body is not inlined, check if the symbol is escaping. - if (C.getLocationContext()->getParent() == 0) - checkEscape(Sym, E, C); + // If this function body is not inlined, stop tracking any returned symbols. + if (C.getLocationContext()->getParent() == 0) { + State = + State->scanReachableSymbols<StopTrackingCallback>(RetVal).getState(); + C.addTransition(State); + } } // TODO: Blocks should be either inlined or should call invalidate regions @@ -1063,11 +1163,15 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE, C.addTransition(state); } -bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, - const Stmt *S) const { +bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { assert(Sym); const RefState *RS = C.getState()->get<RegionState>(Sym); - if (RS && RS->isReleased()) { + return (RS && RS->isReleased()); +} + +bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, + const Stmt *S) const { + if (isReleased(Sym, C)) { if (ExplodedNode *N = C.generateSink()) { if (!BT_UseFree) BT_UseFree.reset(new BugType("Use-after-free", "Memory Error")); @@ -1090,7 +1194,7 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, CheckerContext &C) const { SymbolRef Sym = l.getLocSymbolInBase(); if (Sym) - checkUseAfterFree(Sym, C); + checkUseAfterFree(Sym, C, S); } //===----------------------------------------------------------------------===// @@ -1118,13 +1222,11 @@ void MallocChecker::checkBind(SVal loc, SVal val, const Stmt *S, // To test (3), generate a new state with the binding added. If it is // the same state, then it escapes (since the store cannot represent // the binding). - escapes = (state == (state->bindLoc(*regionLoc, val))); - } - if (!escapes) { - // Case 4: We do not currently model what happens when a symbol is - // assigned to a struct field, so be conservative here and let the symbol - // go. TODO: This could definitely be improved upon. - escapes = !isa<VarRegion>(regionLoc->getRegion()); + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = state->getSVal(regionLoc->getRegion()); + if (StoredVal != val) + escapes = (state == (state->bindLoc(*regionLoc, val))); } } @@ -1165,7 +1267,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, if (RS) { if (RS->isReleased() && ! I.getData().IsFreeOnFailure) state = state->set<RegionState>(ReallocSym, - RefState::getAllocateUnchecked(RS->getStmt())); + RefState::getAllocated(RS->getStmt())); } state = state->remove<ReallocPairs>(I.getKey()); } @@ -1175,118 +1277,30 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, } // Check if the function is known to us. So, for example, we could -// conservatively assume it can free/reallocate it's pointer arguments. +// conservatively assume it can free/reallocate its pointer arguments. // (We assume that the pointers cannot escape through calls to system // functions not handled by this checker.) -bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call, +bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, ProgramStateRef State) const { - if (!Call) - return false; + assert(Call); // For now, assume that any C++ call can free memory. // TODO: If we want to be more optimistic here, we'll need to make sure that // regions escape to C++ containers. They seem to do that even now, but for // mysterious reasons. - if (Call->isCXXCall()) - return false; - - const Decl *D = Call->getDecl(); - if (!D) + if (!(isa<FunctionCall>(Call) || isa<ObjCMethodCall>(Call))) return false; - ASTContext &ASTC = State->getStateManager().getContext(); - - // If it's one of the allocation functions we can reason about, we model - // its behavior explicitly. - if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) { - return true; - } - - // If it's not a system call, assume it frees memory. - SourceManager &SM = ASTC.getSourceManager(); - if (!SM.isInSystemHeader(D->getLocation())) - return false; - - // Process C/ObjC functions. - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - // White list the system functions whose arguments escape. - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return true; - StringRef FName = II->getName(); - - // White list thread local storage. - if (FName.equals("pthread_setspecific")) - return false; - - // White list the 'XXXNoCopy' ObjC functions. - if (FName.endswith("NoCopy")) { - // Look for the deallocator argument. We know that the memory ownership - // is not transfered only if the deallocator argument is - // 'kCFAllocatorNull'. - for (unsigned i = 1; i < Call->getNumArgs(); ++i) { - const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts(); - if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { - StringRef DeallocatorName = DE->getFoundDecl()->getName(); - if (DeallocatorName == "kCFAllocatorNull") - return true; - } - } - return false; - } - - // PR12101 - // Many CoreFoundation and CoreGraphics might allow a tracked object - // to escape. - if (Call->isCFCGAllowingEscape(FName)) + // Check Objective-C messages by selector name. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { + // If it's not a framework call, or if it takes a callback, assume it + // can free memory. + if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg()) return false; - // Associating streams with malloced buffers. The pointer can escape if - // 'closefn' is specified (and if that function does free memory). - // Currently, we do not inspect the 'closefn' function (PR12101). - if (FName == "funopen") - if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0)) - return false; - - // Do not warn on pointers passed to 'setbuf' when used with std streams, - // these leaks might be intentional when setting the buffer for stdio. - // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer - if (FName == "setbuf" || FName =="setbuffer" || - FName == "setlinebuf" || FName == "setvbuf") { - if (Call->getNumArgs() >= 1) - if (const DeclRefExpr *Arg = - dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts())) - if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl())) - if (D->getCanonicalDecl()->getName().find("std") - != StringRef::npos) - return false; - } - - // A bunch of other functions, which take ownership of a pointer (See retain - // release checker). Not all the parameters here are invalidated, but the - // Malloc checker cannot differentiate between them. The right way of doing - // this would be to implement a pointer escapes callback. - if (FName == "CVPixelBufferCreateWithBytes" || - FName == "CGBitmapContextCreateWithData" || - FName == "CVPixelBufferCreateWithPlanarBytes" || - FName == "OSAtomicEnqueue") { - return false; - } - - // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. - if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) - return false; - - // Otherwise, assume that the function does not free memory. - // Most system calls, do not free the memory. - return true; - - // Process ObjC functions. - } else if (const ObjCMethodDecl * ObjCD = dyn_cast<ObjCMethodDecl>(D)) { - Selector S = ObjCD->getSelector(); + Selector S = Msg->getSelector(); - // White list the ObjC functions which do free memory. + // Whitelist the ObjC methods which do free memory. // - Anything containing 'freeWhenDone' param set to 1. // Ex: dataWithBytesNoCopy:length:freeWhenDone. for (unsigned i = 1; i < S.getNumArgs(); ++i) { @@ -1299,20 +1313,111 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call, } // If the first selector ends with NoCopy, assume that the ownership is - // transfered as well. + // transferred as well. // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; - if (S.getNameForSlot(0).endswith("NoCopy")) { + StringRef FirstSlot = S.getNameForSlot(0); + if (FirstSlot.endswith("NoCopy")) + return false; + + // If the first selector starts with addPointer, insertPointer, + // or replacePointer, assume we are dealing with NSPointerArray or similar. + // This is similar to C++ containers (vector); we still might want to check + // that the pointers get freed by following the container itself. + if (FirstSlot.startswith("addPointer") || + FirstSlot.startswith("insertPointer") || + FirstSlot.startswith("replacePointer")) { return false; } - // Otherwise, assume that the function does not free memory. - // Most system calls, do not free the memory. + // Otherwise, assume that the method does not free memory. + // Most framework methods do not free memory. return true; } - // Otherwise, assume that the function can free memory. - return false; + // At this point the only thing left to handle is straight function calls. + const FunctionDecl *FD = cast<FunctionCall>(Call)->getDecl(); + if (!FD) + return false; + + ASTContext &ASTC = State->getStateManager().getContext(); + + // If it's one of the allocation functions we can reason about, we model + // its behavior explicitly. + if (isMemFunction(FD, ASTC)) + return true; + // If it's not a system call, assume it frees memory. + if (!Call->isInSystemHeader()) + return false; + + // White list the system functions whose arguments escape. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + StringRef FName = II->getName(); + + // White list the 'XXXNoCopy' CoreFoundation functions. + // We specifically check these before + if (FName.endswith("NoCopy")) { + // Look for the deallocator argument. We know that the memory ownership + // is not transferred only if the deallocator argument is + // 'kCFAllocatorNull'. + for (unsigned i = 1; i < Call->getNumArgs(); ++i) { + const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts(); + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorNull") + return true; + } + } + return false; + } + + // Associating streams with malloced buffers. The pointer can escape if + // 'closefn' is specified (and if that function does free memory), + // but it will not if closefn is not specified. + // Currently, we do not inspect the 'closefn' function (PR12101). + if (FName == "funopen") + if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0)) + return true; + + // Do not warn on pointers passed to 'setbuf' when used with std streams, + // these leaks might be intentional when setting the buffer for stdio. + // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer + if (FName == "setbuf" || FName =="setbuffer" || + FName == "setlinebuf" || FName == "setvbuf") { + if (Call->getNumArgs() >= 1) { + const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts(); + if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE)) + if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl())) + if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos) + return false; + } + } + + // A bunch of other functions which either take ownership of a pointer or + // wrap the result up in a struct or object, meaning it can be freed later. + // (See RetainCountChecker.) Not all the parameters here are invalidated, + // but the Malloc checker cannot differentiate between them. The right way + // of doing this would be to implement a pointer escapes callback. + if (FName == "CGBitmapContextCreate" || + FName == "CGBitmapContextCreateWithData" || + FName == "CVPixelBufferCreateWithBytes" || + FName == "CVPixelBufferCreateWithPlanarBytes" || + FName == "OSAtomicEnqueue") { + return false; + } + + // Handle cases where we know a buffer's /address/ can escape. + // Note that the above checks handle some special cases where we know that + // even though the address escapes, it's still our responsibility to free the + // buffer. + if (Call->argumentsMayEscape()) + return false; + + // Otherwise, assume that the function does not free memory. + // Most system calls do not free the memory. + return true; } // If the symbol we are tracking is invalidated, but not explicitly (ex: the &p @@ -1323,7 +1428,7 @@ MallocChecker::checkRegionChanges(ProgramStateRef State, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { if (!invalidated || invalidated->empty()) return State; llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; @@ -1345,9 +1450,13 @@ MallocChecker::checkRegionChanges(ProgramStateRef State, SymbolRef sym = *I; if (WhitelistedSymbols.count(sym)) continue; - // The symbol escaped. - if (const RefState *RS = State->get<RegionState>(sym)) - State = State->set<RegionState>(sym, RefState::getEscaped(RS->getStmt())); + // The symbol escaped. Note, we assume that if the symbol is released, + // passing it out will result in a use after free. We also keep tracking + // relinquished symbols. + if (const RefState *RS = State->get<RegionState>(sym)) { + if (RS->isAllocated()) + State = State->remove<RegionState>(sym); + } } return State; } @@ -1377,7 +1486,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, const RefState *RS = state->get<RegionState>(Sym); const RefState *RSPrev = statePrev->get<RegionState>(Sym); - if (!RS && !RSPrev) + if (!RS) return 0; const Stmt *S = 0; @@ -1386,17 +1495,22 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, // Retrieve the associated statement. ProgramPoint ProgLoc = N->getLocation(); - if (isa<StmtPoint>(ProgLoc)) - S = cast<StmtPoint>(ProgLoc).getStmt(); + if (StmtPoint *SP = dyn_cast<StmtPoint>(&ProgLoc)) + S = SP->getStmt(); + else if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&ProgLoc)) + S = Exit->getCalleeContext()->getCallSite(); // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (isa<BlockEdge>(ProgLoc)) { - const CFGBlock *srcBlk = cast<BlockEdge>(ProgLoc).getSrc(); + else if (BlockEdge *Edge = dyn_cast<BlockEdge>(&ProgLoc)) { + const CFGBlock *srcBlk = Edge->getSrc(); S = srcBlk->getTerminator(); } if (!S) return 0; + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). + // Find out if this is an interesting point and what is the kind. if (Mode == Normal) { if (isAllocated(RS, RSPrev, S)) { @@ -1407,6 +1521,9 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, Msg = "Memory is released"; StackHint = new StackHintGeneratorForSymbol(Sym, "Returned released memory"); + } else if (isRelinquished(RS, RSPrev, S)) { + Msg = "Memory ownership is transfered"; + StackHint = new StackHintGeneratorForSymbol(Sym, ""); } else if (isReallocFailedCheck(RS, RSPrev, S)) { Mode = ReallocationFailed; Msg = "Reallocation failed"; @@ -1428,11 +1545,6 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, // Is this is the first appearance of the reallocated symbol? if (!statePrev->get<RegionState>(FailedReallocSymbol)) { - // If we ever hit this assert, that means BugReporter has decided to skip - // node pairs or visit them out of order. - assert(state->get<RegionState>(FailedReallocSymbol) && - "Missed the reallocation point"); - // We're at the reallocation point. Msg = "Attempt to reallocate memory"; StackHint = new StackHintGeneratorForSymbol(Sym, @@ -1452,6 +1564,14 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint); } +void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + RegionStateTy RS = State->get<RegionState>(); + + if (!RS.isEmpty()) + Out << "Has Malloc data" << NL; +} #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) {\ diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index 08a9da1..6292a47 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -118,11 +118,6 @@ public: Visit(E->getRHS()); } - void VisitBinAdd(const BinaryOperator *E) { - Visit(E->getLHS()); - Visit(E->getRHS()); - } - void VisitImplicitCastExpr(const ImplicitCastExpr *E) { return Visit(E->getSubExpr()); } @@ -139,6 +134,29 @@ public: } }; +// Determine if the pointee and sizeof types are compatible. Here +// we ignore constness of pointer types. +static bool typesCompatible(ASTContext &C, QualType A, QualType B) { + while (true) { + A = A.getCanonicalType(); + B = B.getCanonicalType(); + + if (A.getTypePtr() == B.getTypePtr()) + return true; + + if (const PointerType *ptrA = A->getAs<PointerType>()) + if (const PointerType *ptrB = B->getAs<PointerType>()) { + A = ptrA->getPointeeType(); + B = ptrB->getPointeeType(); + continue; + } + + break; + } + + return false; +} + class MallocSizeofChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, @@ -166,7 +184,7 @@ public: continue; QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument(); - if (!BR.getContext().hasSameUnqualifiedType(PointeeType, SizeofType)) { + if (!typesCompatible(BR.getContext(), PointeeType, SizeofType)) { const TypeSourceInfo *TSI = 0; if (i->CastedExprParent.is<const VarDecl *>()) { TSI = @@ -180,9 +198,8 @@ public: OS << "Result of '" << i->AllocCall->getDirectCallee()->getIdentifier()->getName() - << "' is converted to type '" - << CastedType.getAsString() << "', whose pointee type '" - << PointeeType.getAsString() << "' is incompatible with " + << "' is converted to a pointer of type '" + << PointeeType.getAsString() << "', which is incompatible with " << "sizeof operand type '" << SizeofType.getAsString() << "'"; llvm::SmallVector<SourceRange, 4> Ranges; Ranges.push_back(i->AllocCall->getCallee()->getSourceRange()); @@ -194,7 +211,7 @@ public: PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(), BR.getSourceManager(), ADC); - BR.EmitBasicReport(D, "allocator sizeof operand mismatch", + BR.EmitBasicReport(D, "Allocator sizeof operand mismatch", categories::UnixAPI, OS.str(), L, Ranges.data(), Ranges.size()); diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 4989ba8..aad3b0f 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -20,9 +20,9 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" @@ -36,29 +36,20 @@ class NSAutoreleasePoolChecker mutable Selector releaseS; public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } // end anonymous namespace -void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, +void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { - - const Expr *receiver = msg.getInstanceReceiver(); - if (!receiver) + if (!msg.isInstanceMessage()) return; - - // FIXME: Enhance with value-tracking information instead of consulting - // the type of the expression. - const ObjCObjectPointerType* PT = - receiver->getType()->getAs<ObjCObjectPointerType>(); - - if (!PT) - return; - const ObjCInterfaceDecl *OD = PT->getInterfaceDecl(); + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); if (!OD) return; - if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + if (!OD->getIdentifier()->isStr("NSAutoreleasePool")) return; if (releaseS.isNull()) diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index c2d7c09..efb7072 100644 --- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -15,8 +15,8 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "llvm/ADT/StringSwitch.h" #include <cstdarg> @@ -29,7 +29,7 @@ class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>, check::PostObjCMessage > { public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; }; } @@ -98,7 +98,7 @@ static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) { return (Arg == NULL); } -void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg, +void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 777e9ea..4cc92ce 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -50,8 +50,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, "for @synchronized")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report); C.EmitReport(report); } return; @@ -74,8 +73,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, "(no synchronization will occur)")); BugReport *report = new BugReport(*BT_null, BT_null->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report); C.EmitReport(report); return; diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index f4655b6..2ab49ed 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -21,7 +21,6 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 97b58cf..be45da1 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -39,9 +39,9 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" @@ -50,29 +50,27 @@ using namespace ento; static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); static bool isInitializationMethod(const ObjCMethodDecl *MD); -static bool isInitMessage(const ObjCMessage &msg); +static bool isInitMessage(const ObjCMethodCall &Msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { -class ObjCSelfInitChecker : public Checker< check::PreObjCMessage, - check::PostObjCMessage, +class ObjCSelfInitChecker : public Checker< check::PostObjCMessage, check::PostStmt<ObjCIvarRefExpr>, check::PreStmt<ReturnStmt>, - check::PreStmt<CallExpr>, - check::PostStmt<CallExpr>, - check::Location > { + check::PreCall, + check::PostCall, + check::Location, + check::Bind > { public: - void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; - void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const; void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const; + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; - void checkPreStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; - void checkPostStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; + void checkPreCall(const CallEvent &CE, CheckerContext &C) const; + void checkPostCall(const CallEvent &CE, CheckerContext &C) const; }; } // end anonymous namespace @@ -181,7 +179,7 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, C.EmitReport(report); } -void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, +void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { // When encountering a message that does initialization (init rule), // tag the return value so that we know later on that if self has this value @@ -192,7 +190,7 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, C.getCurrentAnalysisDeclContext()->getDecl()))) return; - if (isInitMessage(msg)) { + if (isInitMessage(Msg)) { // Tag the return value as the result of an initializer. ProgramStateRef state = C.getState(); @@ -201,14 +199,11 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, // value out when we return from this method. state = state->set<CalledInit>(true); - SVal V = state->getSVal(msg.getMessageExpr(), C.getLocationContext()); + SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext()); addSelfFlag(state, V, SelfFlag_InitRes, C); return; } - CallOrObjCMessage MsgWrapper(msg, C.getState(), C.getLocationContext()); - checkPostStmt(MsgWrapper, C); - // We don't check for an invalid 'self' in an obj-c message expression to cut // down false positives where logging functions get information from self // (like its class) or doing "invalidation" on self when the initialization @@ -239,8 +234,8 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, "'[(super or self) init...]'"); } -// When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass -// the SelfFlags from the object 'self' point to before the call, to the new +// When a call receives a reference to 'self', [Pre/Post]Call pass +// the SelfFlags from the object 'self' points to before the call to the new // object after the call. This is to avoid invalidation of 'self' by logging // functions. // Another common pattern in classes with multiple initializers is to put the @@ -255,26 +250,13 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, // Until we can use inter-procedural analysis, in such a call, transfer the // SelfFlags to the result of the call. -void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, +void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, CheckerContext &C) const { - CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext()); - checkPreStmt(CEWrapper, C); -} - -void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, - CheckerContext &C) const { - CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext()); - checkPostStmt(CEWrapper, C); -} - -void ObjCSelfInitChecker::checkPreObjCMessage(ObjCMessage Msg, - CheckerContext &C) const { - CallOrObjCMessage MsgWrapper(Msg, C.getState(), C.getLocationContext()); - checkPreStmt(MsgWrapper, C); -} + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; -void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE, - CheckerContext &C) const { ProgramStateRef state = C.getState(); unsigned NumArgs = CE.getNumArgs(); // If we passed 'self' as and argument to the call, record it in the state @@ -296,9 +278,19 @@ void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE, } } -void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, +void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + ProgramStateRef state = C.getState(); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + if (!prevFlags) + return; + state = state->remove<PreCallSelfFlags>(); + unsigned NumArgs = CE.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { SVal argV = CE.getArgSVal(i); @@ -306,8 +298,6 @@ void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, // If the address of 'self' is being passed to the call, assume that the // 'self' after the call will have the same flags. // EX: log(&self) - SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); - state = state->remove<PreCallSelfFlags>(); addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { @@ -315,8 +305,6 @@ void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, // returns 'self'. So assign the flags, which were set on 'self' to the // return value. // EX: self = performMoreInitialization(self) - SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); - state = state->remove<PreCallSelfFlags>(); const Expr *CallExpr = CE.getOriginExpr(); if (CallExpr) addSelfFlag(state, state->getSVal(CallExpr, C.getLocationContext()), @@ -336,6 +324,28 @@ void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); } + +void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + // Allow assignment of anything to self. Self is a local variable in the + // initializer, so it is legal to assign anything to it, like results of + // static functions/method calls. After self is assigned something we cannot + // reason about, stop enforcing the rules. + // (Only continue checking if the assigned value should be treated as self.) + if ((isSelfVar(loc, C)) && + !hasSelfFlag(val, SelfFlag_InitRes, C) && + !hasSelfFlag(val, SelfFlag_Self, C) && + !isSelfVar(val, C)) { + + // Stop tracking the checker-specific state in the state. + ProgramStateRef State = C.getState(); + State = State->remove<CalledInit>(); + if (SymbolRef sym = loc.getAsSymbol()) + State = State->remove<SelfFlag>(sym); + C.addTransition(State); + } +} + // FIXME: A callback should disable checkers at the start of functions. static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { if (!ND) @@ -383,8 +393,8 @@ static bool isInitializationMethod(const ObjCMethodDecl *MD) { return MD->getMethodFamily() == OMF_init; } -static bool isInitMessage(const ObjCMessage &msg) { - return msg.getMethodFamily() == OMF_init; +static bool isInitMessage(const ObjCMethodCall &Call) { + return Call.getMethodFamily() == OMF_init; } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index 4718dc7..582269c 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -47,6 +47,15 @@ static void Scan(IvarUsageMap& M, const Stmt *S) { return; } + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S)) + for (PseudoObjectExpr::const_semantics_iterator + i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { + const Expr *sub = *i; + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) + sub = OVE->getSourceExpr(); + Scan(M, sub); + } + for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) Scan(M, *I); } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index b569e41..5503b23 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -23,10 +23,10 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" @@ -66,8 +66,9 @@ public: /// particular argument. enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg, DecRefBridgedTransfered, + DecRefAndStopTracking, DecRefMsgAndStopTracking, IncRefMsg, IncRef, MakeCollectable, MayEscape, - NewAutoreleasePool, SelfOwn, StopTracking }; + NewAutoreleasePool, StopTracking }; namespace llvm { template <> struct FoldingSetTrait<ArgEffect> { @@ -351,6 +352,20 @@ struct ProgramStateTrait<RefBindings> } } +static inline const RefVal *getRefBinding(ProgramStateRef State, + SymbolRef Sym) { + return State->get<RefBindings>(Sym); +} + +static inline ProgramStateRef setRefBinding(ProgramStateRef State, + SymbolRef Sym, RefVal Val) { + return State->set<RefBindings>(Sym, Val); +} + +static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { + return State->remove<RefBindings>(Sym); +} + //===----------------------------------------------------------------------===// // Function/Method behavior summaries. //===----------------------------------------------------------------------===// @@ -431,6 +446,12 @@ public: bool isSimple() const { return Args.isEmpty(); } + +private: + ArgEffects getArgEffects() const { return Args; } + ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; } + + friend class RetainSummaryManager; }; } // end anonymous namespace @@ -449,9 +470,6 @@ public: ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s) : II(d ? d->getIdentifier() : 0), S(s) {} - ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s) - : II(d ? d->getIdentifier() : ii), S(s) {} - ObjCSummaryKey(Selector s) : II(0), S(s) {} @@ -473,17 +491,14 @@ template <> struct DenseMapInfo<ObjCSummaryKey> { } static unsigned getHashValue(const ObjCSummaryKey &V) { - return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier()) - & 0x88888888) - | (DenseMapInfo<Selector>::getHashValue(V.getSelector()) - & 0x55555555); + typedef std::pair<IdentifierInfo*, Selector> PairTy; + return DenseMapInfo<PairTy>::getHashValue(PairTy(V.getIdentifier(), + V.getSelector())); } static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { - return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(), - RHS.getIdentifier()) && - DenseMapInfo<Selector>::isEqual(LHS.getSelector(), - RHS.getSelector()); + return LHS.getIdentifier() == RHS.getIdentifier() && + LHS.getSelector() == RHS.getSelector(); } }; @@ -498,21 +513,16 @@ class ObjCSummaryCache { public: ObjCSummaryCache() {} - const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName, - Selector S) { - // Lookup the method using the decl for the class @interface. If we - // have no decl, lookup using the class name. - return D ? find(D, S) : find(ClsName, S); - } - const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) { // Do a lookup with the (D,S) pair. If we find a match return // the iterator. ObjCSummaryKey K(D, S); MapTy::iterator I = M.find(K); - if (I != M.end() || !D) + if (I != M.end()) return I->second; + if (!D) + return NULL; // Walk the super chain. If we find a hit with a parent, we'll end // up returning that summary. We actually allow that key (null,S), as @@ -628,9 +638,6 @@ class RetainSummaryManager { ArgEffects getArgEffects(); enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; - -public: - RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } const RetainSummary *getUnarySummary(const FunctionType* FT, UnaryFuncKind func); @@ -648,6 +655,10 @@ public: return getPersistentSummary(Summ); } + const RetainSummary *getDoNothingSummary() { + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } + const RetainSummary *getDefaultSummary() { return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, MayEscape); @@ -739,41 +750,32 @@ public: InitializeMethodSummaries(); } - const RetainSummary *getSummary(const FunctionDecl *FD); + const RetainSummary *getSummary(const CallEvent &Call, + ProgramStateRef State = 0); - const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, + const RetainSummary *getFunctionSummary(const FunctionDecl *FD); + + const RetainSummary *getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy, ObjCMethodSummariesTy &CachedSummaries); - const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - ProgramStateRef state, - const LocationContext *LC); - - const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - const ObjCInterfaceDecl *ID) { - return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(), - msg.getType(Ctx), ObjCMethodSummaries); - } + const RetainSummary *getInstanceMethodSummary(const ObjCMethodCall &M, + ProgramStateRef State); - const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { - const ObjCInterfaceDecl *Class = 0; - if (!msg.isInstanceMessage()) - Class = msg.getReceiverInterface(); + const RetainSummary *getClassMethodSummary(const ObjCMethodCall &M) { + assert(!M.isInstanceMessage()); + const ObjCInterfaceDecl *Class = M.getReceiverInterface(); - return getMethodSummary(msg.getSelector(), Class->getIdentifier(), - Class, msg.getMethodDecl(), msg.getType(Ctx), - ObjCClassMethodSummaries); + return getMethodSummary(M.getSelector(), Class, M.getDecl(), + M.getResultType(), ObjCClassMethodSummaries); } /// getMethodSummary - This version of getMethodSummary is used to query /// the summary for the current method being analyzed. const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { - // FIXME: Eventually this should be unneeded. const ObjCInterfaceDecl *ID = MD->getClassInterface(); Selector S = MD->getSelector(); - IdentifierInfo *ClsName = ID->getIdentifier(); QualType ResultTy = MD->getResultType(); ObjCMethodSummariesTy *CachedSummaries; @@ -782,11 +784,11 @@ public: else CachedSummaries = &ObjCClassMethodSummaries; - return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries); + return getMethodSummary(S, ID, MD, ResultTy, *CachedSummaries); } const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, - Selector S, QualType RetTy); + Selector S, QualType RetTy); void updateSummaryFromAnnotations(const RetainSummary *&Summ, const ObjCMethodDecl *MD); @@ -794,11 +796,18 @@ public: void updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD); + void updateSummaryForCall(const RetainSummary *&Summ, + const CallEvent &Call); + bool isGCEnabled() const { return GCEnabled; } bool isARCEnabled() const { return ARCEnabled; } bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } + + RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } + + friend class RetainSummaryTemplate; }; // Used to avoid allocating long-term (BPAlloc'd) memory for default retain @@ -811,10 +820,8 @@ class RetainSummaryTemplate { RetainSummary ScratchSummary; bool Accessed; public: - RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base, - RetainSummaryManager &mgr) - : Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base), - Accessed(false) {} + RetainSummaryTemplate(const RetainSummary *&real, RetainSummaryManager &mgr) + : Manager(mgr), RealSummary(real), ScratchSummary(*real), Accessed(false) {} ~RetainSummaryTemplate() { if (Accessed) @@ -886,7 +893,101 @@ static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) { return FName.find("MakeCollectable") != StringRef::npos; } -const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { +static ArgEffect getStopTrackingEquivalent(ArgEffect E) { + switch (E) { + case DoNothing: + case Autorelease: + case DecRefBridgedTransfered: + case IncRef: + case IncRefMsg: + case MakeCollectable: + case MayEscape: + case NewAutoreleasePool: + case StopTracking: + return StopTracking; + case DecRef: + case DecRefAndStopTracking: + return DecRefAndStopTracking; + case DecRefMsg: + case DecRefMsgAndStopTracking: + return DecRefMsgAndStopTracking; + case Dealloc: + return Dealloc; + } + + llvm_unreachable("Unknown ArgEffect kind"); +} + +void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, + const CallEvent &Call) { + if (Call.hasNonZeroCallbackArg()) { + ArgEffect RecEffect = getStopTrackingEquivalent(S->getReceiverEffect()); + ArgEffect DefEffect = getStopTrackingEquivalent(S->getDefaultArgEffect()); + + ArgEffects CustomArgEffects = S->getArgEffects(); + for (ArgEffects::iterator I = CustomArgEffects.begin(), + E = CustomArgEffects.end(); + I != E; ++I) { + ArgEffect Translated = getStopTrackingEquivalent(I->second); + if (Translated != DefEffect) + ScratchArgs = AF.add(ScratchArgs, I->first, Translated); + } + + RetEffect RE = RetEffect::MakeNoRet(); + + // Special cases where the callback argument CANNOT free the return value. + // This can generally only happen if we know that the callback will only be + // called when the return value is already being deallocated. + if (const FunctionCall *FC = dyn_cast<FunctionCall>(&Call)) { + IdentifierInfo *Name = FC->getDecl()->getIdentifier(); + + // This callback frees the associated buffer. + if (Name->isStr("CGBitmapContextCreateWithData")) + RE = S->getRetEffect(); + } + + S = getPersistentSummary(RE, RecEffect, DefEffect); + } +} + +const RetainSummary * +RetainSummaryManager::getSummary(const CallEvent &Call, + ProgramStateRef State) { + const RetainSummary *Summ; + switch (Call.getKind()) { + case CE_Function: + Summ = getFunctionSummary(cast<FunctionCall>(Call).getDecl()); + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + case CE_Block: + case CE_CXXConstructor: + case CE_CXXDestructor: + case CE_CXXAllocator: + // FIXME: These calls are currently unsupported. + return getPersistentStopSummary(); + case CE_ObjCMessage: { + const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); + if (Msg.isInstanceMessage()) + Summ = getInstanceMethodSummary(Msg, State); + else + Summ = getClassMethodSummary(Msg); + break; + } + } + + updateSummaryForCall(Summ, Call); + + assert(Summ && "Unknown call type?"); + return Summ; +} + +const RetainSummary * +RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { + // If we don't know what function we're calling, use our default summary. + if (!FD) + return getDefaultSummary(); + // Look up a summary in our cache of FunctionDecls -> Summaries. FuncSummariesTy::iterator I = FuncSummaries.find(FD); if (I != FuncSummaries.end()) @@ -894,6 +995,7 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // No summary? Generate one. const RetainSummary *S = 0; + bool AllowAnnotations = true; do { // We generate "stop" summaries for implicitly defined functions. @@ -901,13 +1003,6 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { S = getPersistentStopSummary(); break; } - // For C++ methods, generate an implicit "stop" summary as well. We - // can relax this once we have a clear policy for C++ methods and - // ownership attributes. - if (isa<CXXMethodDecl>(FD)) { - S = getPersistentStopSummary(); - break; - } // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the // function's type. @@ -929,18 +1024,22 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // filters. assert(ScratchArgs.isEmpty()); - if (FName == "pthread_create") { - // Part of: <rdar://problem/7299394>. This will be addressed - // better with IPA. + if (FName == "pthread_create" || FName == "pthread_setspecific") { + // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. + // This will be addressed better with IPA. S = getPersistentStopSummary(); } else if (FName == "NSMakeCollectable") { // Handle: id NSMakeCollectable(CFTypeRef) S = (RetTy->isObjCIdType()) ? getUnarySummary(FT, cfmakecollectable) : getPersistentStopSummary(); + // The headers on OS X 10.8 use cf_consumed/ns_returns_retained, + // but we can fully model NSMakeCollectable ourselves. + AllowAnnotations = false; } else if (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" || FName == "IOServiceNameMatching" || + FName == "IORegistryEntrySearchCFProperty" || FName == "IORegistryEntryIDMatching" || FName == "IOOpenFirmwarePathMatching") { // Part of <rdar://problem/6961230>. (IOKit) @@ -993,6 +1092,8 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // libdispatch finalizers. ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName.startswith("NSLog")) { + S = getDoNothingSummary(); } else if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) { // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can @@ -1090,8 +1191,13 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { } while (0); + // If we got all the way here without any luck, use a default summary. + if (!S) + S = getDefaultSummary(); + // Annotations override defaults. - updateSummaryFromAnnotations(S, FD); + if (AllowAnnotations) + updateSummaryFromAnnotations(S, FD); FuncSummaries[FD] = S; return S; @@ -1152,7 +1258,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, if (!FD) return; - RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); + assert(Summ && "Must have a summary to add annotations to."); + RetainSummaryTemplate Template(Summ, *this); // Effects on the parameters. unsigned parm_idx = 0; @@ -1200,7 +1307,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, if (!MD) return; - RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); + assert(Summ && "Must have a valid summary to add annotations to"); + RetainSummaryTemplate Template(Summ, *this); bool isTrackedLoc = false; // Effects on the receiver. @@ -1341,10 +1449,15 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, // because the reference count is quite possibly handled by a delegate // method. if (S.isKeywordSelector()) { - const std::string &str = S.getAsString(); - assert(!str.empty()); - if (StrInStrNoCase(str, "delegate:") != StringRef::npos) - ReceiverEff = StopTracking; + for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) { + StringRef Slot = S.getNameForSlot(i); + if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) { + if (ResultEff == ObjCInitRetE) + ResultEff = RetEffect::MakeNoRet(); + else + ReceiverEff = StopTracking; + } + } } if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing && @@ -1355,56 +1468,46 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, } const RetainSummary * -RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, - ProgramStateRef state, - const LocationContext *LC) { - - // We need the type-information of the tracked receiver object - // Retrieve it from the state. - const Expr *Receiver = msg.getInstanceReceiver(); - 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; - - if (Receiver) { - receiverV = state->getSValAsScalarOrLoc(Receiver, LC); - - // 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 = +RetainSummaryManager::getInstanceMethodSummary(const ObjCMethodCall &Msg, + ProgramStateRef State) { + const ObjCInterfaceDecl *ReceiverClass = 0; + + // We do better tracking of the type of the object than the core ExprEngine. + // See if we have its type in our private state. + // FIXME: Eventually replace the use of state->get<RefBindings> with + // a generic API for reasoning about the Objective-C types of symbolic + // objects. + SVal ReceiverV = Msg.getReceiverSVal(); + if (SymbolRef Sym = ReceiverV.getAsLocSymbol()) + if (const RefVal *T = getRefBinding(State, Sym)) + if (const ObjCObjectPointerType *PT = T->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); + ReceiverClass = 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(); - } - } else { - // FIXME: Hack for 'super'. - ID = msg.getReceiverInterface(); - } + // If we don't know what kind of object this is, fall back to its static type. + if (!ReceiverClass) + ReceiverClass = Msg.getReceiverInterface(); // FIXME: The receiver could be a reference to a class, meaning that // we should use the class method. - return getInstanceMethodSummary(msg, ID); + // id x = [NSObject class]; + // [x performSelector:... withObject:... afterDelay:...]; + Selector S = Msg.getSelector(); + const ObjCMethodDecl *Method = Msg.getDecl(); + if (!Method && ReceiverClass) + Method = ReceiverClass->getInstanceMethod(S); + + return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(), + ObjCMethodSummaries); } const RetainSummary * -RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, +RetainSummaryManager::getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy, ObjCMethodSummariesTy &CachedSummaries) { // Look up a summary in our summary cache. - const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S); + const RetainSummary *Summ = CachedSummaries.find(ID, S); if (!Summ) { Summ = getStandardMethodSummary(MD, S, RetTy); @@ -1413,7 +1516,7 @@ RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName, updateSummaryFromAnnotations(Summ, MD); // Memoize the summary. - CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + CachedSummaries[ObjCSummaryKey(ID, S)] = Summ; } return Summ; @@ -1430,29 +1533,6 @@ void RetainSummaryManager::InitializeClassMethodSummaries() { addClassMethSummary("NSAutoreleasePool", "addObject", getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Autorelease)); - - // Create the summaries for [NSObject performSelector...]. We treat - // these as 'stop tracking' for the arguments because they are often - // used for delegates that can release the object. When we have better - // inter-procedural analysis we can potentially do something better. This - // workaround is to remove false positives. - const RetainSummary *Summ = - getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); - IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", "inModes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", - "withObject", NULL); } void RetainSummaryManager::InitializeMethodSummaries() { @@ -1558,6 +1638,10 @@ void RetainSummaryManager::InitializeMethodSummaries() { //===----------------------------------------------------------------------===// // AutoreleaseBindings - State used to track objects in autorelease pools. //===----------------------------------------------------------------------===// +#define AUTORELEASE_POOL_MODELING (0) +// We do not currently have complete modeling of autorelease pools. + +#if AUTORELEASE_POOL_MODELING typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; @@ -1605,6 +1689,7 @@ SendAutorelease(ProgramStateRef state, return state->set<AutoreleasePoolContents>(pool, newCnts); } +#endif //===----------------------------------------------------------------------===// // Error reporting. @@ -1690,32 +1775,18 @@ namespace { }; class Leak : public CFRefBug { - const bool isReturn; - protected: - Leak(StringRef name, bool isRet) - : CFRefBug(name), isReturn(isRet) { + public: + Leak(StringRef name) + : CFRefBug(name) { // Leaks should not be reported if they are post-dominated by a sink. setSuppressOnSink(true); } - public: const char *getDescription() const { return ""; } bool isLeak() const { return true; } }; - class LeakAtReturn : public Leak { - public: - LeakAtReturn(StringRef name) - : Leak(name, true) {} - }; - - class LeakWithinFunction : public Leak { - public: - LeakWithinFunction(StringRef name) - : Leak(name, false) {} - }; - //===---------===// // Bug Reports. // //===---------===// @@ -1854,25 +1925,21 @@ static inline bool contains(const SmallVectorImpl<ArgEffect>& V, return false; } -static bool isPropertyAccess(const Stmt *S, ParentMap &PM) { - unsigned maxDepth = 4; - while (S && maxDepth) { - if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) { - if (!isa<ObjCMessageExpr>(PO->getSyntacticForm())) - return true; - return false; - } - S = PM.getParent(S); - --maxDepth; - } - return false; +static bool isNumericLiteralExpression(const Expr *E) { + // FIXME: This set of cases was copied from SemaExprObjC. + return isa<IntegerLiteral>(E) || + isa<CharacterLiteral>(E) || + isa<FloatingLiteral>(E) || + isa<ObjCBoolLiteralExpr>(E) || + isa<CXXBoolLiteralExpr>(E); } PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). if (!isa<StmtPoint>(N->getLocation())) return NULL; @@ -1881,11 +1948,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, ProgramStateRef CurrSt = N->getState(); const LocationContext *LCtx = N->getLocationContext(); - const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); + const RefVal* CurrT = getRefBinding(CurrSt, Sym); if (!CurrT) return NULL; const RefVal &CurrV = *CurrT; - const RefVal *PrevT = PrevSt->get<RefBindings>(Sym); + const RefVal *PrevT = getRefBinding(PrevSt, Sym); // Create a string buffer to constain all the useful things we want // to tell the user. @@ -1903,6 +1970,24 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, else if (isa<ObjCDictionaryLiteral>(S)) { os << "NSDictionary literal is an object with a +0 retain count"; } + else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { + if (isNumericLiteralExpression(BL->getSubExpr())) + os << "NSNumber literal is an object with a +0 retain count"; + else { + const ObjCInterfaceDecl *BoxClass = 0; + if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) + BoxClass = Method->getClassInterface(); + + // We should always be able to find the boxing class interface, + // but consider this future-proofing. + if (BoxClass) + os << *BoxClass << " b"; + else + os << "B"; + + os << "oxed expression produces an object with a +0 retain count"; + } + } else { if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available). @@ -1913,10 +1998,22 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, os << "function call"; } else { - assert(isa<ObjCMessageExpr>(S)); - // The message expression may have between written directly or as - // a property access. Lazily determine which case we are looking at. - os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method"); + assert(isa<ObjCMessageExpr>(S)); + CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); + CallEventRef<ObjCMethodCall> Call + = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); + + switch (Call->getMessageKind()) { + case OCM_Message: + os << "Method"; + break; + case OCM_PropertyAccess: + os << "Property"; + break; + case OCM_Subscript: + os << "Subscript"; + break; + } } if (CurrV.getObjKind() == RetEffect::CF) { @@ -2143,9 +2240,8 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, while (N) { ProgramStateRef St = N->getState(); - RefBindings B = St->get<RefBindings>(); - if (!B.lookup(Sym)) + if (!getRefBinding(St, Sym)) break; StoreManager::FindUniqueBinding FB(Sym); @@ -2216,7 +2312,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << "allocated object"; // Get the retain count. - const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); + const RefVal* RV = getRefBinding(EndN->getState(), Sym); if (RV->getKind() == RefVal::ErrorLeakReturned) { // FIXME: Per comments in rdar://6320065, "create" only applies to CF @@ -2276,8 +2372,15 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); // Get the SourceLocation for the allocation site. + // FIXME: This will crash the analyzer if an allocation comes from an + // implicit call. (Currently there are no such allocations in Cocoa, though.) + const Stmt *AllocStmt; ProgramPoint P = AllocNode->getLocation(); - const Stmt *AllocStmt = cast<PostStmt>(P).getStmt(); + if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + AllocStmt = Exit->getCalleeContext()->getCallSite(); + else + AllocStmt = cast<PostStmt>(P).getStmt(); + assert(AllocStmt && "All allocations must come from explicit calls"); Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, n->getLocationContext()); // Fill in the description of the bug. @@ -2307,11 +2410,10 @@ class RetainCountChecker check::EndPath, check::PostStmt<BlockExpr>, check::PostStmt<CastExpr>, - check::PostStmt<CallExpr>, - check::PostStmt<CXXConstructExpr>, check::PostStmt<ObjCArrayLiteral>, check::PostStmt<ObjCDictionaryLiteral>, - check::PostObjCMessage, + check::PostStmt<ObjCBoxedExpr>, + check::PostCall, check::PreStmt<ReturnStmt>, check::RegionChanges, eval::Assume, @@ -2330,7 +2432,9 @@ class RetainCountChecker mutable OwningPtr<RetainSummaryManager> Summaries; mutable OwningPtr<RetainSummaryManager> SummariesGC; +#if AUTORELEASE_POOL_MODELING mutable ARCounts::Factory ARCountFactory; +#endif mutable SummaryLogTy SummaryLog; mutable bool ShouldResetSummaryLog; @@ -2382,20 +2486,17 @@ public: bool GCEnabled) const { if (GCEnabled) { if (!leakWithinFunctionGC) - leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when " - "using garbage " - "collection")); + leakWithinFunctionGC.reset(new Leak("Leak of object when using " + "garbage collection")); return leakWithinFunctionGC.get(); } else { if (!leakWithinFunction) { if (LOpts.getGC() == LangOptions::HybridGC) { - leakWithinFunction.reset(new LeakWithinFunction("Leak of object when " - "not using garbage " - "collection (GC) in " - "dual GC/non-GC " - "code")); + leakWithinFunction.reset(new Leak("Leak of object when not using " + "garbage collection (GC) in " + "dual GC/non-GC code")); } else { - leakWithinFunction.reset(new LeakWithinFunction("Leak")); + leakWithinFunction.reset(new Leak("Leak")); } } return leakWithinFunction.get(); @@ -2405,17 +2506,17 @@ public: CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const { if (GCEnabled) { if (!leakAtReturnGC) - leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when " - "using garbage collection")); + leakAtReturnGC.reset(new Leak("Leak of returned object when using " + "garbage collection")); return leakAtReturnGC.get(); } else { if (!leakAtReturn) { if (LOpts.getGC() == LangOptions::HybridGC) { - leakAtReturn.reset(new LeakAtReturn("Leak of returned object when " - "not using garbage collection " - "(GC) in dual GC/non-GC code")); + leakAtReturn.reset(new Leak("Leak of returned object when not using " + "garbage collection (GC) in dual " + "GC/non-GC code")); } else { - leakAtReturn.reset(new LeakAtReturn("Leak of returned object")); + leakAtReturn.reset(new Leak("Leak of returned object")); } } return leakAtReturn.get(); @@ -2453,13 +2554,13 @@ public: void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; - void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const; void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; - void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const; + void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const; + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call, + void checkSummary(const RetainSummary &Summ, const CallEvent &Call, CheckerContext &C) const; bool evalCall(const CallExpr *CE, CheckerContext &C) const; @@ -2472,7 +2573,7 @@ public: const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const; + const CallEvent *Call) const; bool wantsRegionChangeUpdate(ProgramStateRef state) const { return true; @@ -2499,8 +2600,8 @@ public: const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; ProgramStateRef handleSymbolDeath(ProgramStateRef state, - SymbolRef sid, RefVal V, - SmallVectorImpl<SymbolRef> &Leaked) const; + SymbolRef sid, RefVal V, + SmallVectorImpl<SymbolRef> &Leaked) const; std::pair<ExplodedNode *, ProgramStateRef > handleAutoreleaseCounts(ProgramStateRef state, @@ -2597,7 +2698,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol(); if (!Sym) return; - const RefVal* T = state->get<RefBindings>(Sym); + const RefVal* T = getRefBinding(state, Sym); if (!T) return; @@ -2613,54 +2714,6 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, C.addTransition(state); } -void RetainCountChecker::checkPostStmt(const CallExpr *CE, - CheckerContext &C) const { - if (C.wasInlined) - return; - - // Get the callee. - ProgramStateRef state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee, C.getLocationContext()); - - RetainSummaryManager &Summaries = getSummaryManager(C); - const RetainSummary *Summ = 0; - - // FIXME: Better support for blocks. For now we stop tracking anything - // that is passed to blocks. - // FIXME: Need to handle variables that are "captured" by the block. - if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { - Summ = Summaries.getPersistentStopSummary(); - } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) { - Summ = Summaries.getSummary(FD); - } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) { - if (const CXXMethodDecl *MD = me->getMethodDecl()) - Summ = Summaries.getSummary(MD); - } - - if (!Summ) - Summ = Summaries.getDefaultSummary(); - - checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); -} - -void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, - CheckerContext &C) const { - const CXXConstructorDecl *Ctor = CE->getConstructor(); - if (!Ctor) - return; - - RetainSummaryManager &Summaries = getSummaryManager(C); - const RetainSummary *Summ = Summaries.getSummary(Ctor); - - // If we didn't get a summary, this constructor doesn't affect retain counts. - if (!Summ) - return; - - ProgramStateRef state = C.getState(); - checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); -} - void RetainCountChecker::processObjCLiterals(CheckerContext &C, const Expr *Ex) const { ProgramStateRef state = C.getState(); @@ -2670,7 +2723,7 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C, const Stmt *child = *it; SVal V = state->getSVal(child, pred->getLocationContext()); if (SymbolRef sym = V.getAsSymbol()) - if (const RefVal* T = state->get<RefBindings>(sym)) { + if (const RefVal* T = getRefBinding(state, sym)) { RefVal::Kind hasErr = (RefVal::Kind) 0; state = updateSymbol(state, sym, *T, MayEscape, hasErr, C); if (hasErr) { @@ -2685,8 +2738,8 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C, if (SymbolRef sym = state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { QualType ResultTy = Ex->getType(); - state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC, - ResultTy)); + state = setRefBinding(state, sym, + RefVal::makeNotOwned(RetEffect::ObjC, ResultTy)); } C.addTransition(state); @@ -2704,30 +2757,34 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, processObjCLiterals(C, DL); } -void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, - CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - RetainSummaryManager &Summaries = getSummaryManager(C); +void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, + CheckerContext &C) const { + const ExplodedNode *Pred = C.getPredecessor(); + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); - const RetainSummary *Summ; - if (Msg.isInstanceMessage()) { - const LocationContext *LC = C.getLocationContext(); - Summ = Summaries.getInstanceMethodSummary(Msg, state, LC); - } else { - Summ = Summaries.getClassMethodSummary(Msg); + if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) { + QualType ResultTy = Ex->getType(); + State = setRefBinding(State, Sym, + RefVal::makeNotOwned(RetEffect::ObjC, ResultTy)); } - // If we didn't get a summary, this message doesn't affect retain counts. - if (!Summ) + C.addTransition(State); +} + +void RetainCountChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + if (C.wasInlined) return; - checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C); + RetainSummaryManager &Summaries = getSummaryManager(C); + const RetainSummary *Summ = Summaries.getSummary(Call, C.getState()); + checkSummary(*Summ, Call, C); } /// GetReturnType - Used to get the return type of a message expression or /// function call with the intention of affixing that type to a tracked symbol. -/// While the the return type can be queried directly from RetEx, when +/// While the return type can be queried directly from RetEx, when /// invoking class methods we augment to the return type to be that of /// a pointer to the class (as opposed it just being id). // FIXME: We may be able to do this with related result types instead. @@ -2754,7 +2811,7 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { } void RetainCountChecker::checkSummary(const RetainSummary &Summ, - const CallOrObjCMessage &CallOrMsg, + const CallEvent &CallOrMsg, CheckerContext &C) const { ProgramStateRef state = C.getState(); @@ -2767,7 +2824,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, SVal V = CallOrMsg.getArgSVal(idx); if (SymbolRef Sym = V.getAsLocSymbol()) { - if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) { + if (const RefVal *T = getRefBinding(state, Sym)) { state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C); if (hasErr) { ErrorRange = CallOrMsg.getArgSourceRange(idx); @@ -2780,17 +2837,18 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, // Evaluate the effect on the message receiver. bool ReceiverIsTracked = false; - if (!hasErr && CallOrMsg.isObjCMessage()) { - const LocationContext *LC = C.getLocationContext(); - SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC); - if (SymbolRef Sym = Receiver.getAsLocSymbol()) { - if (const RefVal *T = state->get<RefBindings>(Sym)) { - ReceiverIsTracked = true; - state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), - hasErr, C); - if (hasErr) { - ErrorRange = CallOrMsg.getReceiverSourceRange(); - ErrorSym = Sym; + if (!hasErr) { + const ObjCMethodCall *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg); + if (MsgInvocation) { + if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { + if (const RefVal *T = getRefBinding(state, Sym)) { + ReceiverIsTracked = true; + state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), + hasErr, C); + if (hasErr) { + ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange(); + ErrorSym = Sym; + } } } } @@ -2827,11 +2885,11 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, if (!Sym) break; - // Use the result type from callOrMsg as it automatically adjusts + // Use the result type from the CallEvent as it automatically adjusts // for methods/functions that return references. - QualType ResultTy = CallOrMsg.getResultType(C.getASTContext()); - state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), - ResultTy)); + QualType ResultTy = CallOrMsg.getResultType(); + state = setRefBinding(state, Sym, RefVal::makeOwned(RE.getObjKind(), + ResultTy)); // FIXME: Add a flag to the checker where allocations are assumed to // *not* fail. (The code below is out-of-date, though.) @@ -2856,8 +2914,8 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, // Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *. QualType ResultTy = GetReturnType(Ex, C.getASTContext()); - state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(), - ResultTy)); + state = setRefBinding(state, Sym, RefVal::makeNotOwned(RE.getObjKind(), + ResultTy)); break; } } @@ -2895,25 +2953,37 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount; switch (E) { - default: break; - case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break; - case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break; - case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break; - case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing : - NewAutoreleasePool; break; + default: + break; + case IncRefMsg: + E = IgnoreRetainMsg ? DoNothing : IncRef; + break; + case DecRefMsg: + E = IgnoreRetainMsg ? DoNothing : DecRef; + break; + case DecRefMsgAndStopTracking: + E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTracking; + break; + case MakeCollectable: + E = C.isObjCGCEnabled() ? DecRef : DoNothing; + break; + case NewAutoreleasePool: + E = C.isObjCGCEnabled() ? DoNothing : NewAutoreleasePool; + break; } // Handle all use-after-releases. if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) { V = V ^ RefVal::ErrorUseAfterRelease; hasErr = V.getKind(); - return state->set<RefBindings>(sym, V); + return setRefBinding(state, sym, V); } switch (E) { case DecRefMsg: case IncRefMsg: case MakeCollectable: + case DecRefMsgAndStopTracking: llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted"); case Dealloc: @@ -2931,7 +3001,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, // The object immediately transitions to the released state. V = V ^ RefVal::Released; V.clearCounts(); - return state->set<RefBindings>(sym, V); + return setRefBinding(state, sym, V); case RefVal::NotOwned: V = V ^ RefVal::ErrorDeallocNotOwned; hasErr = V.getKind(); @@ -2941,7 +3011,10 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case NewAutoreleasePool: assert(!C.isObjCGCEnabled()); - return state->add<AutoreleaseStack>(sym); +#if AUTORELEASE_POOL_MODELING + state = state->add<AutoreleaseStack>(sym); +#endif + return state; case MayEscape: if (V.getKind() == RefVal::Owned) { @@ -2959,12 +3032,16 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, return state; // Update the autorelease counts. + // TODO: AutoreleasePoolContents are not currently used. We will need to + // call SendAutorelease after it's wired up. +#if AUTORELEASE_POOL_MODELING state = SendAutorelease(state, ARCountFactory, sym); +#endif V = V.autorelease(); break; case StopTracking: - return state->remove<RefBindings>(sym); + return removeRefBinding(state, sym); case IncRef: switch (V.getKind()) { @@ -2982,11 +3059,9 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, } break; - case SelfOwn: - V = V ^ RefVal::NotOwned; - // Fall-through. case DecRef: case DecRefBridgedTransfered: + case DecRefAndStopTracking: switch (V.getKind()) { default: // case 'RefVal::Released' handled above. @@ -2997,13 +3072,18 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, if (V.getCount() == 1) V = V ^ (E == DecRefBridgedTransfered ? RefVal::NotOwned : RefVal::Released); + else if (E == DecRefAndStopTracking) + return removeRefBinding(state, sym); + V = V - 1; break; case RefVal::NotOwned: - if (V.getCount() > 0) + if (V.getCount() > 0) { + if (E == DecRefAndStopTracking) + return removeRefBinding(state, sym); V = V - 1; - else { + } else { V = V ^ RefVal::ErrorReleaseNotOwned; hasErr = V.getKind(); } @@ -3018,7 +3098,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, } break; } - return state->set<RefBindings>(sym, V); + return setRefBinding(state, sym, V); } void RetainCountChecker::processNonLeakError(ProgramStateRef St, @@ -3117,7 +3197,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // If the receiver is unknown, conjure a return value. SValBuilder &SVB = C.getSValBuilder(); unsigned Count = C.getCurrentBlockCount(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); + RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); } state = state->BindExpr(CE, LCtx, RetVal, false); @@ -3126,9 +3206,9 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { if (const MemRegion *ArgRegion = RetVal.getAsRegion()) { // Save the refcount status of the argument. SymbolRef Sym = RetVal.getAsLocSymbol(); - RefBindings::data_type *Binding = 0; + const RefVal *Binding = 0; if (Sym) - Binding = state->get<RefBindings>(Sym); + Binding = getRefBinding(state, Sym); // Invalidate the argument region. unsigned Count = C.getCurrentBlockCount(); @@ -3136,7 +3216,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Restore the refcount status of the argument. if (Binding) - state = state->set<RefBindings>(Sym, *Binding); + state = setRefBinding(state, Sym, *Binding); } C.addTransition(state); @@ -3175,7 +3255,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, return; // Get the reference count binding (if any). - const RefVal *T = state->get<RefBindings>(Sym); + const RefVal *T = getRefBinding(state, Sym); if (!T) return; @@ -3208,7 +3288,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, } // Update the binding. - state = state->set<RefBindings>(Sym, X); + state = setRefBinding(state, Sym, X); ExplodedNode *Pred = C.addTransition(state); // At this point we have updated the state properly. @@ -3230,29 +3310,27 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, return; // Get the updated binding. - T = state->get<RefBindings>(Sym); + T = getRefBinding(state, Sym); assert(T); X = *T; // Consult the summary of the enclosing method. RetainSummaryManager &Summaries = getSummaryManager(C); const Decl *CD = &Pred->getCodeDecl(); + RetEffect RE = RetEffect::MakeNoRet(); + // FIXME: What is the convention for blocks? Is there one? if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) { - // Unlike regular functions, /all/ ObjC methods are assumed to always - // follow Cocoa retain-count conventions, not just those with special - // names or attributes. const RetainSummary *Summ = Summaries.getMethodSummary(MD); - RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet(); - checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); + RE = Summ->getRetEffect(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { + if (!isa<CXXMethodDecl>(FD)) { + const RetainSummary *Summ = Summaries.getFunctionSummary(FD); + RE = Summ->getRetEffect(); + } } - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { - if (!isa<CXXMethodDecl>(FD)) - if (const RetainSummary *Summ = Summaries.getSummary(FD)) - checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X, - Sym, state); - } + checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); } void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, @@ -3283,7 +3361,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (hasError) { // Generate an error node. - state = state->set<RefBindings>(Sym, X); + state = setRefBinding(state, Sym, X); static SimpleProgramPointTag ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak"); @@ -3303,7 +3381,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (RE.isOwned()) { // Trying to return a not owned object to a caller expecting an // owned object. - state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); + state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned); static SimpleProgramPointTag ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned"); @@ -3346,7 +3424,11 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // To test (3), generate a new state with the binding added. If it is // the same state, then it escapes (since the store cannot represent // the binding). - escapes = (state == (state->bindLoc(*regionLoc, val))); + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = state->getSVal(regionLoc->getRegion()); + if (StoredVal != val) + escapes = (state == (state->bindLoc(*regionLoc, val))); } if (!escapes) { // Case 4: We do not currently model what happens when a symbol is @@ -3406,7 +3488,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { if (!invalidated) return state; @@ -3423,7 +3505,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, if (WhitelistedSymbols.count(sym)) continue; // Remove any existing reference-count binding. - state = state->remove<RefBindings>(sym); + state = removeRefBinding(state, sym); } return state; } @@ -3463,7 +3545,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, V.setCount(Cnt - ACnt); V.setAutoreleaseCount(0); } - state = state->set<RefBindings>(Sym, V); + state = setRefBinding(state, Sym, V); ExplodedNode *N = Bd.MakeNode(state, Pred); if (N == 0) state = 0; @@ -3473,7 +3555,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, // Woah! More autorelease counts then retain counts left. // Emit hard error. V = V ^ RefVal::ErrorOverAutorelease; - state = state->set<RefBindings>(Sym, V); + state = setRefBinding(state, Sym, V); if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) { SmallString<128> sbuf; @@ -3507,10 +3589,10 @@ RetainCountChecker::handleSymbolDeath(ProgramStateRef state, hasLeak = (V.getCount() > 0); if (!hasLeak) - return state->remove<RefBindings>(sid); + return removeRefBinding(state, sid); Leaked.push_back(sid); - return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak); + return setRefBinding(state, sid, V ^ RefVal::ErrorLeak); } ExplodedNode * @@ -3559,7 +3641,7 @@ void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const { // If the current LocationContext has a parent, don't check for leaks. // We will do that later. - // FIXME: we should instead check for imblances of the retain/releases, + // FIXME: we should instead check for imbalances of the retain/releases, // and suggest annotations. if (Ctx.getLocationContext()->getParent()) return; @@ -3641,6 +3723,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Debug printing of refcount bindings and autorelease pools. //===----------------------------------------------------------------------===// +#if AUTORELEASE_POOL_MODELING static void PrintPool(raw_ostream &Out, SymbolRef Sym, ProgramStateRef State) { Out << ' '; @@ -3654,7 +3737,6 @@ static void PrintPool(raw_ostream &Out, SymbolRef Sym, if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym)) for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I) Out << '(' << I.getKey() << ',' << I.getData() << ')'; - Out << '}'; } @@ -3664,6 +3746,7 @@ static bool UsesAutorelease(ProgramStateRef state) { return !state->get<AutoreleaseStack>().isEmpty() || state->get<AutoreleasePoolContents>(SymbolRef()); } +#endif void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { @@ -3679,6 +3762,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, Out << NL; } +#if AUTORELEASE_POOL_MODELING // Print the autorelease stack. if (UsesAutorelease(State)) { Out << Sep << NL << "AR pool stack:"; @@ -3690,6 +3774,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, Out << NL; } +#endif } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 7b1f0b1..ca2a55d 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -53,9 +53,9 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, BugReport *report = new BugReport(*BT, BT->getDescription(), N); + report->disablePathPruning(); report->addRange(RetE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, RetE, report); C.EmitReport(report); } diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 3745d4a..731dd66 100644 --- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -116,7 +116,7 @@ namespace ento { bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD) + if (!FD || FD->getKind() != Decl::Function) return false; ASTContext &Ctx = C.getASTContext(); diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp new file mode 100644 index 0000000..b97cd6c --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -0,0 +1,84 @@ +//== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These checkers print various aspects of the ExprEngine's traversal of the CFG +// as it builds the ExplodedGraph. +// +//===----------------------------------------------------------------------===// +#include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class TraversalDumper : public Checker< check::BranchCondition, + check::EndPath > { +public: + void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; + void checkEndPath(CheckerContext &C) const; +}; +} + +void TraversalDumper::checkBranchCondition(const Stmt *Condition, + CheckerContext &C) const { + // Special-case Objective-C's for-in loop, which uses the entire loop as its + // condition. We just print the collection expression. + const Stmt *Parent = dyn_cast<ObjCForCollectionStmt>(Condition); + if (!Parent) { + const ParentMap &Parents = C.getLocationContext()->getParentMap(); + Parent = Parents.getParent(Condition); + } + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + SourceLocation Loc = Parent->getLocStart(); + llvm::outs() << C.getSourceManager().getSpellingLineNumber(Loc) << " " + << Parent->getStmtClassName() << "\n"; +} + +void TraversalDumper::checkEndPath(CheckerContext &C) const { + llvm::outs() << "--END PATH--\n"; +} + +void ento::registerTraversalDumper(CheckerManager &mgr) { + mgr.registerChecker<TraversalDumper>(); +} + +//------------------------------------------------------------------------------ + +namespace { +class CallDumper : public Checker< check::PreCall > { +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; +} + +void CallDumper::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + unsigned Indentation = 0; + for (const LocationContext *LC = C.getLocationContext()->getParent(); + LC != 0; LC = LC->getParent()) + ++Indentation; + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + llvm::outs().indent(Indentation); + Call.dump(llvm::outs()); +} + +void ento::registerCallDumper(CheckerManager &mgr) { + mgr.registerChecker<CallDumper>(); +} diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index a30f6d5..70a33c7 100644 --- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -99,8 +99,9 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, // Emit the bug report. BugReport *R = new BugReport(*BT, BT->getDescription(), N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, R); R->addRange(Ex->getSourceRange()); + R->disablePathPruning(); Ctx.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index d57767e..675b38a 100644 --- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -94,6 +94,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR)); + R->disablePathPruning(); // need location of block C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index c3c9ed7..e220499 100644 --- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -76,12 +76,12 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, BugReport *report = new BugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Ex, report); } else - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, B, report); + + report->disablePathPruning(); C.EmitReport(report); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index 0297c4e..6ae3c18 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -42,9 +42,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, // Generate a report for this bug. BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - A->getIdx(), - R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, A->getIdx(), R); C.EmitReport(R); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 78f7fa6..14a884e 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -78,8 +78,9 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex, R)); + bugreporter::addTrackNullOrUndefValueVisitor(N, ex, R); } + R->disablePathPruning(); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 60e665fe..d35455c 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -224,8 +224,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); report->addRange(arg->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, arg, report); C.EmitReport(report); return true; @@ -256,7 +255,7 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, (void) ReportZeroByteAllocation(C, falseState, arg, fn); return; } - // Assume the the value is non-zero going forward. + // Assume the value is non-zero going forward. assert(trueState); if (trueState != state) C.addTransition(trueState); @@ -292,7 +291,7 @@ void UnixAPIChecker::CheckCallocZero(CheckerContext &C, } } - // Assume the the value is non-zero going forward. + // Assume the value is non-zero going forward. assert(trueState); if (trueState != state) C.addTransition(trueState); @@ -325,7 +324,11 @@ void UnixAPIChecker::CheckVallocZero(CheckerContext &C, void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - StringRef FName = C.getCalleeName(CE); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef FName = C.getCalleeName(FD); if (FName.empty()) return; diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 38c9cc1..fab4adf 100644 --- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -69,8 +69,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind, BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(SizeE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SizeE, - report)); + bugreporter::addTrackNullOrUndefValueVisitor(N, SizeE, report); C.EmitReport(report); return; } diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index f7c7c0c..bdc9627 100644 --- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -46,7 +46,7 @@ class WalkAST : public StmtVisitor<WalkAST> { visited. */ PostVisited /**< A CallExpr to this FunctionDecl is in the worklist, and the body has been visited. */ - } K; + }; /// A DenseMap that records visited states of FunctionDecls. llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions; diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp new file mode 100644 index 0000000..884b0fa --- /dev/null +++ b/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -0,0 +1,38 @@ +//===--- APSIntType.cpp - Simple record of the type of APSInts ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" + +using namespace clang; +using namespace ento; + +APSIntType::RangeTestResultKind +APSIntType::testInRange(const llvm::APSInt &Value) const { + // Negative numbers cannot be losslessly converted to unsigned type. + if (IsUnsigned && Value.isSigned() && Value.isNegative()) + return RTR_Below; + + // Signed integers can be converted to signed integers of the same width + // or (if positive) unsigned integers with one fewer bit. + // Unsigned integers can be converted to unsigned integers of the same width + // or signed integers with one more bit. + unsigned MinBits; + if (Value.isSigned()) + MinBits = Value.getMinSignedBits() - IsUnsigned; + else + MinBits = Value.getActiveBits() + !IsUnsigned; + + if (MinBits <= BitWidth) + return RTR_Within; + + if (Value.isSigned() && Value.isNegative()) + return RTR_Below; + else + return RTR_Above; +} diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index eeaed2d..5aac640 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -25,18 +25,18 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnalysisPurgeMode purge, bool eager, bool trim, bool useUnoptimizedCFG, - bool addImplicitDtors, bool addInitializers, + bool addImplicitDtors, bool eagerlyTrimEGraph, AnalysisIPAMode ipa, unsigned inlineMaxStack, unsigned inlineMaxFunctionSize, AnalysisInliningMode IMode, bool NoRetry) - : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), + : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, /*addInitializers=*/true), Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), - AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit), + MaxNodes(maxnodes), MaxVisit(maxvisit), VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge), EagerlyAssume(eager), TrimGraph(trim), EagerlyTrimEGraph(eagerlyTrimEGraph), @@ -59,7 +59,6 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, CreateStoreMgr(ParentAM.CreateStoreMgr), CreateConstraintMgr(ParentAM.CreateConstraintMgr), CheckerMgr(ParentAM.CheckerMgr), - AScope(ScopeDecl), MaxNodes(ParentAM.MaxNodes), MaxVisit(ParentAM.MaxVisit), VisualizeEGDot(ParentAM.VisualizeEGDot), diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 2d9addd..8897756 100644 --- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/raw_ostream.h" @@ -53,18 +54,25 @@ class BasicConstraintManager ProgramState::IntSetTy::Factory ISetFactory; public: BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine) - : SimpleConstraintManager(subengine), + : SimpleConstraintManager(subengine, statemgr.getBasicVals()), ISetFactory(statemgr.getAllocator()) {} - ProgramStateRef assumeSymNE(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); + ProgramStateRef assumeSymEquality(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment, + bool Assumption); - ProgramStateRef assumeSymEQ(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); + ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + return assumeSymEquality(State, Sym, V, Adjustment, false); + } + + ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + return assumeSymEquality(State, Sym, V, Adjustment, true); + } ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, @@ -108,6 +116,9 @@ public: ProgramStateRef removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper); + bool performTest(llvm::APSInt SymVal, llvm::APSInt Adjustment, + BinaryOperator::Opcode Op, llvm::APSInt ComparisonVal); + void print(ProgramStateRef state, raw_ostream &Out, const char* nl, @@ -122,60 +133,94 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr, return new BasicConstraintManager(statemgr, subengine); } -ProgramStateRef -BasicConstraintManager::assumeSymNE(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) { - // First, determine if sym == X, where X+Adjustment != V. - llvm::APSInt Adjusted = V-Adjustment; - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X != Adjusted); - return isFeasible ? state : NULL; - } - - // Second, determine if sym+Adjustment != V. - if (isNotEqual(state, sym, Adjusted)) - return state; - - // If we reach here, sym is not a constant and we don't know if it is != V. - // Make that assumption. - return AddNE(state, sym, Adjusted); +// FIXME: This is a more general utility and should live somewhere else. +bool BasicConstraintManager::performTest(llvm::APSInt SymVal, + llvm::APSInt Adjustment, + BinaryOperator::Opcode Op, + llvm::APSInt ComparisonVal) { + APSIntType Type(Adjustment); + Type.apply(SymVal); + Type.apply(ComparisonVal); + SymVal += Adjustment; + + assert(BinaryOperator::isComparisonOp(Op)); + BasicValueFactory &BVF = getBasicVals(); + const llvm::APSInt *Result = BVF.evalAPSInt(Op, SymVal, ComparisonVal); + assert(Result && "Comparisons should always have valid results."); + + return Result->getBoolValue(); } -ProgramStateRef -BasicConstraintManager::assumeSymEQ(ProgramStateRef state, - SymbolRef sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) { - // First, determine if sym == X, where X+Adjustment != V. - llvm::APSInt Adjusted = V-Adjustment; - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X == Adjusted); - return isFeasible ? state : NULL; +ProgramStateRef +BasicConstraintManager::assumeSymEquality(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment, + bool Assumption) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(V) != APSIntType::RTR_Within) + return Assumption ? NULL : State; + + // Get the symbol type. + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + APSIntType SymbolType = BVF.getAPSIntType(Sym->getType(Ctx)); + + // First, see if the adjusted value is within range for the symbol. + llvm::APSInt Adjusted = AdjustmentType.convert(V) - Adjustment; + if (SymbolType.testInRange(Adjusted) != APSIntType::RTR_Within) + return Assumption ? NULL : State; + + // Now we can do things properly in the symbol space. + SymbolType.apply(Adjusted); + + // Second, determine if sym == X, where X+Adjustment != V. + if (const llvm::APSInt *X = getSymVal(State, Sym)) { + bool IsFeasible = (*X == Adjusted); + return (IsFeasible == Assumption) ? State : NULL; } - // Second, determine if sym+Adjustment != V. - if (isNotEqual(state, sym, Adjusted)) - return NULL; + // Third, determine if we already know sym+Adjustment != V. + if (isNotEqual(State, Sym, Adjusted)) + return Assumption ? NULL : State; - // If we reach here, sym is not a constant and we don't know if it is == V. - // Make that assumption. - return AddEQ(state, sym, Adjusted); + // If we reach here, sym is not a constant and we don't know if it is != V. + // Make the correct assumption. + if (Assumption) + return AddEQ(State, Sym, Adjusted); + else + return AddNE(State, Sym, Adjusted); } // The logic for these will be handled in another ConstraintManager. +// Approximate it here anyway by handling some edge cases. ProgramStateRef BasicConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Is 'V' the smallest possible value? - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { + APSIntType ComparisonType(V), AdjustmentType(Adjustment); + + // Is 'V' out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (V > ComparisonType.convert(Max)) { + // This path is trivially feasible. + return state; + } + + // Is 'V' the smallest possible value, or out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (V <= ComparisonType.convert(Min)) { // sym cannot be any value less than 'V'. This path is infeasible. return NULL; } + // Reject a path if the value of sym is a constant X and !(X+Adj < V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_LT, V); + return isFeasible ? state : NULL; + } + // FIXME: For now have assuming x < y be the same as assuming sym != V; return assumeSymNE(state, sym, V, Adjustment); } @@ -185,12 +230,28 @@ BasicConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Is 'V' the largest possible value? - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { + APSIntType ComparisonType(V), AdjustmentType(Adjustment); + + // Is 'V' the largest possible value, or out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (V >= ComparisonType.convert(Max)) { // sym cannot be any value greater than 'V'. This path is infeasible. return NULL; } + // Is 'V' out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (V < ComparisonType.convert(Min)) { + // This path is trivially feasible. + return state; + } + + // Reject a path if the value of sym is a constant X and !(X+Adj > V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_GT, V); + return isFeasible ? state : NULL; + } + // FIXME: For now have assuming x > y be the same as assuming sym != V; return assumeSymNE(state, sym, V, Adjustment); } @@ -200,25 +261,33 @@ BasicConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Reject a path if the value of sym is a constant X and !(X+Adj >= V). - if (const llvm::APSInt *X = getSymVal(state, sym)) { - bool isFeasible = (*X >= V-Adjustment); - return isFeasible ? state : NULL; - } + APSIntType ComparisonType(V), AdjustmentType(Adjustment); - // Sym is not a constant, but it is worth looking to see if V is the - // maximum integer value. - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { - llvm::APSInt Adjusted = V-Adjustment; + // Is 'V' the largest possible value, or out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + ComparisonType.apply(Max); - // If we know that sym != V (after adjustment), then this condition - // is infeasible since there is no other value greater than V. - bool isFeasible = !isNotEqual(state, sym, Adjusted); - - // If the path is still feasible then as a consequence we know that + if (V > Max) { + // sym cannot be any value greater than 'V'. This path is infeasible. + return NULL; + } else if (V == Max) { + // If the path is feasible then as a consequence we know that // 'sym+Adjustment == V' because there are no larger values. // Add this constraint. - return isFeasible ? AddEQ(state, sym, Adjusted) : NULL; + return assumeSymEQ(state, sym, V, Adjustment); + } + + // Is 'V' out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (V < ComparisonType.convert(Min)) { + // This path is trivially feasible. + return state; + } + + // Reject a path if the value of sym is a constant X and !(X+Adj >= V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_GE, V); + return isFeasible ? state : NULL; } return state; @@ -229,25 +298,33 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { - // Reject a path if the value of sym is a constant X and !(X+Adj <= V). - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X <= V-Adjustment); - return isFeasible ? state : NULL; - } + APSIntType ComparisonType(V), AdjustmentType(Adjustment); - // Sym is not a constant, but it is worth looking to see if V is the - // minimum integer value. - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { - llvm::APSInt Adjusted = V-Adjustment; + // Is 'V' out of range above the type? + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (V > ComparisonType.convert(Max)) { + // This path is trivially feasible. + return state; + } - // If we know that sym != V (after adjustment), then this condition - // is infeasible since there is no other value less than V. - bool isFeasible = !isNotEqual(state, sym, Adjusted); + // Is 'V' the smallest possible value, or out of range below the type? + llvm::APSInt Min = AdjustmentType.getMinValue(); + ComparisonType.apply(Min); - // If the path is still feasible then as a consequence we know that + if (V < Min) { + // sym cannot be any value less than 'V'. This path is infeasible. + return NULL; + } else if (V == Min) { + // If the path is feasible then as a consequence we know that // 'sym+Adjustment == V' because there are no smaller values. // Add this constraint. - return isFeasible ? AddEQ(state, sym, Adjusted) : NULL; + return assumeSymEQ(state, sym, V, Adjustment); + } + + // Reject a path if the value of sym is a constant X and !(X+Adj >= V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = performTest(*X, Adjustment, BO_LE, V); + return isFeasible ? state : NULL; } return state; @@ -256,8 +333,10 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state, ProgramStateRef BasicConstraintManager::AddEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) { - // Create a new state with the old binding replaced. - return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V)); + // Now that we have an actual value, we can throw out the NE-set. + // Create a new state with the old bindings replaced. + state = state->remove<ConstNotEq>(sym); + return state->set<ConstEq>(sym, &getBasicVals().getValue(V)); } ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state, @@ -269,7 +348,7 @@ ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state, ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); // Now add V to the NE set. - S = ISetFactory.add(S, &state->getBasicVals().getValue(V)); + S = ISetFactory.add(S, &getBasicVals().getValue(V)); // Create a new state with the old binding replaced. return state->set<ConstNotEq>(sym, S); @@ -289,7 +368,7 @@ bool BasicConstraintManager::isNotEqual(ProgramStateRef state, const ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym); // See if V is present in the NE-set. - return T ? T->contains(&state->getBasicVals().getValue(V)) : false; + return T ? T->contains(&getBasicVals().getValue(V)) : false; } bool BasicConstraintManager::isEqual(ProgramStateRef state, diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index fe96700..20c7361 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -13,6 +13,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index a264212..7ba2fa7 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -48,6 +48,10 @@ static inline const Stmt *GetStmt(const ProgramPoint &P) { return SP->getStmt(); else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) return BE->getSrc()->getTerminator(); + else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) + return CE->getCallExpr(); + else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) + return CEE->getCalleeContext()->getCallSite(); return 0; } @@ -427,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); PD.getActivePath().push_front(C); @@ -437,21 +441,23 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + // Flush all locations, and pop the active path. + bool VisitedEntireCall = PD.isWithinCall(); PD.popActivePath(); - // The current active path should never be empty. Either we - // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active - // path is not a PathDiagnosticCallPiece, it means that the + + // Either we just added a bunch of stuff to the top-level path, or + // we have a previous CallExitEnd. If the former, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within // a new PathDiagnosticCallPiece. - assert(!PD.getActivePath().empty()); - PathDiagnosticCallPiece *C = - dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); - if (!C) { + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + } else { const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); } + C->setCallee(*CE, SMgr); if (!CallStack.empty()) { assert(CallStack.back().first == C); @@ -864,6 +870,7 @@ public: void rawAddEdge(PathDiagnosticLocation NewLoc); void addContext(const Stmt *S); + void addContext(const PathDiagnosticLocation &L); void addExtendedContext(const Stmt *S); }; } // end anonymous namespace @@ -1031,7 +1038,10 @@ void EdgeBuilder::addContext(const Stmt *S) { return; PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC); + addContext(L); +} +void EdgeBuilder::addContext(const PathDiagnosticLocation &L) { while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1051,6 +1061,78 @@ void EdgeBuilder::addContext(const Stmt *S) { CLocs.push_back(L); } +// Cone-of-influence: support the reverse propagation of "interesting" symbols +// and values by tracing interesting calculations backwards through evaluated +// expressions along a path. This is probably overly complicated, but the idea +// is that if an expression computed an "interesting" value, the child +// expressions are are also likely to be "interesting" as well (which then +// propagates to the values they in turn compute). This reverse propagation +// is needed to track interesting correlations across function call boundaries, +// where formal arguments bind to actual arguments, etc. This is also needed +// because the constraint solver sometimes simplifies certain symbolic values +// into constants when appropriate, and this complicates reasoning about +// interesting values. +typedef llvm::DenseSet<const Expr *> InterestingExprs; + +static void reversePropagateIntererstingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const Expr *Ex, + const LocationContext *LCtx) { + SVal V = State->getSVal(Ex, LCtx); + if (!(R.isInteresting(V) || IE.count(Ex))) + return; + + switch (Ex->getStmtClass()) { + default: + if (!isa<CastExpr>(Ex)) + break; + // Fall through. + case Stmt::BinaryOperatorClass: + case Stmt::UnaryOperatorClass: { + for (Stmt::const_child_iterator CI = Ex->child_begin(), + CE = Ex->child_end(); + CI != CE; ++CI) { + if (const Expr *child = dyn_cast_or_null<Expr>(*CI)) { + IE.insert(child); + SVal ChildV = State->getSVal(child, LCtx); + R.markInteresting(ChildV); + } + break; + } + } + } + + R.markInteresting(V); +} + +static void reversePropagateInterestingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const LocationContext *CalleeCtx, + const LocationContext *CallerCtx) +{ + // FIXME: Handle non-CallExpr-based CallEvents. + const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame(); + const Stmt *CallSite = Callee->getCallSite(); + if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { + FunctionDecl::param_const_iterator PI = FD->param_begin(), + PE = FD->param_end(); + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + for (; AI != AE && PI != PE; ++AI, ++PI) { + if (const Expr *ArgE = *AI) { + if (const ParmVarDecl *PD = *PI) { + Loc LV = State->getLValue(PD, CalleeCtx); + if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV))) + IE.insert(ArgE); + } + } + } + } + } +} + static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, @@ -1058,6 +1140,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); StackDiagVector CallStack; + InterestingExprs IE; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { @@ -1066,16 +1149,27 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { - const StackFrameContext *LCtx = - CE->getLocationContext()->getCurrentStackFrame(); - PathDiagnosticLocation Loc(LCtx->getCallSite(), - PDB.getSourceManager(), - LCtx); - EB.addEdge(Loc, true); - EB.flushLocations(); + if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { + if (const Expr *Ex = PS->getStmtAs<Expr>()) + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + const Stmt *S = CE->getCalleeContext()->getCallSite(); + if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SM); + + EB.addEdge(C->callReturn, true); + EB.flushLocations(); + PD.getActivePath().push_front(C); PD.pushActivePath(&C->path); CallStack.push_back(StackDiagPair(C, N)); @@ -1092,26 +1186,26 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EB.addEdge(pos); // Flush all locations, and pop the active path. + bool VisitedEntireCall = PD.isWithinCall(); EB.flushLocations(); PD.popActivePath(); - assert(!PD.getActivePath().empty()); PDB.LC = N->getLocationContext(); - // The current active path should never be empty. Either we - // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active - // path is not a PathDiagnosticCallPiece, it means that the + // Either we just added a bunch of stuff to the top-level path, or + // we have a previous CallExitEnd. If the former, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within // a new PathDiagnosticCallPiece. - PathDiagnosticCallPiece *C = - dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); - if (!C) { - const Decl * Caller = CE->getLocationContext()->getDecl(); + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + } else { + const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); } + C->setCallee(*CE, SM); - EB.addContext(CE->getCallExpr()); + EB.addContext(C->getLocation()); if (!CallStack.empty()) { assert(CallStack.back().first == C); @@ -1127,7 +1221,19 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PDB.LC = N->getLocationContext(); // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + // Does this represent entering a call? If so, look at propagating + // interesting symbols across call boundaries. + if (NextNode) { + const LocationContext *CallerCtx = NextNode->getLocationContext(); + const LocationContext *CalleeCtx = PDB.LC; + if (CallerCtx != CalleeCtx) { + reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), + CalleeCtx, CallerCtx); + } + } + const CFGBlock &Blk = *BE->getSrc(); const Stmt *Term = Blk.getTerminator(); @@ -1430,9 +1536,12 @@ void BugReporter::FlushReports() { I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) const_cast<BugType*>(*I)->FlushReports(*this); - typedef llvm::FoldingSet<BugReportEquivClass> SetTy; - for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ - BugReportEquivClass& EQ = *EI; + // We need to flush reports in deterministic order to ensure the order + // of the reports is consistent between runs. + typedef std::vector<BugReportEquivClass *> ContVecTy; + for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end(); + EI != EE; ++EI){ + BugReportEquivClass& EQ = **EI; FlushReport(EQ); } @@ -1768,9 +1877,11 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, } while(finalReportConfigToken != originalReportConfigToken); // Finally, prune the diagnostic path of uninteresting stuff. - bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); - assert(hasSomethingInteresting); - (void) hasSomethingInteresting; + if (R->shouldPrunePath()) { + bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); + assert(hasSomethingInteresting); + (void) hasSomethingInteresting; + } } void BugReporter::Register(BugType *BT) { diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 6532486..46aa9e2 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -34,15 +34,23 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { // a[0], p->f, *p const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { - if (U->getOpcode() == UO_Deref) - return U->getSubExpr()->IgnoreParenCasts(); - } - else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { - return ME->getBase()->IgnoreParenCasts(); - } - else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { - return AE->getBase(); + while (true) { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) { + assert(B->isAssignmentOp()); + S = B->getLHS()->IgnoreParenCasts(); + continue; + } + else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { + if (U->getOpcode() == UO_Deref) + return U->getSubExpr()->IgnoreParenCasts(); + } + else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { + return ME->getBase()->IgnoreParenCasts(); + } + else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { + return AE->getBase(); + } + break; } return NULL; @@ -55,14 +63,6 @@ const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) { return NULL; } -const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) { - // Callee is checked as a PreVisit to the CallExpr. - const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) - return CE->getCallee(); - return NULL; -} - const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) @@ -119,10 +119,16 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, return NULL; if (!StoreSite) { - const ExplodedNode *Node = N, *Last = NULL; + // Make sure the region is actually bound to value V here. + // This is necessary because the region may not actually be live at the + // report's error node. + if (N->getState()->getSVal(R) != V) + return NULL; - for ( ; Node ; Node = Node->getFirstPred()) { + const ExplodedNode *Node = N, *Last = N; + // Now look for the store of V. + for ( ; Node ; Node = Node->getFirstPred()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { if (const PostStmt *P = Node->getLocationAs<PostStmt>()) if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) @@ -137,9 +143,11 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, // looking for store sites. if (Node->getState()->getSVal(R) != V) break; + + Last = Node; } - if (!Node || !Last) { + if (!Node) { satisfied = true; return NULL; } @@ -189,6 +197,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, os << "declared without an initial value"; } } + else { + os << "initialized here"; + } } } @@ -215,7 +226,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, << " is assigned to "; } else - return NULL; + os << "Value assigned to "; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << '\'' << *VR->getDecl() << '\''; @@ -285,12 +296,11 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return NULL; } -BugReporterVisitor * -bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, - const Stmt *S, - BugReport *report) { +void bugreporter::addTrackNullOrUndefValueVisitor(const ExplodedNode *N, + const Stmt *S, + BugReport *report) { if (!S || !N) - return 0; + return; ProgramStateManager &StateMgr = N->getState()->getStateManager(); @@ -306,7 +316,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, } if (!N) - return 0; + return; ProgramStateRef state = N->getState(); @@ -320,10 +330,18 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? - SVal V = state->getSVal(loc::MemRegionVal(R)); + SVal V = state->getRawSVal(loc::MemRegionVal(R)); report->markInteresting(R); report->markInteresting(V); - return new FindLastStoreBRVisitor(V, R); + + if (V.getAsLocSymbol()) { + BugReporterVisitor *ConstraintTracker + = new TrackConstraintBRVisitor(cast<loc::MemRegionVal>(V), false); + report->addVisitor(ConstraintTracker); + } + + report->addVisitor(new FindLastStoreBRVisitor(V, R)); + return; } } } @@ -343,11 +361,10 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, if (R) { report->markInteresting(R); - return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); + report->addVisitor(new TrackConstraintBRVisitor(loc::MemRegionVal(R), + false)); } } - - return 0; } BugReporterVisitor * @@ -389,7 +406,7 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver, &BR)); + bugreporter::addTrackNullOrUndefValueVisitor(N, Receiver, &BR); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -699,6 +716,9 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) { + // FIXME: If there's already a constraint tracker for this variable, + // we shouldn't emit anything here (c.f. the double note in + // test/Analysis/inlining/path-notes.c) SmallString<256> buf; llvm::raw_svector_ostream Out(buf); Out << "Assuming " << LhsString << " is "; diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 1ea25bd..b16b233d 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -1,14 +1,14 @@ set(LLVM_LINK_COMPONENTS support) -set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) - add_clang_library(clangStaticAnalyzerCore AnalysisManager.cpp + APSIntType.cpp BasicConstraintManager.cpp BasicValueFactory.cpp BlockCounter.cpp BugReporter.cpp BugReporterVisitors.cpp + CallEvent.cpp Checker.cpp CheckerContext.cpp CheckerHelpers.cpp @@ -25,7 +25,6 @@ add_clang_library(clangStaticAnalyzerCore FunctionSummary.cpp HTMLDiagnostics.cpp MemRegion.cpp - ObjCMessage.cpp PathDiagnostic.cpp PlistDiagnostics.cpp ProgramState.cpp @@ -41,5 +40,19 @@ add_clang_library(clangStaticAnalyzerCore TextPathDiagnostics.cpp ) -add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNodes) +add_dependencies(clangStaticAnalyzerCore + ClangAttrClasses + ClangAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangStmtNodes + ) + +target_link_libraries(clangStaticAnalyzerCore + clangBasic + clangLex + clangAST + clangFrontend + clangRewrite + ) diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp new file mode 100644 index 0000000..e3f4c61 --- /dev/null +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -0,0 +1,856 @@ +//===- Calls.cpp - Wrapper for all function and method calls ------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file This file defines CallEvent and its subclasses, which represent path- +/// sensitive instances of different kinds of function and method calls +/// (C, C++, and Objective-C). +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/AST/ParentMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringExtras.h" + +using namespace clang; +using namespace ento; + +QualType CallEvent::getResultType() const { + QualType ResultTy = getDeclaredResultType(); + + if (ResultTy.isNull()) + ResultTy = getOriginExpr()->getType(); + + return ResultTy; +} + +static bool isCallbackArg(SVal V, QualType T) { + // If the parameter is 0, it's harmless. + if (V.isZeroConstant()) + return false; + + // If a parameter is a block or a callback, assume it can modify pointer. + if (T->isBlockPointerType() || + T->isFunctionPointerType() || + T->isObjCSelType()) + return true; + + // Check if a callback is passed inside a struct (for both, struct passed by + // reference and by value). Dig just one level into the struct for now. + + if (isa<PointerType>(T) || isa<ReferenceType>(T)) + T = T->getPointeeType(); + + if (const RecordType *RT = T->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) { + QualType FieldT = I->getType(); + if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType()) + return true; + } + } + + return false; +} + +bool CallEvent::hasNonZeroCallbackArg() const { + unsigned NumOfArgs = getNumArgs(); + + // If calling using a function pointer, assume the function does not + // have a callback. TODO: We could check the types of the arguments here. + if (!getDecl()) + return false; + + unsigned Idx = 0; + for (CallEvent::param_type_iterator I = param_type_begin(), + E = param_type_end(); + I != E && Idx < NumOfArgs; ++I, ++Idx) { + if (NumOfArgs <= Idx) + break; + + if (isCallbackArg(getArgSVal(Idx), *I)) + return true; + } + + return false; +} + +/// \brief Returns true if a type is a pointer-to-const or reference-to-const +/// with no further indirection. +static bool isPointerToConst(QualType Ty) { + QualType PointeeTy = Ty->getPointeeType(); + if (PointeeTy == QualType()) + return false; + if (!PointeeTy.isConstQualified()) + return false; + if (PointeeTy->isAnyPointerType()) + return false; + return true; +} + +// Try to retrieve the function declaration and find the function parameter +// types which are pointers/references to a non-pointer const. +// We will not invalidate the corresponding argument regions. +static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, + const CallEvent &Call) { + unsigned Idx = 0; + for (CallEvent::param_type_iterator I = Call.param_type_begin(), + E = Call.param_type_end(); + I != E; ++I, ++Idx) { + if (isPointerToConst(*I)) + PreserveArgs.insert(Idx); + } +} + +ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, + ProgramStateRef Orig) const { + ProgramStateRef Result = (Orig ? Orig : getState()); + + SmallVector<const MemRegion *, 8> RegionsToInvalidate; + getExtraInvalidatedRegions(RegionsToInvalidate); + + // Indexes of arguments whose values will be preserved by the call. + llvm::SmallSet<unsigned, 1> PreserveArgs; + if (!argumentsMayEscape()) + findPtrToConstParams(PreserveArgs, *this); + + for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) { + if (PreserveArgs.count(Idx)) + continue; + + SVal V = getArgSVal(Idx); + + // If we are passing a location wrapped as an integer, unwrap it and + // invalidate the values referred by the location. + if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) + V = Wrapped->getLoc(); + else if (!isa<Loc>(V)) + continue; + + if (const MemRegion *R = V.getAsRegion()) { + // Invalidate the value of the variable passed by reference. + + // Are we dealing with an ElementRegion? If the element type is + // a basic integer type (e.g., char, int) and the underlying region + // is a variable region then strip off the ElementRegion. + // FIXME: We really need to think about this for the general case + // as sometimes we are reasoning about arrays and other times + // about (char*), etc., is just a form of passing raw bytes. + // e.g., void *p = alloca(); foo((char*)p); + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + // Checking for 'integral type' is probably too promiscuous, but + // we'll leave it in for now until we have a systematic way of + // handling all of these cases. Eventually we need to come up + // with an interface to StoreManager so that this logic can be + // appropriately delegated to the respective StoreManagers while + // still allowing us to do checker-specific logic (e.g., + // invalidating reference counts), probably via callbacks. + if (ER->getElementType()->isIntegralOrEnumerationType()) { + const MemRegion *superReg = ER->getSuperRegion(); + if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || + isa<ObjCIvarRegion>(superReg)) + R = cast<TypedRegion>(superReg); + } + // FIXME: What about layers of ElementRegions? + } + + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + RegionsToInvalidate.push_back(R); + } + } + + // Invalidate designated regions using the batch invalidation API. + // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate + // global variables. + return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(), + BlockCount, getLocationContext(), + /*Symbols=*/0, this); +} + +ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, + const ProgramPointTag *Tag) const { + if (const Expr *E = getOriginExpr()) { + if (IsPreVisit) + return PreStmt(E, getLocationContext(), Tag); + return PostStmt(E, getLocationContext(), Tag); + } + + const Decl *D = getDecl(); + assert(D && "Cannot get a program point without a statement or decl"); + + SourceLocation Loc = getSourceRange().getBegin(); + if (IsPreVisit) + return PreImplicitCall(D, Loc, getLocationContext(), Tag); + return PostImplicitCall(D, Loc, getLocationContext(), Tag); +} + +SVal CallEvent::getArgSVal(unsigned Index) const { + const Expr *ArgE = getArgExpr(Index); + if (!ArgE) + return UnknownVal(); + return getSVal(ArgE); +} + +SourceRange CallEvent::getArgSourceRange(unsigned Index) const { + const Expr *ArgE = getArgExpr(Index); + if (!ArgE) + return SourceRange(); + return ArgE->getSourceRange(); +} + +void CallEvent::dump(raw_ostream &Out) const { + ASTContext &Ctx = getState()->getStateManager().getContext(); + if (const Expr *E = getOriginExpr()) { + E->printPretty(Out, Ctx, 0, Ctx.getPrintingPolicy()); + Out << "\n"; + return; + } + + if (const Decl *D = getDecl()) { + Out << "Call to "; + D->print(Out, Ctx.getPrintingPolicy()); + return; + } + + // FIXME: a string representation of the kind would be nice. + Out << "Unknown call (type " << getKind() << ")"; +} + + +bool CallEvent::mayBeInlined(const Stmt *S) { + // FIXME: Kill this. + return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) + || isa<CXXConstructExpr>(S); +} + +static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, + CallEvent::BindingsTy &Bindings, + SValBuilder &SVB, + const CallEvent &Call, + CallEvent::param_iterator I, + CallEvent::param_iterator E) { + MemRegionManager &MRMgr = SVB.getRegionManager(); + + unsigned Idx = 0; + for (; I != E; ++I, ++Idx) { + const ParmVarDecl *ParamDecl = *I; + assert(ParamDecl && "Formal parameter has no decl?"); + + SVal ArgVal = Call.getArgSVal(Idx); + if (!ArgVal.isUnknown()) { + Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); + Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + } + } + + // FIXME: Variadic arguments are not handled at all right now. +} + + +CallEvent::param_iterator AnyFunctionCall::param_begin() const { + const FunctionDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_begin(); +} + +CallEvent::param_iterator AnyFunctionCall::param_end() const { + const FunctionDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_end(); +} + +void AnyFunctionCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const FunctionDecl *D = cast<FunctionDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->param_begin(), D->param_end()); +} + +QualType AnyFunctionCall::getDeclaredResultType() const { + const FunctionDecl *D = getDecl(); + if (!D) + return QualType(); + + return D->getResultType(); +} + +bool AnyFunctionCall::argumentsMayEscape() const { + if (hasNonZeroCallbackArg()) + return true; + + const FunctionDecl *D = getDecl(); + if (!D) + return true; + + const IdentifierInfo *II = D->getIdentifier(); + if (!II) + return true; + + // This set of "escaping" APIs is + + // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a + // value into thread local storage. The value can later be retrieved with + // 'void *ptheread_getspecific(pthread_key)'. So even thought the + // parameter is 'const void *', the region escapes through the call. + if (II->isStr("pthread_setspecific")) + return true; + + // - xpc_connection_set_context stores a value which can be retrieved later + // with xpc_connection_get_context. + if (II->isStr("xpc_connection_set_context")) + return true; + + // - funopen - sets a buffer for future IO calls. + if (II->isStr("funopen")) + return true; + + StringRef FName = II->getName(); + + // - CoreFoundation functions that end with "NoCopy" can free a passed-in + // buffer even if it is const. + if (FName.endswith("NoCopy")) + return true; + + // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) + return true; + + // - Many CF containers allow objects to escape through custom + // allocators/deallocators upon container construction. (PR12101) + if (FName.startswith("CF") || FName.startswith("CG")) { + return StrInStrNoCase(FName, "InsertValue") != StringRef::npos || + StrInStrNoCase(FName, "AddValue") != StringRef::npos || + StrInStrNoCase(FName, "SetValue") != StringRef::npos || + StrInStrNoCase(FName, "WithData") != StringRef::npos || + StrInStrNoCase(FName, "AppendValue") != StringRef::npos || + StrInStrNoCase(FName, "SetAttribute") != StringRef::npos; + } + + return false; +} + + +const FunctionDecl *SimpleCall::getDecl() const { + const FunctionDecl *D = getOriginExpr()->getDirectCallee(); + if (D) + return D; + + return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl(); +} + + +const FunctionDecl *CXXInstanceCall::getDecl() const { + const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr()); + if (!CE) + return AnyFunctionCall::getDecl(); + + const FunctionDecl *D = CE->getDirectCallee(); + if (D) + return D; + + return getSVal(CE->getCallee()).getAsFunctionDecl(); +} + +void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { + if (const MemRegion *R = getCXXThisVal().getAsRegion()) + Regions.push_back(R); +} + +static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){ + const MemRegion *R = ThisVal.getAsRegion(); + if (!R) + return 0; + + const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R->StripCasts()); + if (!TR) + return 0; + + const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl(); + if (!RD) + return 0; + + const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD); + const FunctionDecl *Definition; + if (!Result->hasBody(Definition)) + return 0; + + return cast<CXXMethodDecl>(Definition); +} + + +RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { + const Decl *D = getDecl(); + if (!D) + return RuntimeDefinition(); + + const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); + if (!MD->isVirtual()) + return AnyFunctionCall::getRuntimeDefinition(); + + // If the method is virtual, see if we can find the actual implementation + // based on context-sensitivity. + // FIXME: Virtual method calls behave differently when an object is being + // constructed or destructed. It's not as simple as "no devirtualization" + // because a /partially/ constructed object can be referred to through a + // base pointer. We'll eventually want to use DynamicTypeInfo here. + if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) + return RuntimeDefinition(Devirtualized); + + return RuntimeDefinition(); +} + +void CXXInstanceCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); + + // Handle the binding of 'this' in the new stack frame. + // We need to make sure we have the proper layering of CXXBaseObjectRegions. + SVal ThisVal = getCXXThisVal(); + if (!ThisVal.isUnknown()) { + ProgramStateManager &StateMgr = getState()->getStateManager(); + SValBuilder &SVB = StateMgr.getSValBuilder(); + + const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); + + if (const MemRegion *ThisReg = ThisVal.getAsRegion()) { + ASTContext &Ctx = SVB.getContext(); + const CXXRecordDecl *Class = MD->getParent(); + QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class)); + + // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager. + bool Failed; + ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed); + assert(!Failed && "Calling an incorrectly devirtualized method"); + + // If we couldn't build the correct cast, just strip off all casts. + if (ThisVal.isUnknown()) + ThisVal = loc::MemRegionVal(ThisReg->StripCasts()); + } + + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } +} + + + +const Expr *CXXMemberCall::getCXXThisExpr() const { + return getOriginExpr()->getImplicitObjectArgument(); +} + + +const Expr *CXXMemberOperatorCall::getCXXThisExpr() const { + return getOriginExpr()->getArg(0); +} + + +const BlockDataRegion *BlockCall::getBlockRegion() const { + const Expr *Callee = getOriginExpr()->getCallee(); + const MemRegion *DataReg = getSVal(Callee).getAsRegion(); + + return dyn_cast_or_null<BlockDataRegion>(DataReg); +} + +CallEvent::param_iterator BlockCall::param_begin() const { + const BlockDecl *D = getBlockDecl(); + if (!D) + return 0; + return D->param_begin(); +} + +CallEvent::param_iterator BlockCall::param_end() const { + const BlockDecl *D = getBlockDecl(); + if (!D) + return 0; + return D->param_end(); +} + +void BlockCall::getExtraInvalidatedRegions(RegionList &Regions) const { + // FIXME: This also needs to invalidate captured globals. + if (const MemRegion *R = getBlockRegion()) + Regions.push_back(R); +} + +void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const BlockDecl *D = cast<BlockDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->param_begin(), D->param_end()); +} + + +QualType BlockCall::getDeclaredResultType() const { + const BlockDataRegion *BR = getBlockRegion(); + if (!BR) + return QualType(); + QualType BlockTy = BR->getCodeRegion()->getLocationType(); + return cast<FunctionType>(BlockTy->getPointeeType())->getResultType(); +} + + +SVal CXXConstructorCall::getCXXThisVal() const { + if (Data) + return loc::MemRegionVal(static_cast<const MemRegion *>(Data)); + return UnknownVal(); +} + +void CXXConstructorCall::getExtraInvalidatedRegions(RegionList &Regions) const { + if (Data) + Regions.push_back(static_cast<const MemRegion *>(Data)); +} + +void CXXConstructorCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); + + SVal ThisVal = getCXXThisVal(); + if (!ThisVal.isUnknown()) { + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } +} + + + +SVal CXXDestructorCall::getCXXThisVal() const { + if (Data) + return loc::MemRegionVal(static_cast<const MemRegion *>(Data)); + return UnknownVal(); +} + + +CallEvent::param_iterator ObjCMethodCall::param_begin() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_begin(); +} + +CallEvent::param_iterator ObjCMethodCall::param_end() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return 0; + + return D->param_end(); +} + +void +ObjCMethodCall::getExtraInvalidatedRegions(RegionList &Regions) const { + if (const MemRegion *R = getReceiverSVal().getAsRegion()) + Regions.push_back(R); +} + +QualType ObjCMethodCall::getDeclaredResultType() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return QualType(); + + return D->getResultType(); +} + +SVal ObjCMethodCall::getReceiverSVal() const { + // FIXME: Is this the best way to handle class receivers? + if (!isInstanceMessage()) + return UnknownVal(); + + if (const Expr *RecE = getOriginExpr()->getInstanceReceiver()) + return getSVal(RecE); + + // An instance message with no expression means we are sending to super. + // In this case the object reference is the same as 'self'. + const LocationContext *LCtx = getLocationContext(); + const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); + assert(SelfDecl && "No message receiver Expr, but not in an ObjC method"); + return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx)); +} + +SourceRange ObjCMethodCall::getSourceRange() const { + switch (getMessageKind()) { + case OCM_Message: + return getOriginExpr()->getSourceRange(); + case OCM_PropertyAccess: + case OCM_Subscript: + return getContainingPseudoObjectExpr()->getSourceRange(); + } + llvm_unreachable("unknown message kind"); +} + +typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy; + +const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const { + assert(Data != 0 && "Lazy lookup not yet performed."); + assert(getMessageKind() != OCM_Message && "Explicit message send."); + return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer(); +} + +ObjCMessageKind ObjCMethodCall::getMessageKind() const { + if (Data == 0) { + ParentMap &PM = getLocationContext()->getParentMap(); + const Stmt *S = PM.getParent(getOriginExpr()); + if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) { + const Expr *Syntactic = POE->getSyntacticForm(); + + // This handles the funny case of assigning to the result of a getter. + // This can happen if the getter returns a non-const reference. + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic)) + Syntactic = BO->getLHS(); + + ObjCMessageKind K; + switch (Syntactic->getStmtClass()) { + case Stmt::ObjCPropertyRefExprClass: + K = OCM_PropertyAccess; + break; + case Stmt::ObjCSubscriptRefExprClass: + K = OCM_Subscript; + break; + default: + // FIXME: Can this ever happen? + K = OCM_Message; + break; + } + + if (K != OCM_Message) { + const_cast<ObjCMethodCall *>(this)->Data + = ObjCMessageDataTy(POE, K).getOpaqueValue(); + assert(getMessageKind() == K); + return K; + } + } + + const_cast<ObjCMethodCall *>(this)->Data + = ObjCMessageDataTy(0, 1).getOpaqueValue(); + assert(getMessageKind() == OCM_Message); + return OCM_Message; + } + + ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data); + if (!Info.getPointer()) + return OCM_Message; + return static_cast<ObjCMessageKind>(Info.getInt()); +} + + +bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, + Selector Sel) const { + assert(IDecl); + const SourceManager &SM = + getState()->getStateManager().getContext().getSourceManager(); + + // If the class interface is declared inside the main file, assume it is not + // subcassed. + // TODO: It could actually be subclassed if the subclass is private as well. + // This is probably very rare. + SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc(); + if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc)) + return false; + + + // We assume that if the method is public (declared outside of main file) or + // has a parent which publicly declares the method, the method could be + // overridden in a subclass. + + // Find the first declaration in the class hierarchy that declares + // the selector. + ObjCMethodDecl *D = 0; + while (true) { + D = IDecl->lookupMethod(Sel, true); + + // Cannot find a public definition. + if (!D) + return false; + + // If outside the main file, + if (D->getLocation().isValid() && !SM.isFromMainFile(D->getLocation())) + return true; + + if (D->isOverriding()) { + // Search in the superclass on the next iteration. + IDecl = D->getClassInterface(); + if (!IDecl) + return false; + + IDecl = IDecl->getSuperClass(); + if (!IDecl) + return false; + + continue; + } + + return false; + }; + + llvm_unreachable("The while loop should always terminate."); +} + +RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { + const ObjCMessageExpr *E = getOriginExpr(); + assert(E); + Selector Sel = E->getSelector(); + + if (E->isInstanceMessage()) { + + // Find the the receiver type. + const ObjCObjectPointerType *ReceiverT = 0; + bool CanBeSubClassed = false; + QualType SupersType = E->getSuperType(); + const MemRegion *Receiver = 0; + + if (!SupersType.isNull()) { + // Super always means the type of immediate predecessor to the method + // where the call occurs. + ReceiverT = cast<ObjCObjectPointerType>(SupersType); + } else { + Receiver = getReceiverSVal().getAsRegion(); + if (!Receiver) + return RuntimeDefinition(); + + DynamicTypeInfo DTI = getState()->getDynamicTypeInfo(Receiver); + QualType DynType = DTI.getType(); + CanBeSubClassed = DTI.canBeASubClass(); + ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType); + + if (ReceiverT && CanBeSubClassed) + if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) + if (!canBeOverridenInSubclass(IDecl, Sel)) + CanBeSubClassed = false; + } + + // Lookup the method implementation. + if (ReceiverT) + if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { + const ObjCMethodDecl *MD = IDecl->lookupPrivateMethod(Sel); + if (CanBeSubClassed) + return RuntimeDefinition(MD, Receiver); + else + return RuntimeDefinition(MD, 0); + } + + } else { + // This is a class method. + // If we have type info for the receiver class, we are calling via + // class name. + if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) { + // Find/Return the method implementation. + return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel)); + } + } + + return RuntimeDefinition(); +} + +void ObjCMethodCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const ObjCMethodDecl *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->param_begin(), D->param_end()); + + SVal SelfVal = getReceiverSVal(); + if (!SelfVal.isUnknown()) { + const VarDecl *SelfD = CalleeCtx->getAnalysisDeclContext()->getSelfDecl(); + MemRegionManager &MRMgr = SVB.getRegionManager(); + Loc SelfLoc = SVB.makeLoc(MRMgr.getVarRegion(SelfD, CalleeCtx)); + Bindings.push_back(std::make_pair(SelfLoc, SelfVal)); + } +} + +CallEventRef<> +CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, + const LocationContext *LCtx) { + if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) + return create<CXXMemberCall>(MCE, State, LCtx); + + if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) { + const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) + if (MD->isInstance()) + return create<CXXMemberOperatorCall>(OpCE, State, LCtx); + + } else if (CE->getCallee()->getType()->isBlockPointerType()) { + return create<BlockCall>(CE, State, LCtx); + } + + // Otherwise, it's a normal function call, static member function call, or + // something we can't reason about. + return create<FunctionCall>(CE, State, LCtx); +} + + +CallEventRef<> +CallEventManager::getCaller(const StackFrameContext *CalleeCtx, + ProgramStateRef State) { + const LocationContext *ParentCtx = CalleeCtx->getParent(); + const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame(); + assert(CallerCtx && "This should not be used for top-level stack frames"); + + const Stmt *CallSite = CalleeCtx->getCallSite(); + + if (CallSite) { + if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite)) + return getSimpleCall(CE, State, CallerCtx); + + switch (CallSite->getStmtClass()) { + case Stmt::CXXConstructExprClass: { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); + SVal ThisVal = State->getSVal(ThisPtr); + + return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite), + ThisVal.getAsRegion(), State, CallerCtx); + } + case Stmt::CXXNewExprClass: + return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx); + case Stmt::ObjCMessageExprClass: + return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite), + State, CallerCtx); + default: + llvm_unreachable("This is not an inlineable statement."); + } + } + + // Fall back to the CFG. The only thing we haven't handled yet is + // destructors, though this could change in the future. + const CFGBlock *B = CalleeCtx->getCallSiteBlock(); + CFGElement E = (*B)[CalleeCtx->getIndex()]; + assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs"); + assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet"); + + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); + Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx); + SVal ThisVal = State->getSVal(ThisPtr); + + const Stmt *Trigger; + if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E)) + Trigger = AutoDtor->getTriggerStmt(); + else + Trigger = Dtor->getBody(); + + return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), + State, CallerCtx); +} + diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 0bcc343..c786655 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -14,7 +14,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/AST/DeclBase.h" @@ -25,6 +25,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const { return !StmtCheckers.empty() || !PreObjCMessageCheckers.empty() || !PostObjCMessageCheckers.empty() || + !PreCallCheckers.empty() || + !PostCallCheckers.empty() || !LocationCheckers.empty() || !BindCheckers.empty() || !EndAnalysisCheckers.empty() || @@ -138,7 +140,7 @@ namespace { const CheckersTy &Checkers; const Stmt *S; ExprEngine &Eng; - bool wasInlined; + bool WasInlined; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } @@ -146,7 +148,7 @@ namespace { CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, const Stmt *s, ExprEngine &eng, bool wasInlined = false) : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), - wasInlined(wasInlined) {} + WasInlined(wasInlined) {} void runChecker(CheckerManager::CheckStmtFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -155,7 +157,7 @@ namespace { ProgramPoint::PostStmtKind; const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Bldr, Eng, Pred, L, wasInlined); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); checkFn(S, C); } }; @@ -167,38 +169,35 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit, const ExplodedNodeSet &Src, const Stmt *S, ExprEngine &Eng, - bool wasInlined) { + bool WasInlined) { CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit), - S, Eng, wasInlined); + S, Eng, WasInlined); expandGraphWithCheckers(C, Dst, Src); } namespace { struct CheckObjCMessageContext { typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy; - bool IsPreVisit; + bool IsPreVisit, WasInlined; const CheckersTy &Checkers; - const ObjCMessage &Msg; + const ObjCMethodCall &Msg; ExprEngine &Eng; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers, - const ObjCMessage &msg, ExprEngine &eng) - : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { } + const ObjCMethodCall &msg, ExprEngine &eng, + bool wasInlined) + : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + Msg(msg), Eng(eng) { } void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : - ProgramPoint::PostStmtKind; - const ProgramPoint &L = - ProgramPoint::getProgramPoint(Msg.getMessageExpr(), - K, Pred->getLocationContext(), - checkFn.Checker); - CheckerContext C(Bldr, Eng, Pred, L); + const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); - checkFn(Msg, C); + checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C); } }; } @@ -207,12 +206,56 @@ namespace { void CheckerManager::runCheckersForObjCMessage(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const ObjCMessage &msg, - ExprEngine &Eng) { + const ObjCMethodCall &msg, + ExprEngine &Eng, + bool WasInlined) { CheckObjCMessageContext C(isPreVisit, isPreVisit ? PreObjCMessageCheckers : PostObjCMessageCheckers, - msg, Eng); + msg, Eng, WasInlined); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + // FIXME: This has all the same signatures as CheckObjCMessageContext. + // Is there a way we can merge the two? + struct CheckCallContext { + typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy; + bool IsPreVisit, WasInlined; + const CheckersTy &Checkers; + const CallEvent &Call; + ExprEngine &Eng; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckCallContext(bool isPreVisit, const CheckersTy &checkers, + const CallEvent &call, ExprEngine &eng, + bool wasInlined) + : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + Call(call), Eng(eng) { } + + void runChecker(CheckerManager::CheckCallFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + + checkFn(*Call.cloneWithState(Pred->getState()), C); + } + }; +} + +/// \brief Run checkers for visiting an abstract call event. +void CheckerManager::runCheckersForCallEvent(bool isPreVisit, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const CallEvent &Call, + ExprEngine &Eng, + bool WasInlined) { + CheckCallContext C(isPreVisit, + isPreVisit ? PreCallCheckers + : PostCallCheckers, + Call, Eng, WasInlined); expandGraphWithCheckers(C, Dst, Src); } @@ -381,21 +424,25 @@ namespace { SymbolReaper &SR; const Stmt *S; ExprEngine &Eng; + ProgramPoint::Kind ProgarmPointKind; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, - const Stmt *s, ExprEngine &eng) - : Checkers(checkers), SR(sr), S(s), Eng(eng) { } + const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind K) + : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { } void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind, Pred->getLocationContext(), checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); + // Note, do not pass the statement to the checkers without letting them + // differentiate if we ran remove dead bindings before or after the + // statement. checkFn(SR, C); } }; @@ -406,8 +453,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SymbolReaper &SymReaper, const Stmt *S, - ExprEngine &Eng) { - CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng); + ExprEngine &Eng, + ProgramPoint::Kind K) { + CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K); expandGraphWithCheckers(C, Dst, Src); } @@ -426,7 +474,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) { + const CallEvent *Call) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. @@ -456,16 +504,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, /// Only one checker will evaluate the call. void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const CallExpr *CE, - ExprEngine &Eng, - GraphExpander *defaultEval) { - if (EvalCallCheckers.empty() && - InlineCallCheckers.empty() && - defaultEval == 0) { - Dst.insert(Src); - return; - } - + const CallEvent &Call, + ExprEngine &Eng) { + const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); for (ExplodedNodeSet::iterator NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) { @@ -529,10 +570,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { - if (defaultEval) - defaultEval->expandGraph(Dst, Pred); - else - Dst.insert(Pred); + NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); + Eng.defaultEvalCall(B, Pred, Call); } } } @@ -590,6 +629,13 @@ void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { PostObjCMessageCheckers.push_back(checkfn); } +void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) { + PreCallCheckers.push_back(checkfn); +} +void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) { + PostCallCheckers.push_back(checkfn); +} + void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { LocationCheckers.push_back(checkfn); } @@ -673,6 +719,3 @@ CheckerManager::~CheckerManager() { for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i) CheckerDtors[i](); } - -// Anchor for the vtable. -GraphExpander::~GraphExpander() { } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index ca662c7..1f13742 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -26,6 +26,8 @@ using namespace clang; using namespace ento; +STATISTIC(NumSteps, + "The # of steps executed."); STATISTIC(NumReachedMaxSteps, "The # of times we reached the max number of steps."); STATISTIC(NumPathsExplored, @@ -74,7 +76,7 @@ public: } virtual void enqueue(const WorkListUnit& U) { - Queue.push_front(U); + Queue.push_back(U); } virtual WorkListUnit dequeue() { @@ -207,6 +209,8 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, --Steps; } + NumSteps++; + const WorkListUnit& WU = WList->dequeue(); // Set the current block counter. @@ -248,7 +252,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; } - case ProgramPoint::CallExitKind: + case ProgramPoint::CallExitBeginKind: SubEng.processCallExit(Pred); break; @@ -261,7 +265,9 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, } default: assert(isa<PostStmt>(Loc) || - isa<PostInitializer>(Loc)); + isa<PostInitializer>(Loc) || + isa<PostImplicitCall>(Loc) || + isa<CallExitEnd>(Loc)); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; } @@ -502,7 +508,8 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, } // Do not create extra nodes. Move to the next CFG element. - if (isa<PostInitializer>(N->getLocation())) { + if (isa<PostInitializer>(N->getLocation()) || + isa<PostImplicitCall>(N->getLocation())) { WList->enqueue(N, Block, Idx+1); return; } @@ -531,14 +538,13 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, WList->enqueue(Succ, Block, Idx+1); } -ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) { - // Create a CallExit node and enqueue it. +ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) { + // Create a CallExitBegin node and enqueue it. const StackFrameContext *LocCtx = cast<StackFrameContext>(N->getLocationContext()); - const Stmt *CE = LocCtx->getCallSite(); - // Use the the callee location context. - CallExit Loc(CE, LocCtx); + // Use the callee location context. + CallExitBegin Loc(LocCtx); bool isNew; ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); @@ -565,12 +571,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set, void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { ExplodedNode *N = *I; - // If we are in an inlined call, generate CallExit node. + // If we are in an inlined call, generate CallExitBegin node. if (N->getLocationContext()->getParent()) { - N = generateCallExitNode(N); + N = generateCallExitBeginNode(N); if (N) WList->enqueue(N); } else { + // TODO: We should run remove dead bindings here. G->addEndOfPath(N); NumPathsExplored++; } diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index b5ea3db..52644f7 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -71,6 +71,11 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, else return svalBuilder.makeBoolVal(cast<CXXBoolLiteralExpr>(E)); } + case Stmt::CXXScalarValueInitExprClass: + case Stmt::ImplicitValueInitExprClass: { + QualType Ty = cast<Expr>(E)->getType(); + return svalBuilder.makeZeroVal(Ty); + } case Stmt::IntegerLiteralClass: { // In C++, this expression may have been bound to a temporary object. SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx)); @@ -91,8 +96,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::CXXBindTemporaryExprClass: E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); continue; - case Stmt::ObjCPropertyRefExprClass: - return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E)); + case Stmt::SubstNonTypeTemplateParmExprClass: + E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); + continue; case Stmt::ObjCStringLiteralClass: { MemRegionManager &MRMgr = svalBuilder.getRegionManager(); const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E); @@ -227,13 +233,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, RSScaner.scan(X); continue; } - - // Otherwise the expression is dead with a couple exceptions. - // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the - // beginning of itself, but we need its UndefinedVal to determine its - // SVal. - if (X.isUndef() && cast<UndefinedVal>(X).getData()) - EBMapRef = EBMapRef.add(BlkExpr, X); } // Go through he deferred locations and add them to the new environment if diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 0dcbe1f..b79f3f5 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -13,12 +13,14 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/Stmt.h" #include "clang/AST/ParentMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include <vector> using namespace clang; @@ -57,7 +59,7 @@ ExplodedGraph::~ExplodedGraph() {} //===----------------------------------------------------------------------===// bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { - // Reclaimn all nodes that match *all* the following criteria: + // Reclaim all nodes that match *all* the following criteria: // // (1) 1 predecessor (that has one successor) // (2) 1 successor (that has one predecessor) @@ -67,6 +69,9 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // (6) The 'GDM' is the same as the predecessor. // (7) The LocationContext is the same as the predecessor. // (8) The PostStmt is for a non-consumed Stmt or Expr. + // (9) The successor is not a CallExpr StmtPoint (so that we would be able to + // find it when retrying a call with no inlining). + // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. // Conditions 1 and 2. if (node->pred_size() != 1 || node->succ_size() != 1) @@ -82,8 +87,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Condition 3. ProgramPoint progPoint = node->getLocation(); - if (!isa<PostStmt>(progPoint) || - (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint))) + if (!isa<PostStmt>(progPoint)) return false; // Condition 4. @@ -108,7 +112,13 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { return false; } - return true; + // Condition 9. + const ProgramPoint SuccLoc = succ->getLocation(); + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc)) + if (CallEvent::mayBeInlined(SP->getStmt())) + return false; + + return true; } void ExplodedGraph::collectNode(ExplodedNode *node) { diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1fd9068..b0435fb 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -18,13 +18,12 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtCXX.h" -#include "clang/AST/DeclCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" @@ -42,8 +41,6 @@ using llvm::APSInt; STATISTIC(NumRemoveDeadBindings, "The # of times RemoveDeadBindings is called"); -STATISTIC(NumRemoveDeadBindingsSkipped, - "The # of times RemoveDeadBindings is skipped"); STATISTIC(NumMaxBlockCountReached, "The # of aborted paths due to reaching the maximum block count in " "a top level function"); @@ -54,15 +51,6 @@ STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); //===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(0, &II); -} - -//===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -163,7 +151,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { // analyzing an "open" program. const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); if (SFC->getParent() == 0) { - loc::MemRegionVal L(getCXXThisRegion(MD, SFC)); + loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); if (const Loc *LV = dyn_cast<Loc>(&V)) { state = state->assume(*LV, true); @@ -196,7 +184,7 @@ ExprEngine::processRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, - const CallOrObjCMessage *Call) { + const CallEvent *Call) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, Explicits, Regions, Call); } @@ -231,6 +219,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); return; } + currentBuilderContext = 0; } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, @@ -251,7 +240,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Run before processing a call. - if (isa<CallExpr>(S.getStmt())) + if (CallEvent::mayBeInlined(S.getStmt())) return true; // Is this an expression that is consumed by another expression? If so, @@ -260,62 +249,47 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { - // Reclaim any unnecessary nodes in the ExplodedGraph. - G.reclaimRecentlyAllocatedNodes(); - - currentStmt = S.getStmt(); - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - currentStmt->getLocStart(), - "Error evaluating statement"); - - EntryNode = Pred; - - ProgramStateRef EntryState = EntryNode->getState(); - CleanedState = EntryState; - - // Create the cleaned state. - const LocationContext *LC = EntryNode->getLocationContext(); - SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager()); - - if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) { - NumRemoveDeadBindings++; - getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); - - const StackFrameContext *SFC = LC->getCurrentStackFrame(); - - // Create a state in which dead bindings are removed from the environment - // and the store. TODO: The function should just return new env and store, - // not a new state. - CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); - } else { - NumRemoveDeadBindingsSkipped++; - } +void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, + const Stmt *ReferenceStmt, + const LocationContext *LC, + const Stmt *DiagnosticStmt, + ProgramPoint::Kind K) { + assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || + ReferenceStmt == 0) && "PreStmt is not generally supported by " + "the SymbolReaper yet"); + NumRemoveDeadBindings++; + CleanedState = Pred->getState(); + SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager()); + + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); + + // Create a state in which dead bindings are removed from the environment + // and the store. TODO: The function should just return new env and store, + // not a new state. + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); // Process any special transfer function for dead symbols. - ExplodedNodeSet Tmp; // A tag to track convenience transitions, which can be removed at cleanup. static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); - if (!SymReaper.hasDeadSymbols()) { // Generate a CleanedNode that has the environment and store cleaned // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. - StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); - Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag); + StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext); + Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K); } else { // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode, - SymReaper, currentStmt, *this); + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, + DiagnosticStmt, *this, K); // For each node in CheckedSet, generate CleanedNodes that have the // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. - StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext); + StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext); for (ExplodedNodeSet::const_iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { ProgramStateRef CheckerState = (*I)->getState(); @@ -324,10 +298,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, CheckerState = getConstraintManager().removeDeadBindings(CheckerState, SymReaper); - assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) && + assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Environment as a part of " "checkDeadSymbols processing."); - assert(StateMgr.haveEqualStores(CheckerState, EntryState) && + assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && "Checkers are not allowed to modify the Store as a part of " "checkDeadSymbols processing."); @@ -335,13 +309,35 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // generate a transition to that state. ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag, - ProgramPoint::PostPurgeDeadSymbolsKind); + Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false, + &cleanupTag, K); } } +} + +void ExprEngine::ProcessStmt(const CFGStmt S, + ExplodedNode *Pred) { + // Reclaim any unnecessary nodes in the ExplodedGraph. + G.reclaimRecentlyAllocatedNodes(); + + currentStmt = S.getStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + currentStmt->getLocStart(), + "Error evaluating statement"); + // Remove dead bindings and symbols. + EntryNode = Pred; + ExplodedNodeSet CleanedStates; + if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){ + removeDead(EntryNode, CleanedStates, currentStmt, + Pred->getLocationContext(), currentStmt); + } else + CleanedStates.Add(EntryNode); + + // Visit the statement. ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + for (ExplodedNodeSet::iterator I = CleanedStates.begin(), + E = CleanedStates.end(); I != E; ++I) { ExplodedNodeSet DstI; // Visit the statement. Visit(currentStmt, *I, DstI); @@ -360,49 +356,49 @@ void ExprEngine::ProcessStmt(const CFGStmt S, void ExprEngine::ProcessInitializer(const CFGInitializer Init, ExplodedNode *Pred) { ExplodedNodeSet Dst; + NodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + + ProgramStateRef State = Pred->getState(); - // We don't set EntryNode and currentStmt. And we don't clean up state. const CXXCtorInitializer *BMI = Init.getInitializer(); + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + BMI->getSourceLocation(), + "Error evaluating initializer"); + + // We don't set EntryNode and currentStmt. And we don't clean up state. const StackFrameContext *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); - const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame); - - SVal thisVal = Pred->getState()->getSVal(thisReg); + SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); + // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { - // Evaluate the initializer. - - StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); - ProgramStateRef state = Pred->getState(); - - const FieldDecl *FD = BMI->getAnyMember(); - - SVal FieldLoc = state->getLValue(FD, thisVal); - SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext()); - state = state->bindLoc(FieldLoc, InitVal); + // Constructors build the object directly in the field, + // but non-objects must be copied in from the initializer. + if (!isa<CXXConstructExpr>(BMI->getInit())) { + SVal FieldLoc; + if (BMI->isIndirectMemberInitializer()) + FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); + else + FieldLoc = State->getLValue(BMI->getMember(), thisVal); - // Use a custom node building process. - PostInitializer PP(BMI, stackFrame); - // Builder automatically add the generated node to the deferred set, - // which are processed in the builder's dtor. - Bldr.generateNode(PP, Pred, state); + SVal InitVal = State->getSVal(BMI->getInit(), stackFrame); + State = State->bindLoc(FieldLoc, InitVal); + } } else { - assert(BMI->isBaseInitializer()); - - // Get the base class declaration. - const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); - - // Create the base object region. - SVal baseVal = - getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); - const MemRegion *baseReg = baseVal.getAsRegion(); - assert(baseReg); - - VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst); + assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); + // We already did all the work when visiting the CXXConstructExpr. } + // Construct a PostInitializer node whether the state changed or not, + // so that the diagnostics don't get confused. + PostInitializer PP(BMI, stackFrame); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + Bldr.generateNode(PP, State, Pred); + // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } @@ -442,27 +438,51 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, if (const ReferenceType *refType = varType->getAs<ReferenceType>()) varType = refType->getPointeeType(); - const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl(); - assert(recordDecl && "get CXXRecordDecl fail"); - const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor(); - Loc dest = state->getLValue(varDecl, Pred->getLocationContext()); - VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(), + VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(), Dtor.getTriggerStmt(), Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, - ExplodedNode *Pred, ExplodedNodeSet &Dst) {} + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = Pred->getState()->getSVal(ThisPtr); + + // Create the base object region. + QualType BaseTy = D.getBaseSpecifier()->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + + VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(), + CurDtor->getBody(), Pred, Dst); +} void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, - ExplodedNode *Pred, ExplodedNodeSet &Dst) {} + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const FieldDecl *Member = D.getFieldDecl(); + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, + LCtx->getCurrentStackFrame()); + SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal))); + + VisitCXXDestructor(Member->getType(), + cast<loc::MemRegionVal>(FieldVal).getRegion(), + CurDtor->getBody(), Pred, Dst); +} void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) {} -void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, +void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), @@ -490,7 +510,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: case Stmt::CXXUnresolvedConstructExprClass: - case Stmt::CXXScalarValueInitExprClass: case Stmt::DependentScopeDeclRefExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::BinaryTypeTraitExprClass: @@ -514,7 +533,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // We don't handle default arguments either yet, but we can fake it // for now by just skipping them. - case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXDefaultArgExprClass: break; @@ -544,6 +562,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Expr::MSDependentExistsStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); + case Stmt::ObjCSubscriptRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + llvm_unreachable("These are handled by PseudoObjectExpr"); + case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. ProgramStateRef state = Pred->getState(); @@ -559,23 +581,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - // FIXME. - case Stmt::ObjCSubscriptRefExprClass: - break; - - case Stmt::ObjCPropertyRefExprClass: - // Implicitly handled by Environment::getSVal(). - break; - - case Stmt::ImplicitValueInitExprClass: { - ProgramStateRef state = Pred->getState(); - QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); - SVal val = svalBuilder.makeZeroVal(ty); - Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), - val)); - break; - } - case Stmt::ExprWithCleanupsClass: // Handled due to fully linearised CFG. break; @@ -592,7 +597,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: - case Expr::ObjCNumericLiteralClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: @@ -613,6 +617,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AddrLabelExprClass: case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXBoolLiteralExprClass: case Stmt::ObjCBoolLiteralExprClass: case Stmt::FloatingLiteralClass: @@ -620,6 +626,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXBindTemporaryExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; @@ -630,22 +637,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Expr::ObjCArrayLiteralClass: - case Expr::ObjCDictionaryLiteralClass: { + case Expr::ObjCDictionaryLiteralClass: + // FIXME: explicitly model with a region and the actual contents + // of the container. For now, conjure a symbol. + case Expr::ObjCBoxedExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); - // FIXME: explicitly model with a region and the actual contents - // of the container. For now, conjure a symbol. ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext); + const Expr *Ex = cast<Expr>(S); + QualType resultType = Ex->getType(); + for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); it != et; ++it) { ExplodedNode *N = *it; - const Expr *Ex = cast<Expr>(S); - QualType resultType = Ex->getType(); const LocationContext *LCtx = N->getLocationContext(); SVal result = svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType, @@ -671,6 +680,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::MSAsmStmtClass: + Bldr.takeNodes(Pred); + VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + case Stmt::BlockExprClass: Bldr.takeNodes(Pred); VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); @@ -727,12 +742,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXConstructExprClass: { - const CXXConstructExpr *C = cast<CXXConstructExpr>(S); - // For block-level CXXConstructExpr, we don't have a destination region. - // Let VisitCXXConstructExpr() create one. + case Stmt::CXXConstructExprClass: { Bldr.takeNodes(Pred); - VisitCXXConstructExpr(C, 0, Pred, Dst); + VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; } @@ -868,33 +880,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - case Stmt::ObjCMessageExprClass: { + case Stmt::ObjCMessageExprClass: Bldr.takeNodes(Pred); - // Is this a property access? - const ParentMap &PM = Pred->getLocationContext()->getParentMap(); - const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S); - bool evaluated = false; - - if (const PseudoObjectExpr *PO = - dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) { - const Expr *syntactic = PO->getSyntacticForm(); - if (const ObjCPropertyRefExpr *PR = - dyn_cast<ObjCPropertyRefExpr>(syntactic)) { - bool isSetter = ME->getNumArgs() > 0; - VisitObjCMessage(ObjCMessage(ME, PR, isSetter), Pred, Dst); - evaluated = true; - } - else if (isa<BinaryOperator>(syntactic)) { - VisitObjCMessage(ObjCMessage(ME, 0, true), Pred, Dst); - } - } - - if (!evaluated) - VisitObjCMessage(ME, Pred, Dst); - + VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::ObjCAtThrowStmtClass: { // FIXME: This is not complete. We basically treat @throw as @@ -982,6 +972,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); assert(CalleeSF && CallerSF); ExplodedNode *BeforeProcessingCall = 0; + const Stmt *CE = CalleeSF->getCallSite(); // Find the first node before we started processing the call expression. while (N) { @@ -993,11 +984,15 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) continue; // We reached the caller. Find the node right before we started - // processing the CallExpr. - if (isa<PostPurgeDeadSymbols>(L)) + // processing the call. + if (L.isPurgeKind()) + continue; + if (isa<PreImplicitCall>(&L)) + continue; + if (isa<CallEnter>(&L)) continue; if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) - if (SP->getStmt() == CalleeSF->getCallSite()) + if (SP->getStmt() == CE) continue; break; } @@ -1008,7 +1003,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // TODO: Clean up the unneeded nodes. // Build an Epsilon node from which we will restart the analyzes. - const Stmt *CE = CalleeSF->getCallSite(); + // Note that CE is permitted to be NULL! ProgramPoint NewNodeLoc = EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); // Add the special flag to GDM to signal retrying with no inlining. @@ -1073,63 +1068,6 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // Branch processing. //===----------------------------------------------------------------------===// -ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state, - const Stmt *Terminator, - const LocationContext *LCtx, - bool branchTaken) { - - switch (Terminator->getStmtClass()) { - default: - return state; - - case Stmt::BinaryOperatorClass: { // '&&' and '||' - - const BinaryOperator* B = cast<BinaryOperator>(Terminator); - BinaryOperator::Opcode Op = B->getOpcode(); - - assert (Op == BO_LAnd || Op == BO_LOr); - - // For &&, if we take the true branch, then the value of the whole - // expression is that of the RHS expression. - // - // For ||, if we take the false branch, then the value of the whole - // expression is that of the RHS expression. - - const Expr *Ex = (Op == BO_LAnd && branchTaken) || - (Op == BO_LOr && !branchTaken) - ? B->getRHS() : B->getLHS(); - - return state->BindExpr(B, LCtx, UndefinedVal(Ex)); - } - - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: { // ?: - const AbstractConditionalOperator* C - = cast<AbstractConditionalOperator>(Terminator); - - // For ?, if branchTaken == true then the value is either the LHS or - // the condition itself. (GNU extension). - - const Expr *Ex; - - if (branchTaken) - Ex = C->getTrueExpr(); - else - Ex = C->getFalseExpr(); - - return state->BindExpr(C, LCtx, UndefinedVal(Ex)); - } - - case Stmt::ChooseExprClass: { // ?: - - const ChooseExpr *C = cast<ChooseExpr>(Terminator); - - const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS(); - return state->BindExpr(C, LCtx, UndefinedVal(Ex)); - } - } -} - /// RecoverCastedSymbol - A helper function for ProcessBranch that is used /// to try to recover some path-sensitivity for casts of symbolic /// integers that promote their values (which are currently not tracked well). @@ -1172,6 +1110,45 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, return state->getSVal(Ex, LCtx); } +static const Stmt *ResolveCondition(const Stmt *Condition, + const CFGBlock *B) { + if (const Expr *Ex = dyn_cast<Expr>(Condition)) + Condition = Ex->IgnoreParens(); + + const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition); + if (!BO || !BO->isLogicalOp()) + return Condition; + + // For logical operations, we still have the case where some branches + // use the traditional "merge" approach and others sink the branch + // directly into the basic blocks representing the logical operation. + // We need to distinguish between those two cases here. + + // The invariants are still shifting, but it is possible that the + // last element in a CFGBlock is not a CFGStmt. Look for the last + // CFGStmt as the value of the condition. + CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); + for (; I != E; ++I) { + CFGElement Elem = *I; + CFGStmt *CS = dyn_cast<CFGStmt>(&Elem); + if (!CS) + continue; + if (CS->getStmt() != Condition) + break; + return Condition; + } + + assert(I != E); + + while (Condition) { + BO = dyn_cast<BinaryOperator>(Condition); + if (!BO || !BO->isLogicalOp()) + return Condition; + Condition = BO->getRHS()->IgnoreParens(); + } + llvm_unreachable("could not resolve condition"); +} + void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, NodeBuilderContext& BldCtx, ExplodedNode *Pred, @@ -1188,6 +1165,12 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; } + + // Resolve the condition in the precense of nested '||' and '&&'. + if (const Expr *Ex = dyn_cast<Expr>(Condition)) + Condition = Ex->IgnoreParens(); + + Condition = ResolveCondition(Condition, BldCtx.getBlock()); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Condition->getLocStart(), "Error evaluating branch"); @@ -1230,14 +1213,10 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, } } - const LocationContext *LCtx = PredI->getLocationContext(); - // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) { - builder.generateNode(MarkBranch(PrevState, Term, LCtx, true), - true, PredI); - builder.generateNode(MarkBranch(PrevState, Term, LCtx, false), - false, PredI); + builder.generateNode(PrevState, true, PredI); + builder.generateNode(PrevState, false, PredI); continue; } @@ -1246,8 +1225,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, // Process the true branch. if (builder.isFeasible(true)) { if (ProgramStateRef state = PrevState->assume(V, true)) - builder.generateNode(MarkBranch(state, Term, LCtx, true), - true, PredI); + builder.generateNode(state, true, PredI); else builder.markInfeasible(true); } @@ -1255,8 +1233,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, // Process the false branch. if (builder.isFeasible(false)) { if (ProgramStateRef state = PrevState->assume(V, false)) - builder.generateNode(MarkBranch(state, Term, LCtx, false), - false, PredI); + builder.generateNode(state, false, PredI); else builder.markInfeasible(false); } @@ -1433,7 +1410,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, const LocationContext *LCtx = Pred->getLocationContext(); if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { - assert(Ex->isLValue()); + assert(Ex->isGLValue()); SVal V = state->getLValue(VD, Pred->getLocationContext()); // For references, the 'lvalue' is the pointer address stored in the @@ -1450,7 +1427,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, return; } if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { - assert(!Ex->isLValue()); + assert(!Ex->isGLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; @@ -1493,7 +1470,7 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, SVal V = state->getLValue(A->getType(), state->getSVal(Idx, LCtx), state->getSVal(Base, LCtx)); - assert(A->isLValue()); + assert(A->isGLValue()); Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), false, 0, ProgramPoint::PostLValueKind); } @@ -1506,14 +1483,26 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext); ExplodedNodeSet Dst; Decl *member = M->getMemberDecl(); + if (VarDecl *VD = dyn_cast<VarDecl>(member)) { - assert(M->isLValue()); + assert(M->isGLValue()); Bldr.takeNodes(Pred); VisitCommonDeclRefExpr(M, VD, Pred, Dst); Bldr.addNodes(Dst); return; } - + + // Handle C++ method calls. + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(member)) { + Bldr.takeNodes(Pred); + SVal MDVal = svalBuilder.getFunctionPointer(MD); + ProgramStateRef state = + Pred->getState()->BindExpr(M, Pred->getLocationContext(), MDVal); + Bldr.generateNode(M, Pred, state); + return; + } + + FieldDecl *field = dyn_cast<FieldDecl>(member); if (!field) // FIXME: skipping member expressions for non-fields return; @@ -1538,10 +1527,17 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // For all other cases, compute an lvalue. SVal L = state->getLValue(field, baseExprVal); - if (M->isLValue()) + if (M->isGLValue()) { + if (field->getType()->isReferenceType()) { + if (const MemRegion *R = L.getAsRegion()) + L = state->getSVal(R); + else + L = UnknownVal(); + } + Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0, ProgramPoint::PostLValueKind); - else { + } else { Bldr.takeNodes(Pred); evalLoad(Dst, M, M, Pred, state, L); Bldr.addNodes(Dst); @@ -1591,9 +1587,9 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, /// evalStore - Handle the semantics of a store via an assignment. /// @param Dst The node set to store generated state nodes -/// @param AssignE The assignment expression if the store happens in an +/// @param AssignE The assignment expression if the store happens in an /// assignment. -/// @param LocatioinE The location expression that is stored to. +/// @param LocationE The location expression that is stored to. /// @param state The current simulation state /// @param location The location to store the value /// @param Val The value to be stored @@ -1606,10 +1602,6 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, // ProgramPoint if it is non-NULL, and LocationE otherwise. const Expr *StoreE = AssignE ? AssignE : LocationE; - if (isa<loc::ObjCPropRef>(location)) { - assert(false); - } - // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false); @@ -1634,7 +1626,6 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, QualType LoadTy) { assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); - assert(!isa<loc::ObjCPropRef>(location)); // Are we loading from a region? This actually results in two loads; one // to fetch the address of the referenced value and one to fetch the @@ -1814,6 +1805,12 @@ void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, Bldr.generateNode(A, Pred, state); } +void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + Bldr.generateNode(A, Pred, Pred->getState()); +} + //===----------------------------------------------------------------------===// // Visualization. //===----------------------------------------------------------------------===// @@ -1852,6 +1849,16 @@ struct DOTGraphTraits<ExplodedNode*> : return ""; } + static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) { + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getExpansionLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) + << "\\l"; + } + } + static std::string getNodeLabel(const ExplodedNode *N, void*){ std::string sbuf; @@ -1861,10 +1868,17 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramPoint Loc = N->getLocation(); switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: + case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); + if (const NamedDecl *ND = + dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { + Out << " ("; + ND->printName(Out); + Out << ")"; + } break; + } case ProgramPoint::BlockExitKind: assert (false); @@ -1874,30 +1888,54 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "CallEnter"; break; - case ProgramPoint::CallExitKind: - Out << "CallExit"; + case ProgramPoint::CallExitBeginKind: + Out << "CallExitBegin"; + break; + + case ProgramPoint::CallExitEndKind: + Out << "CallExitEnd"; + break; + + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + Out << "PostStmtPurgeDeadSymbols"; + break; + + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + Out << "PreStmtPurgeDeadSymbols"; break; case ProgramPoint::EpsilonKind: Out << "Epsilon Point"; break; + case ProgramPoint::PreImplicitCallKind: { + ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + Out << "PreCall: "; + + // FIXME: Get proper printing options. + PC->getDecl()->print(Out, LangOptions()); + printLocation(Out, PC->getLocation()); + break; + } + + case ProgramPoint::PostImplicitCallKind: { + ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + Out << "PostCall: "; + + // FIXME: Get proper printing options. + PC->getDecl()->print(Out, LangOptions()); + printLocation(Out, PC->getLocation()); + break; + } + default: { if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { const Stmt *S = L->getStmt(); - SourceLocation SLoc = S->getLocStart(); Out << S->getStmtClassName() << ' ' << (void*) S << ' '; LangOptions LO; // FIXME. S->printPretty(Out, 0, PrintingPolicy(LO)); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getExpansionLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) - << "\\l"; - } + printLocation(Out, S->getLocStart()); if (isa<PreStmt>(Loc)) Out << "\\lPreStmt\\l;"; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 93e598a..46cba81 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -50,7 +50,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, } // Simulate the effects of a "store": bind the value of the RHS // to the L-Value represented by the LHS. - SVal ExprVal = B->isLValue() ? LeftV : RightV; + SVal ExprVal = B->isGLValue() ? LeftV : RightV; evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal), LeftV, RightV); continue; @@ -58,6 +58,26 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, if (!B->isAssignmentOp()) { StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext); + + if (B->isAdditiveOp()) { + // If one of the operands is a location, conjure a symbol for the other + // one (offset) if it's unknown so that memory arithmetic always + // results in an ElementRegion. + // TODO: This can be removed after we enable history tracking with + // SymSymExpr. + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + if (isa<Loc>(LeftV) && + RHS->getType()->isIntegerType() && RightV.isUnknown()) { + RightV = svalBuilder.getConjuredSymbolVal(RHS, LCtx, + RHS->getType(), Count); + } + if (isa<Loc>(RightV) && + LHS->getType()->isIntegerType() && LeftV.isUnknown()) { + LeftV = svalBuilder.getConjuredSymbolVal(LHS, LCtx, + LHS->getType(), Count); + } + } + // Process non-assignments except commas or short-circuited // logical expressions (LAnd and LOr). SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); @@ -145,7 +165,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // In C++, assignment and compound assignment operators return an // lvalue. - if (B->isLValue()) + if (B->isGLValue()) state = state->BindExpr(B, LCtx, location); else state = state->BindExpr(B, LCtx, Result); @@ -162,14 +182,35 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { CanQualType T = getContext().getCanonicalType(BE->getType()); + + // Get the value of the block itself. SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, Pred->getLocationContext()); + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the block, we should explicitly bind + // the captured variables. + if (const BlockDataRegion *BDR = + dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { + + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + + for (; I != E; ++I) { + const MemRegion *capturedR = I.getCapturedRegion(); + const MemRegion *originalR = I.getOriginalRegion(); + if (capturedR != originalR) { + SVal originalV = State->getSVal(loc::MemRegionVal(originalR)); + State = State->bindLoc(loc::MemRegionVal(capturedR), originalV); + } + } + } + ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); Bldr.generateNode(BE, Pred, - Pred->getState()->BindExpr(BE, Pred->getLocationContext(), - V), + State->BindExpr(BE, Pred->getLocationContext(), V), false, 0, ProgramPoint::PostLValueKind); @@ -238,7 +279,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: - case CK_LValueBitCast: case CK_IntegralCast: case CK_NullToPointer: case CK_IntegralToPointer: @@ -278,7 +318,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); SVal val = state->getSVal(Ex, LCtx); - val = getStoreManager().evalDerivedToBase(val, T); + val = getStoreManager().evalDerivedToBase(val, CastE); state = state->BindExpr(CastE, LCtx, val); Bldr.generateNode(CastE, Pred, state); continue; @@ -291,7 +331,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, // Compute the type of the result. QualType resultType = CastE->getType(); - if (CastE->isLValue()) + if (CastE->isGLValue()) resultType = getContext().getPointerType(resultType); bool Failed = false; @@ -337,10 +377,11 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_UserDefinedConversion: case CK_ConstructorConversion: case CK_VectorSplat: - case CK_MemberPointerToBoolean: { + case CK_MemberPointerToBoolean: + case CK_LValueBitCast: { // Recover some path-sensitivty by conjuring a new value. QualType resultType = CastE->getType(); - if (CastE->isLValue()) + if (CastE->isGLValue()) resultType = getContext().getPointerType(resultType); const LocationContext *LCtx = Pred->getLocationContext(); SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx, @@ -366,8 +407,16 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, SVal ILV = state->getSVal(ILE, Pred->getLocationContext()); const LocationContext *LC = Pred->getLocationContext(); state = state->bindCompoundLiteral(CL, LC, ILV); - - if (CL->isLValue()) + + // Compound literal expressions are a GNU extension in C++. + // Unlike in C, where CLs are lvalues, in C++ CLs are prvalues, + // and like temporary objects created by the functional notation T() + // CLs are destroyed at the end of the containing full-expression. + // HOWEVER, an rvalue of array type is not something the analyzer can + // reason about, since we expect all regions to be wrapped in Locs. + // So we treat array CLs as lvalues as well, knowing that they will decay + // to pointers as soon as they are used. + if (CL->isGLValue() || CL->getType()->isArrayType()) B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC))); else B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV)); @@ -404,31 +453,39 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, const LocationContext *LC = N->getLocationContext(); if (const Expr *InitEx = VD->getInit()) { - SVal InitVal = state->getSVal(InitEx, Pred->getLocationContext()); - - // We bound the temp obj region to the CXXConstructExpr. Now recover - // the lazy compound value when the variable is not a reference. - if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && - !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ - InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); - assert(isa<nonloc::LazyCompoundVal>(InitVal)); - } - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if (InitVal.isUnknown()) { - QualType Ty = InitEx->getType(); - if (InitEx->isLValue()) { - Ty = getContext().getPointerType(Ty); - } - - InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty, - currentBuilderContext->getCurrentBlockCount()); + SVal InitVal = state->getSVal(InitEx, LC); + + if (InitVal == state->getLValue(VD, LC) || + (VD->getType()->isArrayType() && + isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) { + // We constructed the object directly in the variable. + // No need to bind anything. + B.generateNode(DS, N, state); + } else { + // We bound the temp obj region to the CXXConstructExpr. Now recover + // the lazy compound value when the variable is not a reference. + if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && + !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ + InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); + assert(isa<nonloc::LazyCompoundVal>(InitVal)); + } + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown()) { + QualType Ty = InitEx->getType(); + if (InitEx->isGLValue()) { + Ty = getContext().getPointerType(Ty); + } + + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty, + currentBuilderContext->getCurrentBlockCount()); + } + B.takeNodes(N); + ExplodedNodeSet Dst2; + evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); + B.addNodes(Dst2); } - B.takeNodes(N); - ExplodedNodeSet Dst2; - evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); - B.addNodes(Dst2); } else { B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC))); @@ -443,48 +500,44 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); ProgramStateRef state = Pred->getState(); - const LocationContext *LCtx = Pred->getLocationContext(); - SVal X = state->getSVal(B, LCtx); - assert(X.isUndef()); - - const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); - assert(Ex); - - if (Ex == B->getRHS()) { - X = state->getSVal(Ex, LCtx); - - // Handle undefined values. - if (X.isUndef()) { - Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); - return; - } - - DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X); - - // We took the RHS. Because the value of the '&&' or '||' expression must - // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 - // or 1. Alternatively, we could take a lazy approach, and calculate this - // value later when necessary. We don't have the machinery in place for - // this right now, and since most logical expressions are used for branches, - // the payoff is not likely to be large. Instead, we do eager evaluation. - if (ProgramStateRef newState = state->assume(XD, true)) - Bldr.generateNode(B, Pred, - newState->BindExpr(B, LCtx, - svalBuilder.makeIntVal(1U, B->getType()))); - - if (ProgramStateRef newState = state->assume(XD, false)) - Bldr.generateNode(B, Pred, - newState->BindExpr(B, LCtx, - svalBuilder.makeIntVal(0U, B->getType()))); + + ExplodedNode *N = Pred; + while (!isa<BlockEntrance>(N->getLocation())) { + ProgramPoint P = N->getLocation(); + assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P)); + (void) P; + assert(N->pred_size() == 1); + N = *N->pred_begin(); + } + assert(N->pred_size() == 1); + N = *N->pred_begin(); + BlockEdge BE = cast<BlockEdge>(N->getLocation()); + SVal X; + + // Determine the value of the expression by introspecting how we + // got this location in the CFG. This requires looking at the previous + // block we were in and what kind of control-flow transfer was involved. + const CFGBlock *SrcBlock = BE.getSrc(); + // The only terminator (if there is one) that makes sense is a logical op. + CFGTerminator T = SrcBlock->getTerminator(); + if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) { + (void) Term; + assert(Term->isLogicalOp()); + assert(SrcBlock->succ_size() == 2); + // Did we take the true or false branch? + unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0; + X = svalBuilder.makeIntVal(constant, B->getType()); } else { - // We took the LHS expression. Depending on whether we are '&&' or - // '||' we know what the value of the expression is via properties of - // the short-circuiting. - X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, - B->getType()); - Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); + // If there is no terminator, by construction the last statement + // in SrcBlock is the value of the enclosing expression. + assert(!SrcBlock->empty()); + CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin()); + const Stmt *S = Elem.getStmt(); + X = N->getState()->getSVal(S, Pred->getLocationContext()); } + + Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); } void ExprEngine::VisitInitListExpr(const InitListExpr *IE, @@ -519,16 +572,17 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, svalBuilder.makeCompoundVal(T, vals))); return; } - - if (Loc::isLocType(T) || T->isIntegerType()) { - assert(IE->getNumInits() == 1); - const Expr *initEx = IE->getInit(0); - B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, - state->getSVal(initEx, LCtx))); - return; - } - - llvm_unreachable("unprocessed InitListExpr type"); + + // Handle scalars: int{5} and int{}. + assert(NumInitElements <= 1); + + SVal V; + if (NumInitElements == 0) + V = getSValBuilder().makeZeroVal(T); + else + V = state->getSVal(IE->getInit(0), LCtx); + + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); } void ExprEngine::VisitGuardedExpr(const Expr *Ex, @@ -537,17 +591,41 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); - ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - SVal X = state->getSVal(Ex, LCtx); - assert (X.isUndef()); - const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); - assert(SE); - X = state->getSVal(SE, LCtx); - - // Make sure that we invalidate the previous binding. - B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true)); + const CFGBlock *SrcBlock = 0; + + for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { + ProgramPoint PP = N->getLocation(); + if (isa<PreStmtPurgeDeadSymbols>(PP) || isa<BlockEntrance>(PP)) { + assert(N->pred_size() == 1); + continue; + } + SrcBlock = cast<BlockEdge>(&PP)->getSrc(); + break; + } + + // Find the last expression in the predecessor block. That is the + // expression that is used for the value of the ternary expression. + bool hasValue = false; + SVal V; + + for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(), + E = SrcBlock->rend(); I != E; ++I) { + CFGElement CE = *I; + if (CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) { + const Expr *ValEx = cast<Expr>(CS->getStmt()); + hasValue = true; + V = state->getSVal(ValEx, LCtx); + break; + } + } + + assert(hasValue); + (void) hasValue; + + // Generate a new node with the binding from the appropriate path. + B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true)); } void ExprEngine:: @@ -648,7 +726,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, } case UO_Plus: - assert(!U->isLValue()); + assert(!U->isGLValue()); // FALL-THROUGH. case UO_Deref: case UO_AddrOf: @@ -671,7 +749,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, case UO_LNot: case UO_Minus: case UO_Not: { - assert (!U->isLValue()); + assert (!U->isGLValue()); const Expr *Ex = U->getSubExpr()->IgnoreParens(); ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); @@ -796,7 +874,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, // Since the lvalue-to-rvalue conversion is explicit in the AST, // we bind an l-value if the operator is prefix and an lvalue (in C++). - if (U->isLValue()) + if (U->isGLValue()) state = state->BindExpr(U, LCtx, loc); else state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index a14a491..44a860f 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -14,26 +14,14 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" +#include "clang/Basic/PrettyStackTrace.h" using namespace clang; using namespace ento; -const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D, - const StackFrameContext *SFC) { - const Type *T = D->getTypeForDecl(); - QualType PT = getContext().getPointerType(QualType(T, 0)); - return svalBuilder.getRegionManager().getCXXThisRegion(PT, SFC); -} - -const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl, - const StackFrameContext *frameCtx) { - return svalBuilder.getRegionManager(). - getCXXThisRegion(decl->getThisType(getContext()), frameCtx); -} - void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -53,208 +41,220 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R))); } -void ExprEngine::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - VisitCXXConstructExpr(expr, 0, Pred, Dst); -} - -void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, - const MemRegion *Dest, +void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + const MemRegion *Target = 0; + + switch (CE->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: { + // See if we're constructing an existing region by looking at the next + // element in the CFG. + const CFGBlock *B = currentBuilderContext->getBlock(); + if (currentStmtIdx + 1 < B->size()) { + CFGElement Next = (*B)[currentStmtIdx+1]; + + // Is this a constructor for a local variable? + if (const CFGStmt *StmtElem = dyn_cast<CFGStmt>(&Next)) { + if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) { + if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { + if (Var->getInit()->IgnoreImplicit() == CE) { + QualType Ty = Var->getType(); + if (const ArrayType *AT = getContext().getAsArrayType(Ty)) { + // FIXME: Handle arrays, which run the same constructor for + // every element. This workaround will just run the first + // constructor (which should still invalidate the entire array). + SVal Base = State->getLValue(Var, LCtx); + Target = State->getLValue(AT->getElementType(), + getSValBuilder().makeZeroArrayIndex(), + Base).getAsRegion(); + } else { + Target = State->getLValue(Var, LCtx).getAsRegion(); + } + } + } + } + } + + // Is this a constructor for a member? + if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) { + const CXXCtorInitializer *Init = InitElem->getInitializer(); + assert(Init->isAnyMemberInitializer()); + + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + if (Init->isIndirectMemberInitializer()) { + SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal); + Target = Field.getAsRegion(); + } else { + SVal Field = State->getLValue(Init->getMember(), ThisVal); + Target = Field.getAsRegion(); + } + } -#if 0 - const CXXConstructorDecl *CD = E->getConstructor(); - assert(CD); -#endif - -#if 0 - if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) - // FIXME: invalidate the object. - return; -#endif - -#if 0 - // Is the constructor elidable? - if (E->isElidable()) { - destNodes.Add(Pred); - return; - } -#endif - - // Perform the previsit of the constructor. - ExplodedNodeSet SrcNodes; - SrcNodes.Add(Pred); - ExplodedNodeSet TmpNodes; - getCheckerManager().runCheckersForPreStmt(TmpNodes, SrcNodes, E, *this); - - // Evaluate the constructor. Currently we don't now allow checker-specific - // implementations of specific constructors (as we do with ordinary - // function calls. We can re-evaluate this in the future. - -#if 0 - // Inlining currently isn't fully implemented. - - if (AMgr.shouldInlineCall()) { - if (!Dest) - Dest = - svalBuilder.getRegionManager().getCXXTempObjectRegion(E, - Pred->getLocationContext()); - - // The callee stack frame context used to create the 'this' - // parameter region. - const StackFrameContext *SFC = - AMgr.getStackFrame(CD, Pred->getLocationContext(), - E, currentBuilderContext->getBlock(), - currentStmtIdx); - - // Create the 'this' region. - const CXXThisRegion *ThisR = - getCXXThisRegion(E->getConstructor()->getParent(), SFC); - - CallEnter Loc(E, SFC, Pred->getLocationContext()); - - StmtNodeBuilder Bldr(SrcNodes, TmpNodes, *currentBuilderContext); - for (ExplodedNodeSet::iterator NI = SrcNodes.begin(), - NE = SrcNodes.end(); NI != NE; ++NI) { - ProgramStateRef state = (*NI)->getState(); - // Setup 'this' region, so that the ctor is evaluated on the object pointed - // by 'Dest'. - state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - Bldr.generateNode(Loc, *NI, state); + // FIXME: This will eventually need to handle new-expressions as well. } + + // If we couldn't find an existing region to construct into, we'll just + // generate a symbolic region, which is fine. + + break; } -#endif - - // Default semantics: invalidate all regions passed as arguments. - ExplodedNodeSet destCall; - { - StmtNodeBuilder Bldr(TmpNodes, destCall, *currentBuilderContext); - for (ExplodedNodeSet::iterator i = TmpNodes.begin(), e = TmpNodes.end(); - i != e; ++i) - { - ExplodedNode *Pred = *i; - const LocationContext *LC = Pred->getLocationContext(); - ProgramStateRef state = Pred->getState(); - - state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC); - Bldr.generateNode(E, Pred, state); + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + case CXXConstructExpr::CK_Delegating: { + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { + Target = ThisVal.getAsRegion(); + } else { + // Cast to the base type. + QualType BaseTy = CE->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + Target = BaseVal.getAsRegion(); } + break; + } } - // Do the post visit. - getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXConstructorCall> Call = + CEMgr.getCXXConstructorCall(CE, Target, State, LCtx); + + ExplodedNodeSet DstPreVisit; + getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit, + *Call, *this); + + ExplodedNodeSet DstInvalidated; + StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext); + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated, + *Call, *this); + getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } -void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, - const MemRegion *Dest, - const Stmt *S, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); - if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) - return; +void ExprEngine::VisitCXXDestructor(QualType ObjectType, + const MemRegion *Dest, + const Stmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) { + ObjectType = AT->getElementType(); + Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(), + loc::MemRegionVal(Dest)).getAsRegion(); + } - // Create the context for 'this' region. - const StackFrameContext *SFC = - AnalysisDeclContexts.getContext(DD)-> - getStackFrame(Pred->getLocationContext(), S, - currentBuilderContext->getBlock(), currentStmtIdx); + const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); + assert(RecordDecl && "Only CXXRecordDecls should have destructors"); + const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); - const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC); + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXDestructorCall> Call = + CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx); - CallEnter PP(S, SFC, Pred->getLocationContext()); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Call->getSourceRange().getBegin(), + "Error evaluating destructor"); - ProgramStateRef state = Pred->getState(); - state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - Bldr.generateNode(PP, Pred, state); + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, + *Call, *this); + + ExplodedNodeSet DstInvalidated; + StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext); + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, + *Call, *this); } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + // FIXME: Much of this should eventually migrate to CXXAllocatorCall. + // Also, we need to decide how allocators actually work -- they're not + // really part of the CXXNewExpr because they happen BEFORE the + // CXXConstructExpr subexpression. See PR12014 for some discussion. StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); unsigned blockCount = currentBuilderContext->getCurrentBlockCount(); const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = - svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount); - const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); - const ElementRegion *EleReg = - getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount); + ProgramStateRef State = Pred->getState(); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXAllocatorCall> Call = + CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + + // Invalidate placement args. + // FIXME: Once we figure out how we want allocators to work, + // we should be using the usual pre-/(default-)eval-/post-call checks here. + State = Call->invalidateRegions(blockCount); if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - ProgramStateRef state = Pred->getState(); - state = state->BindExpr(CNE, Pred->getLocationContext(), + const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); + QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + State = State->BindExpr(CNE, Pred->getLocationContext(), loc::MemRegionVal(EleReg)); - Bldr.generateNode(CNE, Pred, state); + Bldr.generateNode(CNE, Pred, State); return; } - // FIXME: Update for AST changes. -#if 0 - // Evaluate constructor arguments. - const FunctionProtoType *FnType = NULL; - const CXXConstructorDecl *CD = CNE->getConstructor(); - if (CD) - FnType = CD->getType()->getAs<FunctionProtoType>(); - ExplodedNodeSet argsEvaluated; - Bldr.takeNodes(Pred); - evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(), - FnType, Pred, argsEvaluated); - Bldr.addNodes(argsEvaluated); - - // Initialize the object region and bind the 'new' expression. - for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), - E = argsEvaluated.end(); I != E; ++I) { - - ProgramStateRef state = (*I)->getState(); - - // Accumulate list of regions that are invalidated. - // FIXME: Eventually we should unify the logic for constructor - // processing in one place. - SmallVector<const MemRegion*, 10> regionsToInvalidate; - for (CXXNewExpr::const_arg_iterator - ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end(); - ai != ae; ++ai) - { - SVal val = state->getSVal(*ai, (*I)->getLocationContext()); - if (const MemRegion *region = val.getAsRegion()) - regionsToInvalidate.push_back(region); - } + // FIXME: Once we have proper support for CXXConstructExprs inside + // CXXNewExpr, we need to make sure that the constructed object is not + // immediately invalidated here. (The placement call should happen before + // the constructor call anyway.) + FunctionDecl *FD = CNE->getOperatorNew(); + if (FD && FD->isReservedGlobalPlacementOperator()) { + // Non-array placement new should always return the placement location. + SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); + State = State->BindExpr(CNE, LCtx, PlacementLoc); + } else { + State = State->BindExpr(CNE, LCtx, symVal); + } - if (ObjTy->isRecordType()) { - regionsToInvalidate.push_back(EleReg); - // Invalidate the regions. - // TODO: Pass the call to new information as the last argument, to limit - // the globals which will get invalidated. - state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, 0); - - } else { - // Invalidate the regions. - // TODO: Pass the call to new information as the last argument, to limit - // the globals which will get invalidated. - state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, 0); - - if (CNE->hasInitializer()) { - SVal V = state->getSVal(*CNE->constructor_arg_begin(), - (*I)->getLocationContext()); - state = state->bindLoc(loc::MemRegionVal(EleReg), V); - } else { - // Explicitly set to undefined, because currently we retrieve symbolic - // value from symbolic region. - state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal()); - } + // If the type is not a record, we won't have a CXXConstructExpr as an + // initializer. Copy the value over. + if (const Expr *Init = CNE->getInitializer()) { + if (!isa<CXXConstructExpr>(Init)) { + QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + (void)ObjTy; + assert(!ObjTy->isRecordType()); + SVal Location = State->getSVal(CNE, LCtx); + if (isa<Loc>(Location)) + State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx)); } - state = state->BindExpr(CNE, (*I)->getLocationContext(), - loc::MemRegionVal(EleReg)); - Bldr.generateNode(CNE, *I, state); } -#endif + + Bldr.generateNode(CNE, Pred, State); } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index b9f4e15..8ee6723 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -11,16 +11,25 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "ExprEngine" + +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" +#define CXX_INLINING_ENABLED 1 + using namespace clang; using namespace ento; +STATISTIC(NumOfDynamicDispatchPathSplits, + "The # of times we split the path due to imprecise dynamic dispatch info"); + void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -37,11 +46,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, Succ, calleeCtx); - // Construct a new state which contains the mapping from actual to - // formal arguments. - const LocationContext *callerCtx = Pred->getLocationContext(); - ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx, - calleeCtx); + ProgramStateRef state = Pred->getState(); // Construct a new node and add it to the worklist. bool isNew; @@ -51,71 +56,178 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { Engine.getWorkList()->enqueue(Node); } -static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) { +// Find the last statement on the path to the exploded node and the +// corresponding Block. +static std::pair<const Stmt*, + const CFGBlock*> getLastStmt(const ExplodedNode *Node) { + const Stmt *S = 0; + const StackFrameContext *SF = + Node->getLocation().getLocationContext()->getCurrentStackFrame(); + + // Back up through the ExplodedGraph until we reach a statement node. while (Node) { const ProgramPoint &PP = Node->getLocation(); - // Skip any BlockEdges. - if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) { - assert(Node->pred_size() == 1); - Node = *Node->pred_begin(); - continue; - } + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { - const Stmt *S = SP->getStmt(); - return dyn_cast<ReturnStmt>(S); + S = SP->getStmt(); + break; + } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) { + S = CEE->getCalleeContext()->getCallSite(); + if (S) + break; + // If we have an implicit call, we'll probably end up with a + // StmtPoint inside the callee, which is acceptable. + // (It's possible a function ONLY contains implicit calls -- such as an + // implicitly-generated destructor -- so we shouldn't just skip back to + // the CallEnter node and keep going.) + } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) { + // If we reached the CallEnter for this function, it has no statements. + if (CE->getCalleeContext() == SF) + break; } - break; + + Node = *Node->pred_begin(); } - return 0; + + const CFGBlock *Blk = 0; + if (S) { + // Now, get the enclosing basic block. + while (Node && Node->pred_size() >=1 ) { + const ProgramPoint &PP = Node->getLocation(); + if (isa<BlockEdge>(PP) && + (PP.getLocationContext()->getCurrentStackFrame() == SF)) { + BlockEdge &EPP = cast<BlockEdge>(PP); + Blk = EPP.getDst(); + break; + } + Node = *Node->pred_begin(); + } + } + + return std::pair<const Stmt*, const CFGBlock*>(S, Blk); } -void ExprEngine::processCallExit(ExplodedNode *Pred) { - ProgramStateRef state = Pred->getState(); - const StackFrameContext *calleeCtx = - Pred->getLocationContext()->getCurrentStackFrame(); - const LocationContext *callerCtx = calleeCtx->getParent(); - const Stmt *CE = calleeCtx->getCallSite(); +/// The call exit is simulated with a sequence of nodes, which occur between +/// CallExitBegin and CallExitEnd. The following operations occur between the +/// two program points: +/// 1. CallExitBegin (triggers the start of call exit sequence) +/// 2. Bind the return value +/// 3. Run Remove dead bindings to clean up the dead symbols from the callee. +/// 4. CallExitEnd (switch to the caller context) +/// 5. PostStmt<CallExpr> +void ExprEngine::processCallExit(ExplodedNode *CEBNode) { + // Step 1 CEBNode was generated before the call. + + const StackFrameContext *calleeCtx = + CEBNode->getLocationContext()->getCurrentStackFrame(); + + // The parent context might not be a stack frame, so make sure we + // look up the first enclosing stack frame. + const StackFrameContext *callerCtx = + calleeCtx->getParent()->getCurrentStackFrame(); + const Stmt *CE = calleeCtx->getCallSite(); + ProgramStateRef state = CEBNode->getState(); + // Find the last statement in the function and the corresponding basic block. + const Stmt *LastSt = 0; + const CFGBlock *Blk = 0; + llvm::tie(LastSt, Blk) = getLastStmt(CEBNode); + + // Step 2: generate node with bound return value: CEBNode -> BindedRetNode. + // If the callee returns an expression, bind its value to CallExpr. - if (const ReturnStmt *RS = getReturnStmt(Pred)) { - const LocationContext *LCtx = Pred->getLocationContext(); - SVal V = state->getSVal(RS, LCtx); - state = state->BindExpr(CE, callerCtx, V); + if (CE) { + if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { + const LocationContext *LCtx = CEBNode->getLocationContext(); + SVal V = state->getSVal(RS, LCtx); + state = state->BindExpr(CE, callerCtx, V); + } + + // Bind the constructed object value to CXXConstructExpr. + if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { + loc::MemRegionVal This = + svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); + SVal ThisV = state->getSVal(This); + + // Always bind the region to the CXXConstructExpr. + state = state->BindExpr(CCE, callerCtx, ThisV); + } } - - // Bind the constructed object value to CXXConstructExpr. - if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { - const CXXThisRegion *ThisR = - getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); - - SVal ThisV = state->getSVal(ThisR); - // Always bind the region to the CXXConstructExpr. - state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV); + + // Step 3: BindedRetNode -> CleanedNodes + // If we can find a statement and a block in the inlined function, run remove + // dead bindings before returning from the call. This is important to ensure + // that we report the issues such as leaks in the stack contexts in which + // they occurred. + ExplodedNodeSet CleanedNodes; + if (LastSt && Blk) { + static SimpleProgramPointTag retValBind("ExprEngine : Bind Return Value"); + PostStmt Loc(LastSt, calleeCtx, &retValBind); + bool isNew; + ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); + BindedRetNode->addPredecessor(CEBNode, G); + if (!isNew) + return; + + NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); + currentBuilderContext = &Ctx; + // Here, we call the Symbol Reaper with 0 statement and caller location + // context, telling it to clean up everything in the callee's context + // (and it's children). We use LastStmt as a diagnostic statement, which + // which the PreStmtPurge Dead point will be associated. + removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt, + ProgramPoint::PostStmtPurgeDeadSymbolsKind); + currentBuilderContext = 0; + } else { + CleanedNodes.Add(CEBNode); } - - static SimpleProgramPointTag returnTag("ExprEngine : Call Return"); - PostStmt Loc(CE, callerCtx, &returnTag); - bool isNew; - ExplodedNode *N = G.getNode(Loc, state, false, &isNew); - N->addPredecessor(Pred, G); - if (!isNew) - return; - - // Perform the post-condition check of the CallExpr. - ExplodedNodeSet Dst; - NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N); - SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, - &Ctx); - SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); - - getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this, - /* wasInlined */ true); - - // Enqueue the next element in the block. - for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) { - Engine.getWorkList()->enqueue(*I, - calleeCtx->getCallSiteBlock(), - calleeCtx->getIndex()+1); + + for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), + E = CleanedNodes.end(); I != E; ++I) { + + // Step 4: Generate the CallExit and leave the callee's context. + // CleanedNodes -> CEENode + CallExitEnd Loc(calleeCtx, callerCtx); + bool isNew; + ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); + ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); + CEENode->addPredecessor(*I, G); + if (!isNew) + return; + + // Step 5: Perform the post-condition check of the CallExpr and enqueue the + // result onto the work list. + // CEENode -> Dst -> WorkList + NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); + SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, + &Ctx); + SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call, + *this, true); + + ExplodedNodeSet Dst; + if (isa<ObjCMethodCall>(Call)) { + getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, + cast<ObjCMethodCall>(*Call), + *this, true); + } else if (CE) { + getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, + *this, true); + } else { + Dst.insert(DstPostCall); + } + + // Enqueue the next element in the block. + for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); + PSI != PSE; ++PSI) { + Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), + calleeCtx->getIndex()+1); + } } } @@ -130,8 +242,8 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) { } // Determine if we should inline the call. -bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); +bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const CFG *CalleeCFG = CalleeADC->getCFG(); // It is possible that the CFG cannot be constructed. @@ -143,258 +255,185 @@ bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { == AMgr.InlineMaxStackDepth) return false; - if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD)) + if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D)) return false; if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize) return false; - return true; -} + // Do not inline variadic calls (for now). + if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { + if (BD->isVariadic()) + return false; + } + else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isVariadic()) + return false; + } -// For now, skip inlining variadic functions. -// We also don't inline blocks. -static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { - if (!E->getAnalysisManager().shouldInlineCall()) + // It is possible that the live variables analysis cannot be + // run. If so, bail out. + if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) return false; - QualType callee = CE->getCallee()->getType(); - const FunctionProtoType *FT = 0; - if (const PointerType *PT = callee->getAs<PointerType>()) - FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); - else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { - // FIXME: inline blocks. - // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); - (void) BT; - return false; - } - // If we have no prototype, assume the function is okay. - if (!FT) - return true; - // Skip inlining of variadic functions. - return !FT->isVariadic(); + return true; } -bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, - const CallExpr *CE, - ExplodedNode *Pred) { - if (!shouldInlineCallExpr(CE, this)) - return false; - - ProgramStateRef state = Pred->getState(); - const Expr *Callee = CE->getCallee(); - const FunctionDecl *FD = - state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl(); - if (!FD || !FD->hasBody(FD)) - return false; - - switch (CE->getStmtClass()) { - default: - // FIXME: Handle C++. - break; - case Stmt::CallExprClass: { - if (!shouldInlineDecl(FD, Pred)) +/// The GDM component containing the dynamic dispatch bifurcation info. When +/// the exact type of the receiver is not known, we want to explore both paths - +/// one on which we do inline it and the other one on which we don't. This is +/// done to ensure we do not drop coverage. +/// This is the map from the receiver region to a bool, specifying either we +/// consider this region's information precise or not along the given path. +namespace clang { +namespace ento { +enum DynamicDispatchMode { DynamicDispatchModeInlined = 1, + DynamicDispatchModeConservative }; + +struct DynamicDispatchBifurcationMap {}; +typedef llvm::ImmutableMap<const MemRegion*, + unsigned int> DynamicDispatchBifur; +template<> struct ProgramStateTrait<DynamicDispatchBifurcationMap> + : public ProgramStatePartialTrait<DynamicDispatchBifur> { + static void *GDMIndex() { static int index; return &index; } +}; + +}} + +bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred, + ProgramStateRef State) { + assert(D); + + const LocationContext *CurLC = Pred->getLocationContext(); + const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); + const LocationContext *ParentOfCallee = 0; + + // FIXME: Refactor this check into a hypothetical CallEvent::canInline. + switch (Call.getKind()) { + case CE_Function: + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + if (!CXX_INLINING_ENABLED) + return false; + break; + case CE_CXXConstructor: { + if (!CXX_INLINING_ENABLED) + return false; + + // Only inline constructors and destructors if we built the CFGs for them + // properly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + if (!ADC->getCFGBuildOptions().AddImplicitDtors || + !ADC->getCFGBuildOptions().AddInitializers) + return false; + + const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); + + // FIXME: We don't handle constructors or destructors for arrays properly. + const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion(); + if (Target && isa<ElementRegion>(Target)) + return false; + + // FIXME: This is a hack. We don't handle temporary destructors + // right now, so we shouldn't inline their constructors. + const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); + if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) + if (!Target || !isa<DeclRegion>(Target)) return false; - // Construct a new stack frame for the callee. - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); - const StackFrameContext *CallerSFC = - Pred->getLocationContext()->getCurrentStackFrame(); - const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(CallerSFC, CE, - currentBuilderContext->getBlock(), - currentStmtIdx); - - CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); - bool isNew; - if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { - N->addPredecessor(Pred, G); - if (isNew) - Engine.getWorkList()->enqueue(N); - } - return true; - } + break; } - return false; -} + case CE_CXXDestructor: { + if (!CXX_INLINING_ENABLED) + return false; -static bool isPointerToConst(const ParmVarDecl *ParamDecl) { - QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType(); - if (PointeeTy != QualType() && PointeeTy.isConstQualified() && - !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) { - return true; - } - return false; -} + // Only inline constructors and destructors if we built the CFGs for them + // properly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + if (!ADC->getCFGBuildOptions().AddImplicitDtors || + !ADC->getCFGBuildOptions().AddInitializers) + return false; -// Try to retrieve the function declaration and find the function parameter -// types which are pointers/references to a non-pointer const. -// We do not invalidate the corresponding argument regions. -static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, - const CallOrObjCMessage &Call) { - const Decl *CallDecl = Call.getDecl(); - if (!CallDecl) - return; + const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call); - if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) { - const IdentifierInfo *II = FDecl->getIdentifier(); - - // List the cases, where the region should be invalidated even if the - // argument is const. - if (II) { - StringRef FName = II->getName(); - // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a - // value into thread local storage. The value can later be retrieved with - // 'void *ptheread_getspecific(pthread_key)'. So even thought the - // parameter is 'const void *', the region escapes through the call. - // - funopen - sets a buffer for future IO calls. - // - ObjC functions that end with "NoCopy" can free memory, of the passed - // in buffer. - // - Many CF containers allow objects to escape through custom - // allocators/deallocators upon container construction. - // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. - if (FName == "pthread_setspecific" || - FName == "funopen" || - FName.endswith("NoCopy") || - (FName.startswith("NS") && - (FName.find("Insert") != StringRef::npos)) || - Call.isCFCGAllowingEscape(FName)) - return; - } + // FIXME: We don't handle constructors or destructors for arrays properly. + const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion(); + if (Target && isa<ElementRegion>(Target)) + return false; - for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) { - if (FDecl && Idx < FDecl->getNumParams()) { - if (isPointerToConst(FDecl->getParamDecl(Idx))) - PreserveArgs.insert(Idx); - } - } - return; + break; } + case CE_CXXAllocator: + if (!CXX_INLINING_ENABLED) + return false; - if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) { - assert(MDecl->param_size() <= Call.getNumArgs()); - unsigned Idx = 0; - for (clang::ObjCMethodDecl::param_const_iterator - I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) { - if (isPointerToConst(*I)) - PreserveArgs.insert(Idx); - } - return; + // Do not inline allocators until we model deallocators. + // This is unfortunate, but basically necessary for smart pointers and such. + return false; + case CE_Block: { + const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion(); + assert(BR && "If we have the block definition we should have its region"); + AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); + ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, + cast<BlockDecl>(D), + BR); + break; } -} - -ProgramStateRef -ExprEngine::invalidateArguments(ProgramStateRef State, - const CallOrObjCMessage &Call, - const LocationContext *LC) { - SmallVector<const MemRegion *, 8> RegionsToInvalidate; - - if (Call.isObjCMessage()) { - // Invalidate all instance variables of the receiver of an ObjC message. - // FIXME: We should be able to do better with inter-procedural analysis. - if (const MemRegion *MR = Call.getInstanceMessageReceiver(LC).getAsRegion()) - RegionsToInvalidate.push_back(MR); - - } else if (Call.isCXXCall()) { - // Invalidate all instance variables for the callee of a C++ method call. - // FIXME: We should be able to do better with inter-procedural analysis. - // FIXME: We can probably do better for const versus non-const methods. - if (const MemRegion *Callee = Call.getCXXCallee().getAsRegion()) - RegionsToInvalidate.push_back(Callee); - - } else if (Call.isFunctionCall()) { - // Block calls invalidate all captured-by-reference values. - SVal CalleeVal = Call.getFunctionCallee(); - if (const MemRegion *Callee = CalleeVal.getAsRegion()) { - if (isa<BlockDataRegion>(Callee)) - RegionsToInvalidate.push_back(Callee); - } + case CE_ObjCMessage: + if (!(getAnalysisManager().IPAMode == DynamicDispatch || + getAnalysisManager().IPAMode == DynamicDispatchBifurcate)) + return false; + break; } - // Indexes of arguments whose values will be preserved by the call. - llvm::SmallSet<unsigned, 1> PreserveArgs; - findPtrToConstParams(PreserveArgs, Call); - - for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) { - if (PreserveArgs.count(idx)) - continue; - - SVal V = Call.getArgSVal(idx); - - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) - V = Wrapped->getLoc(); - else if (!isa<Loc>(V)) - continue; - - if (const MemRegion *R = V.getAsRegion()) { - // Invalidate the value of the variable passed by reference. - - // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underlying region - // is a variable region then strip off the ElementRegion. - // FIXME: We really need to think about this for the general case - // as sometimes we are reasoning about arrays and other times - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // we'll leave it in for now until we have a systematic way of - // handling all of these cases. Eventually we need to come up - // with an interface to StoreManager so that this logic can be - // appropriately delegated to the respective StoreManagers while - // still allowing us to do checker-specific logic (e.g., - // invalidating reference counts), probably via callbacks. - if (ER->getElementType()->isIntegralOrEnumerationType()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || - isa<ObjCIvarRegion>(superReg)) - R = cast<TypedRegion>(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - } else { - // Nuke all other arguments passed by reference. - // FIXME: is this necessary or correct? This handles the non-Region - // cases. Is it ever valid to store to these? - State = State->unbindLoc(cast<Loc>(V)); - } - } + if (!shouldInlineDecl(D, Pred)) + return false; + + if (!ParentOfCallee) + ParentOfCallee = CallerSFC; + + // This may be NULL, but that's fine. + const Expr *CallE = Call.getOriginExpr(); + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(ParentOfCallee, CallE, + currentBuilderContext->getBlock(), + currentStmtIdx); + + CallEnter Loc(CallE, CalleeSFC, CurLC); - // Invalidate designated regions using the batch invalidation API. + // Construct a new state which contains the mapping from actual to + // formal arguments. + State = State->enterStackFrame(Call, CalleeSFC); - // FIXME: We can have collisions on the conjured symbol if the - // expression *I also creates conjured symbols. We probably want - // to identify conjured symbols by an expression pair: the enclosing - // expression (the context) and the expression itself. This should - // disambiguate conjured symbols. - unsigned Count = currentBuilderContext->getCurrentBlockCount(); - StoreManager::InvalidatedSymbols IS; + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } - // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate - // global variables. - return State->invalidateRegions(RegionsToInvalidate, - Call.getOriginExpr(), Count, LC, - &IS, &Call); + // If we decided to inline the call, the successor has been manually + // added onto the work list so remove it from the node builder. + Bldr.takeNodes(Pred); + return true; } -static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N, - const CallExpr *CE) { - void *ReplayState = N->getState()->get<ReplayWithoutInlining>(); +static ProgramStateRef getInlineFailedState(ProgramStateRef State, + const Stmt *CallE) { + void *ReplayState = State->get<ReplayWithoutInlining>(); if (!ReplayState) return 0; - const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState); - if (CE == ReplayCE) { - return N->getState()->remove<ReplayWithoutInlining>(); - } - return 0; + + assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call."); + (void)CallE; + + return State->remove<ReplayWithoutInlining>(); } void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, @@ -402,74 +441,175 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // Perform the previsit of the CallExpr. ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this); - - // Now evaluate the call itself. - class DefaultEval : public GraphExpander { - ExprEngine &Eng; - const CallExpr *CE; - public: - - DefaultEval(ExprEngine &eng, const CallExpr *ce) - : Eng(eng), CE(ce) {} - virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - - ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); - - // First, try to inline the call. - if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) - return; - // First handle the return value. - StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); + // Get the call in its initial state. We use this as a template to perform + // all the checks. + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> CallTemplate + = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext()); - // Get the callee. - const Expr *Callee = CE->getCallee()->IgnoreParens(); - if (state == 0) - state = Pred->getState(); - SVal L = state->getSVal(Callee, Pred->getLocationContext()); + // Evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet dstCallEvaluated; + for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); + I != E; ++I) { + evalCall(dstCallEvaluated, *I, *CallTemplate); + } + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + // Note that if the call was inlined, dstCallEvaluated will be empty. + // The post-CallExpr check will occur in processCallExit. + getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, + *this); +} - // Figure out the result type. We do this dance to handle references. - QualType ResultTy; - if (const FunctionDecl *FD = L.getAsFunctionDecl()) - ResultTy = FD->getResultType(); - else - ResultTy = CE->getType(); +void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call) { + // WARNING: At this time, the state attached to 'Call' may be older than the + // state in 'Pred'. This is a minor optimization since CheckerManager will + // use an updated CallEvent instance when calling checkers, but if 'Call' is + // ever used directly in this function all callers should be updated to pass + // the most recent state. (It is probably not worth doing the work here since + // for some callers this will not be necessary.) - if (CE->isLValue()) - ResultTy = Eng.getContext().getPointerType(ResultTy); + // Run any pre-call checks using the generic call interface. + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this); - // Conjure a symbol value to use as the result. - SValBuilder &SVB = Eng.getSValBuilder(); - unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); - const LocationContext *LCtx = Pred->getLocationContext(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); + // Actually evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call, and get a callback at + // defaultEvalCall if all of them fail. + ExplodedNodeSet dstCallEvaluated; + getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, + Call, *this); - // Generate a new state with the return value set. - state = state->BindExpr(CE, LCtx, RetVal); + // Finally, run any post-call checks. + getCheckerManager().runCheckersForPostCall(Dst, dstCallEvaluated, + Call, *this); +} - // Invalidate the arguments. - state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx), - LCtx); +ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, + const LocationContext *LCtx, + ProgramStateRef State) { + const Expr *E = Call.getOriginExpr(); + if (!E) + return State; - // And make the result node. - Bldr.generateNode(CE, Pred, state); + // Some method families have known return values. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { + switch (Msg->getMethodFamily()) { + default: + break; + case OMF_autorelease: + case OMF_retain: + case OMF_self: { + // These methods return their receivers. + return State->BindExpr(E, LCtx, Msg->getReceiverSVal()); } - }; - - // Finally, evaluate the function call. We try each of the checkers - // to see if the can evaluate the function call. - ExplodedNodeSet dstCallEvaluated; - DefaultEval defEval(*this, CE); - getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, - dstPreVisit, - CE, *this, &defEval); - - // Finally, perform the post-condition check of the CallExpr and store - // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, - *this); + } + } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){ + return State->BindExpr(E, LCtx, C->getCXXThisVal()); + } + + // Conjure a symbol if the return value is unknown. + QualType ResultTy = Call.getResultType(); + SValBuilder &SVB = getSValBuilder(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + SVal R = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count); + return State->BindExpr(E, LCtx, R); +} + +// Conservatively evaluate call by invalidating regions and binding +// a conjured return value. +void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State) { + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + State = Call.invalidateRegions(Count, State); + State = bindReturnValue(Call, Pred->getLocationContext(), State); + + // And make the result node. + Bldr.generateNode(Call.getProgramPoint(), State, Pred); +} + +void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &CallTemplate) { + // Make sure we have the most recent state attached to the call. + ProgramStateRef State = Pred->getState(); + CallEventRef<> Call = CallTemplate.cloneWithState(State); + + if (!getAnalysisManager().shouldInlineCall()) { + conservativeEvalCall(*Call, Bldr, Pred, State); + return; + } + // Try to inline the call. + // The origin expression here is just used as a kind of checksum; + // this should still be safe even for CallEvents that don't come from exprs. + const Expr *E = Call->getOriginExpr(); + ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); + + if (InlinedFailedState) { + // If we already tried once and failed, make sure we don't retry later. + State = InlinedFailedState; + } else { + RuntimeDefinition RD = Call->getRuntimeDefinition(); + const Decl *D = RD.getDecl(); + if (D) { + // Explore with and without inlining the call. + if (RD.mayHaveOtherDefinitions() && + getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { + BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); + return; + } + // We are not bifurcating and we do have a Decl, so just inline. + if (inlineCall(*Call, D, Bldr, Pred, State)) + return; + } + } + + // If we can't inline it, handle the return value and invalidate the regions. + conservativeEvalCall(*Call, Bldr, Pred, State); } +void ExprEngine::BifurcateCall(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred) { + assert(BifurReg); + + // Check if we've performed the split already - note, we only want + // to split the path once per memory region. + ProgramStateRef State = Pred->getState(); + const unsigned int *BState = + State->get<DynamicDispatchBifurcationMap>(BifurReg); + if (BState) { + // If we are on "inline path", keep inlining if possible. + if (*BState == DynamicDispatchModeInlined) + if (inlineCall(Call, D, Bldr, Pred, State)) + return; + // If inline failed, or we are on the path where we assume we + // don't have enough info about the receiver to inline, conjure the + // return value and invalidate the regions. + conservativeEvalCall(Call, Bldr, Pred, State); + return; + } + + // If we got here, this is the first time we process a message to this + // region, so split the path. + ProgramStateRef IState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, + DynamicDispatchModeInlined); + inlineCall(Call, D, Bldr, Pred, IState); + + ProgramStateRef NoIState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, + DynamicDispatchModeConservative); + conservativeEvalCall(Call, Bldr, Pred, NoIState); + + NumOfDynamicDispatchPathSplits++; + return; +} + + void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index c8ad70a..e3bc498 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -13,8 +13,8 @@ #include "clang/AST/StmtObjC.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" using namespace clang; using namespace ento; @@ -74,7 +74,6 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, const Stmt *elem = S->getElement(); ProgramStateRef state = Pred->getState(); SVal elementV; - StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); @@ -86,10 +85,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, } ExplodedNodeSet dstLocation; - Bldr.takeNodes(Pred); evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false); - Bldr.addNodes(dstLocation); - + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); + for (ExplodedNodeSet::iterator NI = dstLocation.begin(), NE = dstLocation.end(); NI!=NE; ++NI) { Pred = *NI; @@ -126,148 +126,135 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, Bldr.generateNode(S, Pred, hasElems); Bldr.generateNode(S, Pred, noElems); } + + // Finally, run any custom checkers. + // FIXME: Eventually all pre- and post-checks should live in VisitStmt. + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); +} + +static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) { + if (!Class) + return false; + if (Class->getIdentifier() == II) + return true; + return isSubclass(Class->getSuperClass(), II); } -void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, +void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<ObjCMethodCall> Msg = + CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); + // Handle the previsits checks. ExplodedNodeSet dstPrevisit; - getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, - msg, *this); - + getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, + *Msg, *this); + ExplodedNodeSet dstGenericPrevisit; + getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, + *Msg, *this); + // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext); + StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currentBuilderContext); - for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(), - DE = dstPrevisit.end(); DI != DE; ++DI) { - + for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), + DE = dstGenericPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; - bool RaisesException = false; + ProgramStateRef State = Pred->getState(); + CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); - if (const Expr *Receiver = msg.getInstanceReceiver()) { - ProgramStateRef state = Pred->getState(); - SVal recVal = state->getSVal(Receiver, Pred->getLocationContext()); + if (UpdatedMsg->isInstanceMessage()) { + SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef notNilState, nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); + llvm::tie(notNilState, nilState) = State->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. + // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). + // Revisit once we have lazier constraints. if (nilState && !notNilState) { continue; } // Check if the "raise" message was sent. assert(notNilState); - if (msg.getSelector() == RaiseSel) - RaisesException = true; + if (Msg->getSelector() == RaiseSel) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Bldr.generateNode(currentStmt, Pred, State, true); + continue; + } - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - // Dispatch to plug-in transfer function. - evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException); - } - } - else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { - IdentifierInfo* ClsName = Iface->getIdentifier(); - Selector S = msg.getSelector(); - - // Check for special instance methods. - if (!NSExceptionII) { - ASTContext &Ctx = getContext(); - NSExceptionII = &Ctx.Idents.get("NSException"); + // Generate a transition to non-Nil state. + if (notNilState != State) + Pred = Bldr.generateNode(currentStmt, Pred, notNilState); } - - if (ClsName == NSExceptionII) { - enum { NUM_RAISE_SELECTORS = 2 }; - - // Lazily create a cache of the selectors. - if (!NSExceptionInstanceRaiseSelectors) { + } else { + // Check for special class methods. + if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) { + if (!NSExceptionII) { ASTContext &Ctx = getContext(); - NSExceptionInstanceRaiseSelectors = - new Selector[NUM_RAISE_SELECTORS]; - SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; - unsigned idx = 0; - - // raise:format: - II.push_back(&Ctx.Idents.get("raise")); - II.push_back(&Ctx.Idents.get("format")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - - // raise:format::arguments: - II.push_back(&Ctx.Idents.get("arguments")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); + NSExceptionII = &Ctx.Idents.get("NSException"); } - for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) - if (S == NSExceptionInstanceRaiseSelectors[i]) { - RaisesException = true; - break; + if (isSubclass(Iface, NSExceptionII)) { + enum { NUM_RAISE_SELECTORS = 2 }; + + // Lazily create a cache of the selectors. + if (!NSExceptionInstanceRaiseSelectors) { + ASTContext &Ctx = getContext(); + NSExceptionInstanceRaiseSelectors = + new Selector[NUM_RAISE_SELECTORS]; + SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; + unsigned idx = 0; + + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + + // raise:format:arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); } + + Selector S = Msg->getSelector(); + bool RaisesException = false; + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) { + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; + break; + } + } + if (RaisesException) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Bldr.generateNode(currentStmt, Pred, Pred->getState(), true); + continue; + } + + } } - - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - // Dispatch to plug-in transfer function. - evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException); } + + // Evaluate the call. + defaultEvalCall(Bldr, Pred, *UpdatedMsg); } + ExplodedNodeSet dstPostvisit; + getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, + *Msg, *this); + // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); + getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit, + *Msg, *this); } - -void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr, - const ObjCMessage &msg, - ExplodedNode *Pred, - ProgramStateRef state, - bool GenSink) { - // First handle the return value. - SVal ReturnValue = UnknownVal(); - - // Some method families have known return values. - switch (msg.getMethodFamily()) { - default: - break; - case OMF_autorelease: - case OMF_retain: - case OMF_self: { - // These methods return their receivers. - const Expr *ReceiverE = msg.getInstanceReceiver(); - if (ReceiverE) - ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext()); - break; - } - } - - // If we failed to figure out the return value, use a conjured value instead. - if (ReturnValue.isUnknown()) { - SValBuilder &SVB = getSValBuilder(); - QualType ResultTy = msg.getResultType(getContext()); - unsigned Count = currentBuilderContext->getCurrentBlockCount(); - const Expr *CurrentE = cast<Expr>(currentStmt); - const LocationContext *LCtx = Pred->getLocationContext(); - ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count); - } - - // Bind the return value. - const LocationContext *LCtx = Pred->getLocationContext(); - state = state->BindExpr(currentStmt, LCtx, ReturnValue); - - // Invalidate the arguments (and the receiver) - state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx); - - // And create the new node. - Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink); - assert(Bldr.hasGeneratedNodes()); -} - diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 629f1ea..0152e32 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -95,37 +95,6 @@ void HTMLDiagnostics::FlushDiagnosticsImpl( } } -static void flattenPath(PathPieces &primaryPath, PathPieces ¤tPath, - const PathPieces &oldPath) { - for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end(); - it != et; ++it ) { - PathDiagnosticPiece *piece = it->getPtr(); - if (const PathDiagnosticCallPiece *call = - dyn_cast<PathDiagnosticCallPiece>(piece)) { - IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = - call->getCallEnterEvent(); - if (callEnter) - currentPath.push_back(callEnter); - flattenPath(primaryPath, primaryPath, call->path); - IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = - call->getCallExitEvent(); - if (callExit) - currentPath.push_back(callExit); - continue; - } - if (PathDiagnosticMacroPiece *macro = - dyn_cast<PathDiagnosticMacroPiece>(piece)) { - currentPath.push_back(piece); - PathPieces newPath; - flattenPath(primaryPath, newPath, macro->subPieces); - macro->subPieces = newPath; - continue; - } - - currentPath.push_back(piece); - } -} - void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, SmallVectorImpl<std::string> *FilesMade) { @@ -152,8 +121,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, return; // First flatten out the entire path to make it easier to use. - PathPieces path; - flattenPath(path, path, D.path); + PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false); // The path as already been prechecked that all parts of the path are // from the same file and that it is non-empty. @@ -428,6 +396,15 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "<div class=\"PathIndex"; if (Kind) os << " PathIndex" << Kind; os << "\">" << num << "</div>"; + + if (num > 1) { + os << "</td><td><div class=\"PathNav\"><a href=\"#Path" + << (num - 1) + << "\" title=\"Previous event (" + << (num - 1) + << ")\">←</a></div></td>"; + } + os << "</td><td>"; } @@ -441,9 +418,10 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); - const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); - Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(), - MacroName, BufferInfo.end()); + std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc(); + const char* MacroName = LocInfo.second + BufferInfo.data(); + Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(), + BufferInfo.begin(), MacroName, BufferInfo.end()); Token TheTok; rawLexer.LexFromRawLexer(TheTok); @@ -453,8 +431,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "':\n"; - if (max > 1) - os << "</td></tr></table>"; + if (max > 1) { + os << "</td>"; + if (num < max) { + os << "<td><div class=\"PathNav\"><a href=\"#"; + if (num == max - 1) + os << "EndPath"; + else + os << "Path" << (num + 1); + os << "\" title=\"Next event (" + << (num + 1) + << ")\">→</a></div></td>"; + } + + os << "</tr></table>"; + } // Within a macro piece. Write out each event. ProcessMacroPiece(os, *MP, 0); @@ -462,8 +453,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, else { os << html::EscapeText(P.getString()); - if (max > 1) - os << "</td></tr></table>"; + if (max > 1) { + os << "</td>"; + if (num < max) { + os << "<td><div class=\"PathNav\"><a href=\"#"; + if (num == max - 1) + os << "EndPath"; + else + os << "Path" << (num + 1); + os << "\" title=\"Next event (" + << (num + 1) + << ")\">→</a></div></td>"; + } + + os << "</tr></table>"; + } } os << "</div></td></tr>"; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index ed94c79..62e602a 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -179,7 +179,7 @@ const StackFrameContext *VarRegion::getStackFrame() const { // Region extents. //===----------------------------------------------------------------------===// -DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const { +DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const { ASTContext &Ctx = svalBuilder.getContext(); QualType T = getDesugaredValueType(Ctx); @@ -470,7 +470,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { - os << "base " << decl->getName(); + os << "base{" << superRegion << ',' << decl->getName() << '}'; } void CXXThisRegion::dumpToStream(raw_ostream &os) const { @@ -518,10 +518,6 @@ void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } -void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { - os << "NonStaticGlobalSpaceRegion"; -} - void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalInternalSpaceRegion"; } @@ -534,17 +530,45 @@ void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalImmutableSpaceRegion"; } -void MemRegion::dumpPretty(raw_ostream &os) const { +void HeapSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "HeapSpaceRegion"; +} + +void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "UnknownSpaceRegion"; +} + +void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StackArgumentsSpaceRegion"; +} + +void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StackLocalsSpaceRegion"; +} + +bool MemRegion::canPrintPretty() const { + return false; +} + +void MemRegion::printPretty(raw_ostream &os) const { return; } -void VarRegion::dumpPretty(raw_ostream &os) const { +bool VarRegion::canPrintPretty() const { + return true; +} + +void VarRegion::printPretty(raw_ostream &os) const { os << getDecl()->getName(); } -void FieldRegion::dumpPretty(raw_ostream &os) const { - superRegion->dumpPretty(os); - os << "->" << getDecl(); +bool FieldRegion::canPrintPretty() const { + return superRegion->canPrintPretty(); +} + +void FieldRegion::printPretty(raw_ostream &os) const { + superRegion->printPretty(os); + os << "." << getDecl()->getName(); } //===----------------------------------------------------------------------===// @@ -643,6 +667,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion()); } +/// Look through a chain of LocationContexts to either find the +/// StackFrameContext that matches a DeclContext, or find a VarRegion +/// for a variable captured by a block. +static llvm::PointerUnion<const StackFrameContext *, const VarRegion *> +getStackOrCaptureRegionForDeclContext(const LocationContext *LC, + const DeclContext *DC, + const VarDecl *VD) { + while (LC) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) { + if (cast<DeclContext>(SFC->getDecl()) == DC) + return SFC; + } + if (const BlockInvocationContext *BC = + dyn_cast<BlockInvocationContext>(LC)) { + const BlockDataRegion *BR = + static_cast<const BlockDataRegion*>(BC->getContextData()); + // FIXME: This can be made more efficient. + for (BlockDataRegion::referenced_vars_iterator + I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); I != E; ++I) { + if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion())) + if (VR->getDecl() == VD) + return cast<VarRegion>(I.getCapturedRegion()); + } + } + + LC = LC->getParent(); + } + return (const StackFrameContext*)0; +} + const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = 0; @@ -675,7 +730,13 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); - const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); + llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V = + getStackOrCaptureRegionForDeclContext(LC, DC, D); + + if (V.is<const VarRegion*>()) + return V.get<const VarRegion*>(); + + const StackFrameContext *STC = V.get<const StackFrameContext*>(); if (!STC) sReg = getUnknownRegion(); @@ -800,6 +861,10 @@ const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); } +const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { + return getSubRegion<SymbolicRegion>(Sym, getHeapRegion()); +} + const FieldRegion* MemRegionManager::getFieldRegion(const FieldDecl *d, const MemRegion* superRegion){ @@ -823,6 +888,37 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E, const CXXBaseObjectRegion * MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl, const MemRegion *superRegion) { + // Check that the base class is actually a direct base of this region. + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(superRegion)) { + if (const CXXRecordDecl *Class = TVR->getValueType()->getAsCXXRecordDecl()){ + if (Class->isVirtuallyDerivedFrom(decl)) { + // Virtual base regions should not be layered, since the layout rules + // are different. + while (const CXXBaseObjectRegion *Base = + dyn_cast<CXXBaseObjectRegion>(superRegion)) { + superRegion = Base->getSuperRegion(); + } + assert(superRegion && !isa<MemSpaceRegion>(superRegion)); + + } else { + // Non-virtual bases should always be direct bases. +#ifndef NDEBUG + bool FoundBase = false; + for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), + E = Class->bases_end(); + I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl() == decl) { + FoundBase = true; + break; + } + } + + assert(FoundBase && "Not a direct base class of this region"); +#endif + } + } + } + return getSubRegion<CXXBaseObjectRegion>(decl, superRegion); } @@ -898,24 +994,26 @@ const MemRegion *MemRegion::getBaseRegion() const { // View handling. //===----------------------------------------------------------------------===// -const MemRegion *MemRegion::StripCasts() const { +const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { const MemRegion *R = this; while (true) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // FIXME: generalize. Essentially we want to strip away ElementRegions - // that were layered on a symbolic region because of casts. We only - // want to strip away ElementRegions, however, where the index is 0. - SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) { - if (CI->getValue().getSExtValue() == 0) { - R = ER->getSuperRegion(); - continue; - } - } + switch (R->getKind()) { + case ElementRegionKind: { + const ElementRegion *ER = cast<ElementRegion>(R); + if (!ER->getIndex().isZeroConstant()) + return R; + R = ER->getSuperRegion(); + break; + } + case CXXBaseObjectRegionKind: + if (!StripBaseCasts) + return R; + R = cast<CXXBaseObjectRegion>(R)->getSuperRegion(); + break; + default: + return R; } - break; } - return R; } // FIXME: Merge with the implementation of the same method in Store.cpp @@ -973,12 +1071,14 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { RegionOffset MemRegion::getAsOffset() const { const MemRegion *R = this; + const MemRegion *SymbolicOffsetBase = 0; int64_t Offset = 0; while (1) { switch (R->getKind()) { default: - return RegionOffset(0); + return RegionOffset(R, RegionOffset::Symbolic); + case SymbolicRegionKind: case AllocaRegionKind: case CompoundLiteralRegionKind: @@ -987,31 +1087,95 @@ RegionOffset MemRegion::getAsOffset() const { case VarRegionKind: case CXXTempObjectRegionKind: goto Finish; + + case ObjCIvarRegionKind: + // This is a little strange, but it's a compromise between + // ObjCIvarRegions having unknown compile-time offsets (when using the + // non-fragile runtime) and yet still being distinct, non-overlapping + // regions. Thus we treat them as "like" base regions for the purposes + // of computing offsets. + goto Finish; + + case CXXBaseObjectRegionKind: { + const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R); + R = BOR->getSuperRegion(); + + QualType Ty; + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) { + Ty = TVR->getDesugaredValueType(getContext()); + } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + // If our base region is symbolic, we don't know what type it really is. + // Pretend the type of the symbol is the true dynamic type. + // (This will at least be self-consistent for the life of the symbol.) + Ty = SR->getSymbol()->getType(getContext())->getPointeeType(); + } + + const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); + if (!Child) { + // We cannot compute the offset of the base class. + SymbolicOffsetBase = R; + } + + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); + + CharUnits BaseOffset; + const CXXRecordDecl *Base = BOR->getDecl(); + if (Child->isVirtuallyDerivedFrom(Base)) + BaseOffset = Layout.getVBaseClassOffset(Base); + else + BaseOffset = Layout.getBaseClassOffset(Base); + + // The base offset is in chars, not in bits. + Offset += BaseOffset.getQuantity() * getContext().getCharWidth(); + break; + } case ElementRegionKind: { const ElementRegion *ER = cast<ElementRegion>(R); - QualType EleTy = ER->getValueType(); + R = ER->getSuperRegion(); - if (!IsCompleteType(getContext(), EleTy)) - return RegionOffset(0); + QualType EleTy = ER->getValueType(); + if (!IsCompleteType(getContext(), EleTy)) { + // We cannot compute the offset of the base class. + SymbolicOffsetBase = R; + continue; + } SVal Index = ER->getIndex(); if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) { + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + int64_t i = CI->getValue().getSExtValue(); - CharUnits Size = getContext().getTypeSizeInChars(EleTy); - Offset += i * Size.getQuantity() * 8; + // This type size is in bits. + Offset += i * getContext().getTypeSize(EleTy); } else { // We cannot compute offset for non-concrete index. - return RegionOffset(0); + SymbolicOffsetBase = R; } - R = ER->getSuperRegion(); break; } case FieldRegionKind: { const FieldRegion *FR = cast<FieldRegion>(R); + R = FR->getSuperRegion(); + const RecordDecl *RD = FR->getDecl()->getParent(); - if (!RD->isCompleteDefinition()) + if (!RD->isCompleteDefinition()) { // We cannot compute offset for incomplete type. - return RegionOffset(0); + SymbolicOffsetBase = R; + } + + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + // Get the field number. unsigned idx = 0; for (RecordDecl::field_iterator FI = RD->field_begin(), @@ -1022,13 +1186,14 @@ RegionOffset MemRegion::getAsOffset() const { const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); // This is offset in bits. Offset += Layout.getFieldOffset(idx); - R = FR->getSuperRegion(); break; } } } Finish: + if (SymbolicOffsetBase) + return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic); return RegionOffset(R, Offset); } @@ -1056,26 +1221,37 @@ void BlockDataRegion::LazyInitializeReferencedVars() { typedef BumpVector<const MemRegion*> VarVec; VarVec *BV = (VarVec*) A.Allocate<VarVec>(); new (BV) VarVec(BC, E - I); + VarVec *BVOriginal = (VarVec*) A.Allocate<VarVec>(); + new (BVOriginal) VarVec(BC, E - I); for ( ; I != E; ++I) { const VarDecl *VD = *I; const VarRegion *VR = 0; + const VarRegion *OriginalVR = 0; - if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) + if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) { VR = MemMgr.getVarRegion(VD, this); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } else { - if (LC) + if (LC) { VR = MemMgr.getVarRegion(VD, LC); + OriginalVR = VR; + } else { VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + OriginalVR = MemMgr.getVarRegion(VD, LC); } } assert(VR); + assert(OriginalVR); BV->push_back(VR, BC); + BVOriginal->push_back(OriginalVR, BC); } ReferencedVars = BV; + OriginalVars = BVOriginal; } BlockDataRegion::referenced_vars_iterator @@ -1085,8 +1261,14 @@ BlockDataRegion::referenced_vars_begin() const { BumpVector<const MemRegion*> *Vec = static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->begin()); + if (Vec == (void*) 0x1) + return BlockDataRegion::referenced_vars_iterator(0, 0); + + BumpVector<const MemRegion*> *VecOriginal = + static_cast<BumpVector<const MemRegion*>*>(OriginalVars); + + return BlockDataRegion::referenced_vars_iterator(Vec->begin(), + VecOriginal->begin()); } BlockDataRegion::referenced_vars_iterator @@ -1096,6 +1278,12 @@ BlockDataRegion::referenced_vars_end() const { BumpVector<const MemRegion*> *Vec = static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->end()); + if (Vec == (void*) 0x1) + return BlockDataRegion::referenced_vars_iterator(0, 0); + + BumpVector<const MemRegion*> *VecOriginal = + static_cast<BumpVector<const MemRegion*>*>(OriginalVars); + + return BlockDataRegion::referenced_vars_iterator(Vec->end(), + VecOriginal->end()); } diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp deleted file mode 100644 index 65cdcd9..0000000 --- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ /dev/null @@ -1,90 +0,0 @@ -//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- 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 ObjCMessage which serves as a common wrapper for ObjC -// message expressions or implicit messages for loading/storing ObjC properties. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace ento; - -QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { - QualType resultTy; - bool isLVal = false; - - if (isObjCMessage()) { - resultTy = Msg.getResultType(ctx); - } else if (const CXXConstructExpr *Ctor = - CallE.dyn_cast<const CXXConstructExpr *>()) { - resultTy = Ctor->getType(); - } else { - const CallExpr *FunctionCall = CallE.get<const CallExpr *>(); - - isLVal = FunctionCall->isLValue(); - const Expr *Callee = FunctionCall->getCallee(); - if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl()) - resultTy = FD->getResultType(); - else - resultTy = FunctionCall->getType(); - } - - if (isLVal) - resultTy = ctx.getPointerType(resultTy); - - return resultTy; -} - -SVal CallOrObjCMessage::getFunctionCallee() const { - assert(isFunctionCall()); - assert(!isCXXCall()); - const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens(); - return State->getSVal(Fun, LCtx); -} - -SVal CallOrObjCMessage::getCXXCallee() const { - assert(isCXXCall()); - const CallExpr *ActualCall = CallE.get<const CallExpr *>(); - const Expr *callee = - cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument(); - - // FIXME: Will eventually need to cope with member pointers. This is - // a limitation in getImplicitObjectArgument(). - if (!callee) - return UnknownVal(); - - return State->getSVal(callee, LCtx); -} - -SVal -CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const { - assert(isObjCMessage()); - return Msg.getInstanceReceiverSVal(State, LC); -} - -const Decl *CallOrObjCMessage::getDecl() const { - if (isCXXCall()) { - const CXXMemberCallExpr *CE = - cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>()); - assert(CE); - return CE->getMethodDecl(); - } else if (isObjCMessage()) { - return Msg.getMethodDecl(); - } else if (isFunctionCall()) { - // In case of a C style call, use the path sensitive information to find - // the function declaration. - SVal CalleeVal = getFunctionCallee(); - return CalleeVal.getAsFunctionDecl(); - } - return 0; -} - diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 01dd965..7d52aac 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtCXX.h" @@ -58,6 +59,48 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} PathPieces::~PathPieces() {} + +void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, + bool ShouldFlattenMacros) const { + for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { + PathDiagnosticPiece *Piece = I->getPtr(); + + switch (Piece->getKind()) { + case PathDiagnosticPiece::Call: { + PathDiagnosticCallPiece *Call = cast<PathDiagnosticCallPiece>(Piece); + IntrusiveRefCntPtr<PathDiagnosticEventPiece> CallEnter = + Call->getCallEnterEvent(); + if (CallEnter) + Current.push_back(CallEnter); + Call->path.flattenTo(Primary, Primary, ShouldFlattenMacros); + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = + Call->getCallExitEvent(); + if (callExit) + Current.push_back(callExit); + break; + } + case PathDiagnosticPiece::Macro: { + PathDiagnosticMacroPiece *Macro = cast<PathDiagnosticMacroPiece>(Piece); + if (ShouldFlattenMacros) { + Macro->subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); + } else { + Current.push_back(Piece); + PathPieces NewPath; + Macro->subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); + // FIXME: This probably shouldn't mutate the original path piece. + Macro->subPieces = NewPath; + } + break; + } + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::ControlFlow: + Current.push_back(Piece); + break; + } + } +} + + PathDiagnostic::~PathDiagnostic() {} PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, @@ -147,23 +190,14 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. + // Note, the enclosing routine is called in deterministic order, so the + // results will be consistent between runs (no reason to break ties if the + // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); - - if (orig_size <= new_size) { - bool shouldKeepOriginal = true; - if (orig_size == new_size) { - // Here we break ties in a fairly arbitrary, but deterministic, way. - llvm::FoldingSetNodeID fullProfile, fullProfileOrig; - D->FullProfile(fullProfile); - orig->FullProfile(fullProfileOrig); - if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) - shouldKeepOriginal = false; - } + if (orig_size <= new_size) + return; - if (shouldKeepOriginal) - return; - } Diags.RemoveNode(orig); delete orig; } @@ -242,30 +276,86 @@ PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { //===----------------------------------------------------------------------===// static SourceLocation getValidSourceLocation(const Stmt* S, - LocationOrAnalysisDeclContext LAC) { - SourceLocation L = S->getLocStart(); + LocationOrAnalysisDeclContext LAC, + bool UseEnd = false) { + SourceLocation L = UseEnd ? S->getLocEnd() : S->getLocStart(); assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " "be passed to PathDiagnosticLocation upon creation."); // S might be a temporary statement that does not have a location in the - // source code, so find an enclosing statement and use it's location. + // source code, so find an enclosing statement and use its location. if (!L.isValid()) { - ParentMap *PM = 0; + AnalysisDeclContext *ADC; if (LAC.is<const LocationContext*>()) - PM = &LAC.get<const LocationContext*>()->getParentMap(); + ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); else - PM = &LAC.get<AnalysisDeclContext*>()->getParentMap(); + ADC = LAC.get<AnalysisDeclContext*>(); + + ParentMap &PM = ADC->getParentMap(); + + const Stmt *Parent = S; + do { + Parent = PM.getParent(Parent); + + // In rare cases, we have implicit top-level expressions, + // such as arguments for implicit member initializers. + // In this case, fall back to the start of the body (even if we were + // asked for the statement end location). + if (!Parent) { + const Stmt *Body = ADC->getBody(); + if (Body) + L = Body->getLocStart(); + else + L = ADC->getDecl()->getLocEnd(); + break; + } - while (!L.isValid()) { - S = PM->getParent(S); - L = S->getLocStart(); - } + L = UseEnd ? Parent->getLocEnd() : Parent->getLocStart(); + } while (!L.isValid()); } return L; } +static PathDiagnosticLocation +getLocationForCaller(const StackFrameContext *SFC, + const LocationContext *CallerCtx, + const SourceManager &SM) { + const CFGBlock &Block = *SFC->getCallSiteBlock(); + CFGElement Source = Block[SFC->getIndex()]; + + switch (Source.getKind()) { + case CFGElement::Invalid: + llvm_unreachable("Invalid CFGElement"); + case CFGElement::Statement: + return PathDiagnosticLocation(cast<CFGStmt>(Source).getStmt(), + SM, CallerCtx); + case CFGElement::Initializer: { + const CFGInitializer &Init = cast<CFGInitializer>(Source); + return PathDiagnosticLocation(Init.getInitializer()->getInit(), + SM, CallerCtx); + } + case CFGElement::AutomaticObjectDtor: { + const CFGAutomaticObjDtor &Dtor = cast<CFGAutomaticObjDtor>(Source); + return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), + SM, CallerCtx); + } + case CFGElement::BaseDtor: + case CFGElement::MemberDtor: { + const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); + if (const Stmt *CallerBody = CallerInfo->getBody()) + return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); + return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); + } + case CFGElement::TemporaryDtor: + llvm_unreachable("not yet implemented!"); + } + + llvm_unreachable("Unknown CFGElement kind"); +} + + PathDiagnosticLocation PathDiagnosticLocation::createBegin(const Decl *D, const SourceManager &SM) { @@ -280,6 +370,17 @@ PathDiagnosticLocation SM, SingleLocK); } + +PathDiagnosticLocation +PathDiagnosticLocation::createEnd(const Stmt *S, + const SourceManager &SM, + LocationOrAnalysisDeclContext LAC) { + if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) + return createEndBrace(CS, SM); + return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), + SM, SingleLocK); +} + PathDiagnosticLocation PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, const SourceManager &SM) { @@ -339,6 +440,19 @@ PathDiagnosticLocation else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { S = PS->getStmt(); } + else if (const PostImplicitCall *PIE = dyn_cast<PostImplicitCall>(&P)) { + return PathDiagnosticLocation(PIE->getLocation(), SMng); + } + else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + return getLocationForCaller(CE->getCalleeContext(), + CE->getLocationContext(), + SMng); + } + else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) { + return getLocationForCaller(CEE->getCalleeContext(), + CEE->getLocationContext(), + SMng); + } return PathDiagnosticLocation(S, SMng, P.getLocationContext()); } @@ -495,25 +609,14 @@ PathDiagnosticLocation PathDiagnostic::getLocation() const { // Manipulation of PathDiagnosticCallPieces. //===----------------------------------------------------------------------===// -static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N, - const SourceManager &SM) { - while (N) { - ProgramPoint PP = N->getLocation(); - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) - return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext()); - if (N->pred_empty()) - break; - N = *N->pred_begin(); - } - return PathDiagnosticLocation(); -} - PathDiagnosticCallPiece * PathDiagnosticCallPiece::construct(const ExplodedNode *N, - const CallExit &CE, + const CallExitEnd &CE, const SourceManager &SM) { - const Decl *caller = CE.getLocationContext()->getParent()->getDecl(); - PathDiagnosticLocation pos = getLastStmtLoc(N, SM); + const Decl *caller = CE.getLocationContext()->getDecl(); + PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), + CE.getLocationContext(), + SM); return new PathDiagnosticCallPiece(caller, pos); } @@ -528,11 +631,11 @@ PathDiagnosticCallPiece::construct(PathPieces &path, void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, const SourceManager &SM) { - const Decl *D = CE.getCalleeContext()->getDecl(); - Callee = D; - callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM, - CE.getLocationContext()); - callEnterWithin = PathDiagnosticLocation::createBegin(D, SM); + const StackFrameContext *CalleeCtx = CE.getCalleeContext(); + Callee = CalleeCtx->getDecl(); + + callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); + callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); } IntrusiveRefCntPtr<PathDiagnosticEventPiece> @@ -667,16 +770,15 @@ StackHintGenerator::~StackHintGenerator() {} std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ ProgramPoint P = N->getLocation(); - const CallExit *CExit = dyn_cast<CallExit>(&P); - assert(CExit && "Stack Hints should be constructed at CallExit points."); + const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P); + assert(CExit && "Stack Hints should be constructed at CallExitEnd points."); - const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt()); + // FIXME: Use CallEvent to abstract this over all calls. + const Stmt *CallSite = CExit->getCalleeContext()->getCallSite(); + const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite); if (!CE) return ""; - // Get the successor node to make sure the return statement is evaluated and - // CE is set to the result value. - N = *N->succ_begin(); if (!N) return getMessageForSymbolNotFound(); diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 323cede..58a4bba 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -31,7 +31,6 @@ namespace { const std::string OutputFile; const LangOptions &LangOpts; OwningPtr<PathDiagnosticConsumer> SubPD; - bool flushed; const bool SupportsCrossFileDiagnostics; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, @@ -61,7 +60,7 @@ PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, bool supportsMultipleFiles, PathDiagnosticConsumer *subPD) - : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false), + : OutputFile(output), LangOpts(LO), SubPD(subPD), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} PathDiagnosticConsumer* @@ -183,10 +182,18 @@ static void ReportControlFlow(raw_ostream &o, I!=E; ++I) { Indent(o, indent) << "<dict>\n"; ++indent; + + // Make the ranges of the start and end point self-consistent with adjacent edges + // by forcing to use only the beginning of the range. This simplifies the layout + // logic for clients. Indent(o, indent) << "<key>start</key>\n"; - EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1); + SourceLocation StartEdge = I->getStart().asRange().getBegin(); + EmitRange(o, SM, LangOpts, SourceRange(StartEdge, StartEdge), FM, indent+1); + Indent(o, indent) << "<key>end</key>\n"; - EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1); + SourceLocation EndEdge = I->getEnd().asRange().getBegin(); + EmitRange(o, SM, LangOpts, SourceRange(EndEdge, EndEdge), FM, indent+1); + --indent; Indent(o, indent) << "</dict>\n"; } @@ -382,6 +389,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl( if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { + IntrusiveRefCntPtr<PathDiagnosticEventPiece> + callEnterWithin = call->getCallEnterWithinCallerEvent(); + if (callEnterWithin) + AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation()); + WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = @@ -476,6 +488,17 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } + + // Output the bug hash for issue unique-ing. Currently, it's just an + // offset from the beginning of the function. + if (const Stmt *Body = DeclWithIssue->getBody()) { + FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()), + *SM); + FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM); + o << " <key>issue_hash</key><integer>" + << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber() + << "</integer>\n"; + } } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b9cfa27..dc988cc 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" @@ -70,6 +71,19 @@ ProgramState::~ProgramState() { stateMgr->getStoreManager().decrementReferenceCount(store); } +ProgramStateManager::ProgramStateManager(ASTContext &Ctx, + StoreManagerCreator CreateSMgr, + ConstraintManagerCreator CreateCMgr, + llvm::BumpPtrAllocator &alloc, + SubEngine &SubEng) + : Eng(&SubEng), EnvMgr(alloc), GDMFactory(alloc), + svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), + CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) { + StoreMgr.reset((*CreateSMgr)(*this)); + ConstraintMgr.reset((*CreateCMgr)(*this, SubEng)); +} + + ProgramStateManager::~ProgramStateManager() { for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); I!=E; ++I) @@ -157,7 +171,7 @@ ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, StoreManager::InvalidatedSymbols *IS, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { if (!IS) { StoreManager::InvalidatedSymbols invalidated; return invalidateRegionsImpl(Regions, E, Count, LCtx, @@ -171,7 +185,7 @@ ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, StoreManager::InvalidatedSymbols &IS, - const CallOrObjCMessage *Call) const { + const CallEvent *Call) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); @@ -203,11 +217,11 @@ ProgramStateRef ProgramState::unbindLoc(Loc LV) const { } ProgramStateRef -ProgramState::enterStackFrame(const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) const { - const StoreRef &new_store = - getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx); - return makeWithStore(new_store); +ProgramState::enterStackFrame(const CallEvent &Call, + const StackFrameContext *CalleeCtx) const { + const StoreRef &NewStore = + getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx); + return makeWithStore(NewStore); } SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { @@ -485,8 +499,6 @@ ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) return getPersistentState(NewState); } -void ScanReachableSymbols::anchor() { } - bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) if (!scan(*I)) @@ -530,6 +542,9 @@ bool ScanReachableSymbols::scan(SVal val) { if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val)) return scan(X->getRegion()); + if (nonloc::LazyCompoundVal *X = dyn_cast<nonloc::LazyCompoundVal>(&val)) + return scan(X->getRegion()); + if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val)) return scan(X->getLoc()); @@ -564,20 +579,30 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { return false; // If this is a subregion, also visit the parent regions. - if (const SubRegion *SR = dyn_cast<SubRegion>(R)) - if (!scan(SR->getSuperRegion())) + if (const SubRegion *SR = dyn_cast<SubRegion>(R)) { + const MemRegion *Super = SR->getSuperRegion(); + if (!scan(Super)) return false; - // Now look at the binding to this region (if any). - if (!scan(state->getSValAsScalarOrLoc(R))) - return false; + // When we reach the topmost region, scan all symbols in it. + if (isa<MemSpaceRegion>(Super)) { + StoreManager &StoreMgr = state->getStateManager().getStoreManager(); + if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this)) + return false; + } + } - // Now look at the subregions. - if (!SRM.get()) - SRM.reset(state->getStateManager().getStoreManager(). - getSubRegionMap(state->getStore())); + // Regions captured by a block are also implicitly reachable. + if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + for ( ; I != E; ++I) { + if (!scan(I.getCapturedRegion())) + return false; + } + } - return SRM->iterSubRegions(R, *this); + return true; } bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { @@ -707,3 +732,39 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { return Tainted; } + +/// The GDM component containing the dynamic type info. This is a map from a +/// symbol to it's most likely type. +namespace clang { +namespace ento { +typedef llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo> DynamicTypeMap; +template<> struct ProgramStateTrait<DynamicTypeMap> + : public ProgramStatePartialTrait<DynamicTypeMap> { + static void *GDMIndex() { static int index; return &index; } +}; +}} + +DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { + // Look up the dynamic type in the GDM. + const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg); + if (GDMType) + return *GDMType; + + // Otherwise, fall back to what we know about the region. + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(Reg)) + return DynamicTypeInfo(TR->getValueType()); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { + SymbolRef Sym = SR->getSymbol(); + return DynamicTypeInfo(Sym->getType(getStateManager().getContext())); + } + + return DynamicTypeInfo(); +} + +ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg, + DynamicTypeInfo NewTy) const { + ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy); + assert(NewState); + return NewState; +} diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 98eb958..550404a 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/Debug.h" @@ -143,6 +144,92 @@ private: } } + const llvm::APSInt &getMinValue() const { + assert(!isEmpty()); + return ranges.begin()->From(); + } + + bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const { + // This function has nine cases, the cartesian product of range-testing + // both the upper and lower bounds against the symbol's type. + // Each case requires a different pinning operation. + // The function returns false if the described range is entirely outside + // the range of values for the associated symbol. + APSIntType Type(getMinValue()); + APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower); + APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper); + + switch (LowerTest) { + case APSIntType::RTR_Below: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower < Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range starts below what's possible but ends within it. Pin. + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range spans all possible values for the symbol. Pin. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Within: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps around, but all lower values are not possible. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range may or may not wrap around, but both limits are valid. + Type.apply(Lower); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range starts within what's possible but ends above it. Pin. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Above: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps but is outside the symbol's set of possible values. + return false; + case APSIntType::RTR_Within: + // The range starts above what's possible but ends within it (wrap). + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower < Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + } + + return true; + } + public: // Returns a set containing the values in the receiving set, intersected with // the closed range [Lower, Upper]. Unlike the Range type, this range uses @@ -152,8 +239,10 @@ public: // intersection with the two ranges [Min, Upper] and [Lower, Max], // or, alternatively, /removing/ all integers between Upper and Lower. RangeSet Intersect(BasicValueFactory &BV, Factory &F, - const llvm::APSInt &Lower, - const llvm::APSInt &Upper) const { + llvm::APSInt Lower, llvm::APSInt Upper) const { + if (!pin(Lower, Upper)) + return F.getEmptySet(); + PrimRangeSet newRanges = F.getEmptySet(); PrimRangeSet::iterator i = begin(), e = end(); @@ -166,6 +255,7 @@ public: IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e); IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e); } + return newRanges; } @@ -206,8 +296,8 @@ namespace { class RangeConstraintManager : public SimpleConstraintManager{ RangeSet GetRange(ProgramStateRef state, SymbolRef sym); public: - RangeConstraintManager(SubEngine &subengine) - : SimpleConstraintManager(subengine) {} + RangeConstraintManager(SubEngine &subengine, BasicValueFactory &BVF) + : SimpleConstraintManager(subengine, BVF) {} ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, @@ -252,9 +342,9 @@ private: } // end anonymous namespace -ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&, - SubEngine &subeng) { - return new RangeConstraintManager(subeng); +ConstraintManager * +ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine &Eng) { + return new RangeConstraintManager(Eng, StMgr.getBasicVals()); } const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, @@ -288,8 +378,8 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { // Lazily generate a new RangeSet representing all possible values for the // given symbol type. - QualType T = state->getSymbolManager().getType(sym); - BasicValueFactory& BV = state->getBasicVals(); + BasicValueFactory &BV = getBasicVals(); + QualType T = sym->getType(BV.getContext()); return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T)); } @@ -306,117 +396,154 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { // UINT_MAX, 0, 1, and 2. ProgramStateRef -RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - llvm::APSInt Lower = Int-Adjustment; +RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + return St; + + llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment; llvm::APSInt Upper = Lower; --Lower; ++Upper; // [Int-Adjustment+1, Int-Adjustment-1] // Notice that the lower bound is greater than the upper bound. - RangeSet New = GetRange(state, sym).Intersect(BV, F, Upper, Lower); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { +RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + return NULL; + // [Int-Adjustment, Int-Adjustment] - BasicValueFactory &BV = state->getBasicVals(); - llvm::APSInt AdjInt = Int-Adjustment; - RangeSet New = GetRange(state, sym).Intersect(BV, F, AdjInt, AdjInt); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment; + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Min = BV.getMinValue(T); +RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return NULL; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return St; + } // Special case for Int == Min. This is always false. - if (Int == Min) + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (ComparisonVal == Min) return NULL; llvm::APSInt Lower = Min-Adjustment; - llvm::APSInt Upper = Int-Adjustment; + llvm::APSInt Upper = ComparisonVal-Adjustment; --Upper; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Max = BV.getMaxValue(T); +RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return St; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return NULL; + } // Special case for Int == Max. This is always false. - if (Int == Max) + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) return NULL; - llvm::APSInt Lower = Int-Adjustment; + llvm::APSInt Lower = ComparisonVal-Adjustment; llvm::APSInt Upper = Max-Adjustment; ++Lower; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Min = BV.getMinValue(T); +RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return St; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return NULL; + } // Special case for Int == Min. This is always feasible. - if (Int == Min) - return state; - - const llvm::APSInt &Max = BV.getMaxValue(T); + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (ComparisonVal == Min) + return St; - llvm::APSInt Lower = Int-Adjustment; + llvm::APSInt Max = AdjustmentType.getMaxValue(); + llvm::APSInt Lower = ComparisonVal-Adjustment; llvm::APSInt Upper = Max-Adjustment; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } ProgramStateRef -RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) { - BasicValueFactory &BV = state->getBasicVals(); - - QualType T = state->getSymbolManager().getType(sym); - const llvm::APSInt &Max = BV.getMaxValue(T); +RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int)) { + case APSIntType::RTR_Below: + return NULL; + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return St; + } // Special case for Int == Max. This is always feasible. - if (Int == Max) - return state; - - const llvm::APSInt &Min = BV.getMinValue(T); + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) + return St; + llvm::APSInt Min = AdjustmentType.getMinValue(); llvm::APSInt Lower = Min-Adjustment; - llvm::APSInt Upper = Int-Adjustment; + llvm::APSInt Upper = ComparisonVal-Adjustment; - RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); - return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); + RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New); } //===------------------------------------------------------------------------=== diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index cc3ea8c3..bc4e4bb 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -17,10 +17,11 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/CXXInheritance.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -40,23 +41,49 @@ using llvm::Optional; namespace { class BindingKey { public: - enum Kind { Direct = 0x0, Default = 0x1 }; + enum Kind { Default = 0x0, Direct = 0x1 }; private: - llvm ::PointerIntPair<const MemRegion*, 1> P; - uint64_t Offset; + enum { Symbolic = 0x2 }; + llvm::PointerIntPair<const MemRegion *, 2> P; + uint64_t Data; + + explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k) + : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) { + assert(r && Base && "Must have known regions."); + assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); + } explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) - : P(r, (unsigned) k), Offset(offset) {} + : P(r, k), Data(offset) { + assert(r && "Must have known regions."); + assert(getOffset() == offset && "Failed to store offset"); + assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r)) && "Not a base"); + } public: - bool isDirect() const { return P.getInt() == Direct; } + bool isDirect() const { return P.getInt() & Direct; } + bool hasSymbolicOffset() const { return P.getInt() & Symbolic; } const MemRegion *getRegion() const { return P.getPointer(); } - uint64_t getOffset() const { return Offset; } + uint64_t getOffset() const { + assert(!hasSymbolicOffset()); + return Data; + } + + const MemRegion *getConcreteOffsetRegion() const { + assert(hasSymbolicOffset()); + return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data)); + } + + const MemRegion *getBaseRegion() const { + if (hasSymbolicOffset()) + return getConcreteOffsetRegion()->getBaseRegion(); + return getRegion()->getBaseRegion(); + } void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddPointer(P.getOpaqueValue()); - ID.AddInteger(Offset); + ID.AddInteger(Data); } static BindingKey Make(const MemRegion *R, Kind k); @@ -66,48 +93,48 @@ public: return true; if (P.getOpaqueValue() > X.P.getOpaqueValue()) return false; - return Offset < X.Offset; + return Data < X.Data; } bool operator==(const BindingKey &X) const { return P.getOpaqueValue() == X.P.getOpaqueValue() && - Offset == X.Offset; + Data == X.Data; } - bool isValid() const { - return getRegion() != NULL; - } + LLVM_ATTRIBUTE_USED void dump() const; }; } // end anonymous namespace BindingKey BindingKey::Make(const MemRegion *R, Kind k) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const RegionRawOffset &O = ER->getAsArrayOffset(); - - // FIXME: There are some ElementRegions for which we cannot compute - // raw offsets yet, including regions with symbolic offsets. These will be - // ignored by the store. - return BindingKey(O.getRegion(), O.getOffset().getQuantity(), k); - } + const RegionOffset &RO = R->getAsOffset(); + if (RO.hasSymbolicOffset()) + return BindingKey(R, RO.getRegion(), k); - return BindingKey(R, 0, k); + return BindingKey(RO.getRegion(), RO.getOffset(), k); } namespace llvm { static inline raw_ostream &operator<<(raw_ostream &os, BindingKey K) { - os << '(' << K.getRegion() << ',' << K.getOffset() - << ',' << (K.isDirect() ? "direct" : "default") + os << '(' << K.getRegion(); + if (!K.hasSymbolicOffset()) + os << ',' << K.getOffset(); + os << ',' << (K.isDirect() ? "direct" : "default") << ')'; return os; } } // end llvm namespace +void BindingKey::dump() const { + llvm::errs() << *this; +} + //===----------------------------------------------------------------------===// // Actual Store type. //===----------------------------------------------------------------------===// -typedef llvm::ImmutableMap<BindingKey, SVal> RegionBindings; +typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; +typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> RegionBindings; //===----------------------------------------------------------------------===// // Fine-grained control of RegionStoreManager. @@ -138,75 +165,15 @@ public: namespace { -class RegionStoreSubRegionMap : public SubRegionMap { -public: - typedef llvm::ImmutableSet<const MemRegion*> Set; - typedef llvm::DenseMap<const MemRegion*, Set> Map; -private: - Set::Factory F; - Map M; -public: - bool add(const MemRegion* Parent, const MemRegion* SubRegion) { - Map::iterator I = M.find(Parent); - - if (I == M.end()) { - M.insert(std::make_pair(Parent, F.add(F.getEmptySet(), SubRegion))); - return true; - } - - I->second = F.add(I->second, SubRegion); - return false; - } - - void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R); - - ~RegionStoreSubRegionMap() {} - - const Set *getSubRegions(const MemRegion *Parent) const { - Map::const_iterator I = M.find(Parent); - return I == M.end() ? NULL : &I->second; - } - - bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { - Map::const_iterator I = M.find(Parent); - - if (I == M.end()) - return true; - - Set S = I->second; - for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) { - if (!V.Visit(Parent, *SI)) - return false; - } - - return true; - } -}; - -void -RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL, - const SubRegion *R) { - const MemRegion *superR = R->getSuperRegion(); - if (add(superR, R)) - if (const SubRegion *sr = dyn_cast<SubRegion>(superR)) - WL.push_back(sr); -} - class RegionStoreManager : public StoreManager { const RegionStoreFeatures Features; RegionBindings::Factory RBFactory; + ClusterBindings::Factory CBFactory; public: RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) - : StoreManager(mgr), - Features(f), - RBFactory(mgr.getAllocator()) {} - - SubRegionMap *getSubRegionMap(Store store) { - return getRegionStoreSubRegionMap(store); - } - - RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); + : StoreManager(mgr), Features(f), + RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {} Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R); /// getDefaultBinding - Returns an SVal* representing an optional default @@ -257,13 +224,15 @@ public: const Expr *E, unsigned Count, const LocationContext *LCtx, InvalidatedSymbols &IS, - const CallOrObjCMessage *Call, + const CallEvent *Call, InvalidatedRegions *Invalidated); + bool scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Callbacks); + public: // Made public for helper classes. - void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, - RegionStoreSubRegionMap &M); + RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R); RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V); @@ -282,6 +251,8 @@ public: // Made public for helper classes. BindingKey::Default); } + RegionBindings removeCluster(RegionBindings B, const MemRegion *R); + public: // Part of public interface to class. StoreRef Bind(Store store, Loc LV, SVal V); @@ -307,10 +278,14 @@ public: // Part of public interface to class. /// BindStruct - Bind a compound value to a structure. StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V); + /// BindVector - Bind a compound value to a vector. + StoreRef BindVector(Store store, const TypedValueRegion* R, SVal V); + StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V); - /// KillStruct - Set the entire struct to unknown. - StoreRef KillStruct(Store store, const TypedRegion* R, SVal DefaultVal); + /// Clears out all bindings in the given region and assigns a new value + /// as a Default binding. + StoreRef BindAggregate(Store store, const TypedRegion *R, SVal DefaultVal); StoreRef Remove(Store store, Loc LV); @@ -377,10 +352,8 @@ public: // Part of public interface to class. /// Get the state and region whose binding this region R corresponds to. std::pair<Store, const MemRegion*> GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion); - - StoreRef CopyLazyBindings(nonloc::LazyCompoundVal V, Store store, - const TypedRegion *R); + const MemRegion *originalRegion, + bool includeSuffix = false); //===------------------------------------------------------------------===// // State pruning. @@ -390,11 +363,7 @@ public: // Part of public interface to class. /// It returns a new Store with these values removed. StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper); - - StoreRef enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx); - + //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// @@ -416,14 +385,18 @@ public: // Part of public interface to class. void iterBindings(Store store, BindingsHandler& f) { RegionBindings B = GetRegionBindings(store); - for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - const BindingKey &K = I.getKey(); - if (!K.isDirect()) - continue; - if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) { - // FIXME: Possibly incorporate the offset? - if (!f.HandleBinding(*this, store, R, I.getData())) - return; + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + const BindingKey &K = CI.getKey(); + if (!K.isDirect()) + continue; + if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) { + // FIXME: Possibly incorporate the offset? + if (!f.HandleBinding(*this, store, R, CI.getData())) + return; + } } } } @@ -448,28 +421,6 @@ ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { } -RegionStoreSubRegionMap* -RegionStoreManager::getRegionStoreSubRegionMap(Store store) { - RegionBindings B = GetRegionBindings(store); - RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); - - SmallVector<const SubRegion*, 10> WL; - - for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) - if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) - M->process(WL, R); - - // We also need to record in the subregion map "intermediate" regions that - // don't have direct bindings but are super regions of those that do. - while (!WL.empty()) { - const SubRegion *R = WL.back(); - WL.pop_back(); - M->process(WL, R); - } - - return M; -} - //===----------------------------------------------------------------------===// // Region Cluster analysis. //===----------------------------------------------------------------------===// @@ -478,14 +429,11 @@ namespace { template <typename DERIVED> class ClusterAnalysis { protected: - typedef BumpVector<BindingKey> RegionCluster; - typedef llvm::DenseMap<const MemRegion *, RegionCluster *> ClusterMap; - llvm::DenseMap<const RegionCluster*, unsigned> Visited; - typedef SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10> - WorkList; - - BumpVectorContext BVC; - ClusterMap ClusterM; + typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap; + typedef SmallVector<const MemRegion *, 10> WorkList; + + llvm::SmallPtrSet<const ClusterBindings *, 16> Visited; + WorkList WL; RegionStoreManager &RM; @@ -496,6 +444,10 @@ protected: const bool includeGlobals; + const ClusterBindings *getCluster(const MemRegion *R) { + return B.lookup(R); + } + public: ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, RegionBindings b, const bool includeGlobals) @@ -505,59 +457,36 @@ public: RegionBindings getRegionBindings() const { return B; } - RegionCluster &AddToCluster(BindingKey K) { - const MemRegion *R = K.getRegion(); - const MemRegion *baseR = R->getBaseRegion(); - RegionCluster &C = getCluster(baseR); - C.push_back(K, BVC); - static_cast<DERIVED*>(this)->VisitAddedToCluster(baseR, C); - return C; - } - bool isVisited(const MemRegion *R) { - return (bool) Visited[&getCluster(R->getBaseRegion())]; - } - - RegionCluster& getCluster(const MemRegion *R) { - RegionCluster *&CRef = ClusterM[R]; - if (!CRef) { - void *Mem = BVC.getAllocator().template Allocate<RegionCluster>(); - CRef = new (Mem) RegionCluster(BVC, 10); - } - return *CRef; + return Visited.count(getCluster(R)); } void GenerateClusters() { - // Scan the entire set of bindings and make the region clusters. + // Scan the entire set of bindings and record the region clusters. for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - RegionCluster &C = AddToCluster(RI.getKey()); - if (const MemRegion *R = RI.getData().getAsRegion()) { - // Generate a cluster, but don't add the region to the cluster - // if there aren't any bindings. - getCluster(R->getBaseRegion()); - } - if (includeGlobals) { - const MemRegion *R = RI.getKey().getRegion(); - if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace())) - AddToWorkList(R, C); - } + const MemRegion *Base = RI.getKey(); + + const ClusterBindings &Cluster = RI.getData(); + assert(!Cluster.isEmpty() && "Empty clusters should be removed"); + static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster); + + if (includeGlobals) + if (isa<NonStaticGlobalSpaceRegion>(Base->getMemorySpace())) + AddToWorkList(Base, &Cluster); } } - bool AddToWorkList(const MemRegion *R, RegionCluster &C) { - if (unsigned &visited = Visited[&C]) - return false; - else - visited = 1; + bool AddToWorkList(const MemRegion *R, const ClusterBindings *C) { + if (C) { + if (Visited.count(C)) + return false; + Visited.insert(C); + } - WL.push_back(std::make_pair(R, &C)); + WL.push_back(R); return true; } - bool AddToWorkList(BindingKey K) { - return AddToWorkList(K.getRegion()); - } - bool AddToWorkList(const MemRegion *R) { const MemRegion *baseR = R->getBaseRegion(); return AddToWorkList(baseR, getCluster(baseR)); @@ -565,22 +494,20 @@ public: void RunWorkList() { while (!WL.empty()) { - const MemRegion *baseR; - RegionCluster *C; - llvm::tie(baseR, C) = WL.back(); - WL.pop_back(); + const MemRegion *baseR = WL.pop_back_val(); - // First visit the cluster. - static_cast<DERIVED*>(this)->VisitCluster(baseR, C->begin(), C->end()); + // First visit the cluster. + if (const ClusterBindings *Cluster = getCluster(baseR)) + static_cast<DERIVED*>(this)->VisitCluster(baseR, *Cluster); - // Next, visit the base region. + // Next, visit the base region. static_cast<DERIVED*>(this)->VisitBaseRegion(baseR); } } public: - void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C) {} - void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E) {} + void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {} + void VisitCluster(const MemRegion *baseR, const ClusterBindings &C) {} void VisitBaseRegion(const MemRegion *baseR) {} }; } @@ -589,16 +516,99 @@ public: // Binding invalidation. //===----------------------------------------------------------------------===// -void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, - const MemRegion *R, - RegionStoreSubRegionMap &M) { +bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Callbacks) { + assert(R == R->getBaseRegion() && "Should only be called for base regions"); + RegionBindings B = GetRegionBindings(S); + const ClusterBindings *Cluster = B.lookup(R); + + if (!Cluster) + return true; - if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R)) - for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end(); - I != E; ++I) - RemoveSubRegionBindings(B, *I, M); + for (ClusterBindings::iterator RI = Cluster->begin(), RE = Cluster->end(); + RI != RE; ++RI) { + if (!Callbacks.scan(RI.getData())) + return false; + } - B = removeBinding(B, R); + return true; +} + +RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B, + const SubRegion *R) { + BindingKey SRKey = BindingKey::Make(R, BindingKey::Default); + const MemRegion *ClusterHead = SRKey.getBaseRegion(); + if (R == ClusterHead) { + // We can remove an entire cluster's bindings all in one go. + return RBFactory.remove(B, R); + } + + if (SRKey.hasSymbolicOffset()) { + const SubRegion *Base = cast<SubRegion>(SRKey.getConcreteOffsetRegion()); + B = removeSubRegionBindings(B, Base); + return addBinding(B, Base, BindingKey::Default, UnknownVal()); + } + + // This assumes the region being invalidated is char-aligned. This isn't + // true for bitfields, but since bitfields have no subregions they shouldn't + // be using this function anyway. + uint64_t Length = UINT64_MAX; + + SVal Extent = R->getExtent(svalBuilder); + if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) { + const llvm::APSInt &ExtentInt = ExtentCI->getValue(); + assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); + // Extents are in bytes but region offsets are in bits. Be careful! + Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth(); + } + + const ClusterBindings *Cluster = B.lookup(ClusterHead); + if (!Cluster) + return B; + + ClusterBindings Result = *Cluster; + + // It is safe to iterate over the bindings as they are being changed + // because they are in an ImmutableMap. + for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end(); + I != E; ++I) { + BindingKey NextKey = I.getKey(); + if (NextKey.getRegion() == SRKey.getRegion()) { + if (NextKey.getOffset() > SRKey.getOffset() && + NextKey.getOffset() - SRKey.getOffset() < Length) { + // Case 1: The next binding is inside the region we're invalidating. + // Remove it. + Result = CBFactory.remove(Result, NextKey); + } else if (NextKey.getOffset() == SRKey.getOffset()) { + // Case 2: The next binding is at the same offset as the region we're + // invalidating. In this case, we need to leave default bindings alone, + // since they may be providing a default value for a regions beyond what + // we're invalidating. + // FIXME: This is probably incorrect; consider invalidating an outer + // struct whose first field is bound to a LazyCompoundVal. + if (NextKey.isDirect()) + Result = CBFactory.remove(Result, NextKey); + } + } else if (NextKey.hasSymbolicOffset()) { + const MemRegion *Base = NextKey.getConcreteOffsetRegion(); + if (R->isSubRegionOf(Base)) { + // Case 3: The next key is symbolic and we just changed something within + // its concrete region. We don't know if the binding is still valid, so + // we'll be conservative and remove it. + if (NextKey.isDirect()) + Result = CBFactory.remove(Result, NextKey); + } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) { + // Case 4: The next key is symbolic, but we changed a known + // super-region. In this case the binding is certainly no longer valid. + if (R == Base || BaseSR->isSubRegionOf(R)) + Result = CBFactory.remove(Result, NextKey); + } + } + } + + if (Result.isEmpty()) + return RBFactory.remove(B, ClusterHead); + return RBFactory.add(B, ClusterHead, Result); } namespace { @@ -621,7 +631,7 @@ public: : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {} - void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); + void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); void VisitBaseRegion(const MemRegion *baseR); private: @@ -646,26 +656,31 @@ void invalidateRegionsWorker::VisitBinding(SVal V) { const MemRegion *LazyR = LCS->getRegion(); RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + // FIXME: This should not have to walk all bindings in the old store. for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion()); - if (baseR && baseR->isSubRegionOf(LazyR)) - VisitBinding(RI.getData()); + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + BindingKey K = CI.getKey(); + if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { + if (BaseR == LazyR) + VisitBinding(CI.getData()); + else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) + VisitBinding(CI.getData()); + } + } } return; } } -void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, - BindingKey *I, BindingKey *E) { - for ( ; I != E; ++I) { - // Get the old binding. Is it a region? If so, add it to the worklist. - const BindingKey &K = *I; - if (const SVal *V = RM.lookup(B, K)) - VisitBinding(*V); +void invalidateRegionsWorker::VisitCluster(const MemRegion *BaseR, + const ClusterBindings &C) { + for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) + VisitBinding(I.getData()); - B = RM.removeBinding(B, K); - } + B = RM.removeCluster(B, BaseR); } void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { @@ -681,8 +696,22 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { BI != BE; ++BI) { const VarRegion *VR = *BI; const VarDecl *VD = VR->getDecl(); - if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) + if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) { AddToWorkList(VR); + } + else if (Loc::isLocType(VR->getValueType())) { + // Map the current bindings to a Store to retrieve the value + // of the binding. If that binding itself is a region, we should + // invalidate that region. This is because a block may capture + // a pointer value, but the thing pointed by that pointer may + // get invalidated. + Store store = B.getRootWithoutRetain(); + SVal V = RM.getBinding(store, loc::MemRegionVal(VR)); + if (const Loc *L = dyn_cast<Loc>(&V)) { + if (const MemRegion *LR = L->getAsRegion()) + AddToWorkList(LR); + } + } } return; } @@ -771,7 +800,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, const Expr *Ex, unsigned Count, const LocationContext *LCtx, InvalidatedSymbols &IS, - const CallOrObjCMessage *Call, + const CallEvent *Call, InvalidatedRegions *Invalidated) { invalidateRegionsWorker W(*this, StateMgr, RegionStoreManager::GetRegionBindings(store), @@ -868,10 +897,22 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx)); } +// This mirrors Type::getCXXRecordDeclForPointerType(), but there doesn't +// appear to be another need for this in the rest of the codebase. +static const CXXRecordDecl *GetCXXRecordDeclForReferenceType(QualType Ty) { + if (const ReferenceType *RT = Ty->getAs<ReferenceType>()) + if (const RecordType *RCT = RT->getPointeeType()->getAs<RecordType>()) + return dyn_cast<CXXRecordDecl>(RCT->getDecl()); + return 0; +} + SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) { const CXXRecordDecl *baseDecl; + if (baseType->isPointerType()) baseDecl = baseType->getCXXRecordDeclForPointerType(); + else if (baseType->isReferenceType()) + baseDecl = GetCXXRecordDeclForReferenceType(baseType); else baseDecl = baseType->getAsCXXRecordDecl(); @@ -894,7 +935,7 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType, loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base); if (!baseRegVal) return UnknownVal(); - const MemRegion *BaseRegion = baseRegVal->stripCasts(); + const MemRegion *BaseRegion = baseRegVal->stripCasts(/*StripBases=*/false); // Assume the derived class is a pointer or a reference to a CXX record. derivedType = derivedType->getPointeeType(); @@ -917,23 +958,20 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType, if (SRDecl == DerivedDecl) return loc::MemRegionVal(TSR); - // If the region type is a subclass of the derived type. - if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) { - // This occurs in two cases. - // 1) We are processing an upcast. - // 2) We are processing a downcast but we jumped directly from the - // ancestor to a child of the cast value, so conjure the - // appropriate region to represent value (the intermediate node). - return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl, - BaseRegion)); - } - - // If super region is not a parent of derived class, the cast definitely - // fails. - if (!derivedType->isVoidType() && - DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) { - Failed = true; - return UnknownVal(); + if (!derivedType->isVoidType()) { + // Static upcasts are marked as DerivedToBase casts by Sema, so this will + // only happen when multiple or virtual inheritance is involved. + CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (SRDecl->isDerivedFrom(DerivedDecl, Paths)) { + SVal Result = loc::MemRegionVal(TSR); + const CXXBasePath &Path = *Paths.begin(); + for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end(); + I != E; ++I) { + Result = evalDerivedToBase(Result, I->Base->getType()); + } + return Result; + } } if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR)) @@ -1036,8 +1074,12 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { if (RTy->isUnionType()) return UnknownVal(); - if (RTy->isArrayType()) - return getBindingForArray(store, R); + if (RTy->isArrayType()) { + if (RTy->isConstantArrayType()) + return getBindingForArray(store, R); + else + return UnknownVal(); + } // FIXME: handle Vector types. if (RTy->isVectorType()) @@ -1099,7 +1141,8 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { std::pair<Store, const MemRegion *> RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion) { + const MemRegion *originalRegion, + bool includeSuffix) { if (originalRegion != R) { if (Optional<SVal> OV = getDefaultBinding(B, R)) { @@ -1121,9 +1164,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, const std::pair<Store, const MemRegion *> &X = GetLazyBinding(B, FR->getSuperRegion(), originalRegion); - if (X.second) - return std::make_pair(X.first, - MRMgr.getFieldRegionWithSuper(FR, X.second)); + if (X.second) { + if (includeSuffix) + return std::make_pair(X.first, + MRMgr.getFieldRegionWithSuper(FR, X.second)); + return X; + } + } // C++ base object region is another kind of region that we should blast // through to look for lazy compound value. It is like a field region. @@ -1132,9 +1179,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, const std::pair<Store, const MemRegion *> &X = GetLazyBinding(B, baseReg->getSuperRegion(), originalRegion); - if (X.second) - return std::make_pair(X.first, - MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, X.second)); + if (X.second) { + if (includeSuffix) + return std::make_pair(X.first, + MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, + X.second)); + return X; + } } // The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is @@ -1143,7 +1194,11 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, } SVal RegionStoreManager::getBindingForElement(Store store, - const ElementRegion* R) { + const ElementRegion* R) { + // We do not currently model bindings of the CompoundLiteralregion. + if (isa<CompoundLiteralRegion>(R->getBaseRegion())) + return UnknownVal(); + // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); if (const Optional<SVal> &V = getDirectBinding(B, R)) @@ -1274,7 +1329,16 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, // At this point we have already checked in either getBindingForElement or // getBindingForField if 'R' has a direct binding. RegionBindings B = GetRegionBindings(store); + + // Lazy binding? + Store lazyBindingStore = NULL; + const MemRegion *lazyBindingRegion = NULL; + llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R, + true); + if (lazyBindingRegion) + return getLazyBinding(lazyBindingRegion, lazyBindingStore); + // Record whether or not we see a symbolic index. That can completely // be out of scope of our lookup. bool hasSymbolicIndex = false; @@ -1299,14 +1363,6 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, break; } - // Lazy binding? - Store lazyBindingStore = NULL; - const MemRegion *lazyBindingRegion = NULL; - llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R); - - if (lazyBindingRegion) - return getLazyBinding(lazyBindingRegion, lazyBindingStore); - if (R->hasStackNonParametersStorage()) { if (isa<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check @@ -1410,15 +1466,49 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { return svalBuilder.getRegionValueSymbolVal(R); } +static bool mayHaveLazyBinding(QualType Ty) { + return Ty->isArrayType() || Ty->isStructureOrClassType(); +} + SVal RegionStoreManager::getBindingForStruct(Store store, const TypedValueRegion* R) { - assert(R->getValueType()->isStructureOrClassType()); + const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl(); + if (RD->field_empty()) + return UnknownVal(); + + // If we already have a lazy binding, don't create a new one, + // unless the first field might have a lazy binding of its own. + // (Right now we can't tell the difference.) + QualType FirstFieldType = RD->field_begin()->getType(); + if (!mayHaveLazyBinding(FirstFieldType)) { + RegionBindings B = GetRegionBindings(store); + BindingKey K = BindingKey::Make(R, BindingKey::Default); + if (const nonloc::LazyCompoundVal *V = + dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { + return *V; + } + } + return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } -SVal RegionStoreManager::getBindingForArray(Store store, +SVal RegionStoreManager::getBindingForArray(Store store, const TypedValueRegion * R) { - assert(Ctx.getAsConstantArrayType(R->getValueType())); + const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType()); + assert(Ty && "Only constant array types can have compound bindings."); + + // If we already have a lazy binding, don't create a new one, + // unless the first element might have a lazy binding of its own. + // (Right now we can't tell the difference.) + if (!mayHaveLazyBinding(Ty->getElementType())) { + RegionBindings B = GetRegionBindings(store); + BindingKey K = BindingKey::Make(R, BindingKey::Default); + if (const nonloc::LazyCompoundVal *V = + dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { + return *V; + } + } + return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } @@ -1426,16 +1516,23 @@ bool RegionStoreManager::includedInBindings(Store store, const MemRegion *region) const { RegionBindings B = GetRegionBindings(store); region = region->getBaseRegion(); - - for (RegionBindings::iterator it = B.begin(), ei = B.end(); it != ei; ++it) { - const BindingKey &K = it.getKey(); - if (region == K.getRegion()) - return true; - const SVal &D = it.getData(); - if (const MemRegion *r = D.getAsRegion()) - if (r == region) - return true; + + // Quick path: if the base is the head of a cluster, the region is live. + if (B.lookup(region)) + return true; + + // Slow path: if the region is the VALUE of any binding, it is live. + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + const SVal &D = CI.getData(); + if (const MemRegion *R = D.getAsRegion()) + if (R->getBaseRegion() == region) + return true; + } } + return false; } @@ -1461,24 +1558,15 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); // Check if the region is a struct region. - if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) - if (TR->getValueType()->isStructureOrClassType()) + if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) { + QualType Ty = TR->getValueType(); + if (Ty->isStructureOrClassType()) return BindStruct(store, TR, V); - - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - if (ER->getIndex().isZeroConstant()) { - if (const TypedValueRegion *superR = - dyn_cast<TypedValueRegion>(ER->getSuperRegion())) { - QualType superTy = superR->getValueType(); - // For now, just invalidate the fields of the struct/union/class. - // This is for test rdar_test_7185607 in misc-ps-region-store.m. - // FIXME: Precisely handle the fields of the record. - if (superTy->isStructureOrClassType()) - return KillStruct(store, superR, UnknownVal()); - } - } + if (Ty->isVectorType()) + return BindVector(store, TR, V); } - else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { // Binding directly to a symbolic region should be treated as binding // to element 0. QualType T = SR->getSymbol()->getType(Ctx); @@ -1492,10 +1580,13 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { R = GetElementZeroRegion(SR, T); } + // Clear out bindings that may overlap with this binding. + // Perform the binding. RegionBindings B = GetRegionBindings(store); - return StoreRef(addBinding(B, R, BindingKey::Direct, - V).getRootWithoutRetain(), *this); + B = removeSubRegionBindings(B, cast<SubRegion>(R)); + BindingKey Key = BindingKey::Make(R, BindingKey::Direct); + return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this); } StoreRef RegionStoreManager::BindDecl(Store store, const VarRegion *VR, @@ -1566,12 +1657,12 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, nonloc::LazyCompoundVal LCV = cast<nonloc::LazyCompoundVal>(svalBuilder. makeLazyCompoundVal(StoreRef(store, *this), S)); - return CopyLazyBindings(LCV, store, R); + return BindAggregate(store, R, LCV); } // Handle lazy compound values. - if (nonloc::LazyCompoundVal *LCV = dyn_cast<nonloc::LazyCompoundVal>(&Init)) - return CopyLazyBindings(*LCV, store, R); + if (isa<nonloc::LazyCompoundVal>(Init)) + return BindAggregate(store, R, Init); // Remaining case: explicit compound values. @@ -1607,6 +1698,46 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, return newStore; } +StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R, + SVal V) { + QualType T = R->getValueType(); + assert(T->isVectorType()); + const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. + + // Handle lazy compound values and symbolic values. + if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) + return BindAggregate(store, R, V); + + // We may get non-CompoundVal accidentally due to imprecise cast logic or + // that we are binding symbolic struct value. Kill the field values, and if + // the value is symbolic go and bind it as a "default" binding. + if (!isa<nonloc::CompoundVal>(V)) { + return BindAggregate(store, R, UnknownVal()); + } + + QualType ElemType = VT->getElementType(); + nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + unsigned index = 0, numElements = VT->getNumElements(); + StoreRef newStore(store, *this); + + for ( ; index != numElements ; ++index) { + if (VI == VE) + break; + + NonLoc Idx = svalBuilder.makeArrayIndex(index); + const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); + + if (ElemType->isArrayType()) + newStore = BindArray(newStore.getStore(), ER, *VI); + else if (ElemType->isStructureOrClassType()) + newStore = BindStruct(newStore.getStore(), ER, *VI); + else + newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI); + } + return newStore; +} + StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, SVal V) { @@ -1622,17 +1753,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, if (!RD->isCompleteDefinition()) return StoreRef(store, *this); - // Handle lazy compound values. - if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V)) - return CopyLazyBindings(*LCV, store, R); + // Handle lazy compound values and symbolic values. + if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) + return BindAggregate(store, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. - if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) { - SVal SV = isa<nonloc::SymbolVal>(V) ? V : UnknownVal(); - return KillStruct(store, R, SV); - } + if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) + return BindAggregate(store, R, UnknownVal()); nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); @@ -1646,10 +1775,10 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, break; // Skip any unnamed bitfields to stay in sync with the initializers. - if ((*FI)->isUnnamedBitfield()) + if (FI->isUnnamedBitfield()) continue; - QualType FTy = (*FI)->getType(); + QualType FTy = FI->getType(); const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); if (FTy->isArrayType()) @@ -1671,58 +1800,16 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, return newStore; } -StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R, - SVal DefaultVal) { - BindingKey key = BindingKey::Make(R, BindingKey::Default); - - // The BindingKey may be "invalid" if we cannot handle the region binding - // explicitly. One example is something like array[index], where index - // is a symbolic value. In such cases, we want to invalidate the entire - // array, as the index assignment could have been to any element. In - // the case of nested symbolic indices, we need to march up the region - // hierarchy untile we reach a region whose binding we can reason about. - const SubRegion *subReg = R; - - while (!key.isValid()) { - if (const SubRegion *tmp = dyn_cast<SubRegion>(subReg->getSuperRegion())) { - subReg = tmp; - key = BindingKey::Make(tmp, BindingKey::Default); - } - else - break; - } - - // Remove the old bindings, using 'subReg' as the root of all regions - // we will invalidate. +StoreRef RegionStoreManager::BindAggregate(Store store, const TypedRegion *R, + SVal Val) { + // Remove the old bindings, using 'R' as the root of all regions + // we will invalidate. Then add the new binding. RegionBindings B = GetRegionBindings(store); - OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(store)); - RemoveSubRegionBindings(B, subReg, *SubRegions); - // Set the default value of the struct region to "unknown". - if (!key.isValid()) - return StoreRef(B.getRootWithoutRetain(), *this); - - return StoreRef(addBinding(B, key, DefaultVal).getRootWithoutRetain(), *this); -} - -StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, - Store store, - const TypedRegion *R) { - - // Nuke the old bindings stemming from R. - RegionBindings B = GetRegionBindings(store); + B = removeSubRegionBindings(B, R); + B = addBinding(B, R, BindingKey::Default, Val); - OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(store)); - - // B and DVM are updated after the call to RemoveSubRegionBindings. - RemoveSubRegionBindings(B, R, *SubRegions.get()); - - // Now copy the bindings. This amounts to just binding 'V' to 'R'. This - // results in a zero-copy algorithm. - return StoreRef(addBinding(B, R, BindingKey::Default, - V).getRootWithoutRetain(), *this); + return StoreRef(B.getRootWithoutRetain(), *this); } //===----------------------------------------------------------------------===// @@ -1732,9 +1819,14 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K, SVal V) { - if (!K.isValid()) - return B; - return RBFactory.add(B, K, V); + const MemRegion *Base = K.getBaseRegion(); + + const ClusterBindings *ExistingCluster = B.lookup(Base); + ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster + : CBFactory.getEmptyMap()); + + ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); + return RBFactory.add(B, Base, NewCluster); } RegionBindings RegionStoreManager::addBinding(RegionBindings B, @@ -1744,9 +1836,11 @@ RegionBindings RegionStoreManager::addBinding(RegionBindings B, } const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) { - if (!K.isValid()) - return NULL; - return B.lookup(K); + const ClusterBindings *Cluster = B.lookup(K.getBaseRegion()); + if (!Cluster) + return 0; + + return Cluster->lookup(K); } const SVal *RegionStoreManager::lookup(RegionBindings B, @@ -1757,9 +1851,15 @@ const SVal *RegionStoreManager::lookup(RegionBindings B, RegionBindings RegionStoreManager::removeBinding(RegionBindings B, BindingKey K) { - if (!K.isValid()) + const MemRegion *Base = K.getBaseRegion(); + const ClusterBindings *Cluster = B.lookup(Base); + if (!Cluster) return B; - return RBFactory.remove(B, K); + + ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); + if (NewCluster.isEmpty()) + return RBFactory.remove(B, Base); + return RBFactory.add(B, Base, NewCluster); } RegionBindings RegionStoreManager::removeBinding(RegionBindings B, @@ -1768,6 +1868,11 @@ RegionBindings RegionStoreManager::removeBinding(RegionBindings B, return removeBinding(B, BindingKey::Make(R, k)); } +RegionBindings RegionStoreManager::removeCluster(RegionBindings B, + const MemRegion *Base) { + return RBFactory.remove(B, Base); +} + //===----------------------------------------------------------------------===// // State pruning. //===----------------------------------------------------------------------===// @@ -1789,8 +1894,8 @@ public: SymReaper(symReaper), CurrentLCtx(LCtx) {} // Called by ClusterAnalysis. - void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C); - void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); + void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C); + void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); void VisitBindingKey(BindingKey K); bool UpdatePostponed(); @@ -1799,18 +1904,18 @@ public: } void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, - RegionCluster &C) { + const ClusterBindings &C) { if (const VarRegion *VR = dyn_cast<VarRegion>(baseR)) { if (SymReaper.isLive(VR)) - AddToWorkList(baseR, C); + AddToWorkList(baseR, &C); return; } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) { if (SymReaper.isLive(SR->getSymbol())) - AddToWorkList(SR, C); + AddToWorkList(SR, &C); else Postponed.push_back(SR); @@ -1818,7 +1923,7 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } if (isa<NonStaticGlobalSpaceRegion>(baseR)) { - AddToWorkList(baseR, C); + AddToWorkList(baseR, &C); return; } @@ -1828,34 +1933,57 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); const StackFrameContext *RegCtx = StackReg->getStackFrame(); if (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx)) - AddToWorkList(TR, C); + AddToWorkList(TR, &C); } } void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR, - BindingKey *I, BindingKey *E) { - for ( ; I != E; ++I) - VisitBindingKey(*I); + const ClusterBindings &C) { + for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) { + VisitBindingKey(I.getKey()); + VisitBinding(I.getData()); + } } void removeDeadBindingsWorker::VisitBinding(SVal V) { // Is it a LazyCompoundVal? All referenced regions are live as well. if (const nonloc::LazyCompoundVal *LCS = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + dyn_cast<nonloc::LazyCompoundVal>(&V)) { const MemRegion *LazyR = LCS->getRegion(); RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + + // FIXME: This should not have to walk all bindings in the old store. for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion()); - if (baseR && baseR->isSubRegionOf(LazyR)) - VisitBinding(RI.getData()); + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + BindingKey K = CI.getKey(); + if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { + if (BaseR == LazyR) + VisitBinding(CI.getData()); + else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) + VisitBinding(CI.getData()); + } + } } + return; } // If V is a region, then add it to the worklist. - if (const MemRegion *R = V.getAsRegion()) + if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); + + // All regions captured by a block are also live. + if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); + for ( ; I != E; ++I) + AddToWorkList(I.getCapturedRegion()); + } + } + // Update the set of live symbols. for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); @@ -1874,26 +2002,7 @@ void removeDeadBindingsWorker::VisitBindingKey(BindingKey K) { // should continue to track that symbol. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) SymReaper.markLive(SymR->getSymbol()); - - // For BlockDataRegions, enqueue the VarRegions for variables marked - // with __block (passed-by-reference). - // via BlockDeclRefExprs. - if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) { - for (BlockDataRegion::referenced_vars_iterator - RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); - RI != RE; ++RI) { - if ((*RI)->getDecl()->getAttr<BlocksAttr>()) - AddToWorkList(*RI); - } - - // No possible data bindings on a BlockDataRegion. - return; - } } - - // Visit the data binding for K. - if (const SVal *V = RM.lookup(B, K)) - VisitBinding(*V); } bool removeDeadBindingsWorker::UpdatePostponed() { @@ -1933,68 +2042,32 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, // as live. We now remove all the regions that are dead from the store // as well as update DSymbols with the set symbols that are now dead. for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const BindingKey &K = I.getKey(); + const MemRegion *Base = I.getKey(); // If the cluster has been visited, we know the region has been marked. - if (W.isVisited(K.getRegion())) + if (W.isVisited(Base)) continue; // Remove the dead entry. - B = removeBinding(B, K); + B = removeCluster(B, Base); - // Mark all non-live symbols that this binding references as dead. - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(K.getRegion())) + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base)) SymReaper.maybeDead(SymR->getSymbol()); - SVal X = I.getData(); - SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); + // Mark all non-live symbols that this binding references as dead. + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + SVal X = CI.getData(); + SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) + SymReaper.maybeDead(*SI); + } } return StoreRef(B.getRootWithoutRetain(), *this); } - -StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) -{ - FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl()); - FunctionDecl::param_const_iterator PI = FD->param_begin(), - PE = FD->param_end(); - StoreRef store = StoreRef(state->getStore(), *this); - - if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) { - CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. We check that - // PI != PE because the actual number of arguments may be different than - // the function declaration. - for (; AI != AE && PI != PE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - } else if (const CXXConstructExpr *CE = - dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) { - CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(), - AE = CE->arg_end(); - - // Copy the arg expression value to the arg variables. - for (; AI != AE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI, callerCtx); - store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), - ArgVal); - } - } else - assert(isa<CXXDestructorDecl>(calleeCtx->getDecl())); - - return store; -} - //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// @@ -2002,8 +2075,16 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, void RegionStoreManager::print(Store store, raw_ostream &OS, const char* nl, const char *sep) { RegionBindings B = GetRegionBindings(store); - OS << "Store (direct and default bindings):" << nl; + OS << "Store (direct and default bindings), " + << (void*) B.getRootWithoutRetain() + << " :" << nl; - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - OS << ' ' << I.getKey() << " : " << I.getData() << nl; + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; + } + OS << nl; + } } diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 9e97f5e..d1936cd 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclCXX.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" @@ -61,7 +62,6 @@ NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs, NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type) { assert(lhs && rhs); - assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true); assert(!Loc::isLocType(type)); return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); } @@ -149,6 +149,18 @@ SValBuilder::getConjuredSymbolVal(const Stmt *stmt, return nonloc::SymbolVal(sym); } +DefinedOrUnknownSVal +SValBuilder::getConjuredHeapSymbolVal(const Expr *E, + const LocationContext *LCtx, + unsigned VisitCount) { + QualType T = E->getType(); + assert(Loc::isLocType(T)); + assert(SymbolManager::canSymbolicate(T)); + + SymbolRef sym = SymMgr.getConjuredSymbol(E, LCtx, T, VisitCount); + return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym)); +} + DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag, const MemRegion *region, const Expr *expr, QualType type, @@ -193,31 +205,48 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, return loc::MemRegionVal(BD); } +/// Return a memory region for the 'this' object reference. +loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D, + const StackFrameContext *SFC) { + return loc::MemRegionVal(getRegionManager(). + getCXXThisRegion(D->getThisType(getContext()), SFC)); +} + +/// Return a memory region for the 'this' object reference. +loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D, + const StackFrameContext *SFC) { + const Type *T = D->getTypeForDecl(); + QualType PT = getContext().getPointerType(QualType(T, 0)); + return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC)); +} + //===----------------------------------------------------------------------===// -SVal SValBuilder::makeGenericVal(ProgramStateRef State, - BinaryOperator::Opcode Op, - NonLoc LHS, NonLoc RHS, - QualType ResultTy) { - // If operands are tainted, create a symbol to ensure that we propagate taint. - if (State->isTainted(RHS) || State->isTainted(LHS)) { - const SymExpr *symLHS; - const SymExpr *symRHS; - - if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) { - symLHS = LHS.getAsSymExpr(); +SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, + BinaryOperator::Opcode Op, + NonLoc LHS, NonLoc RHS, + QualType ResultTy) { + if (!State->isTainted(RHS) && !State->isTainted(LHS)) + return UnknownVal(); + + const SymExpr *symLHS = LHS.getAsSymExpr(); + const SymExpr *symRHS = RHS.getAsSymExpr(); + // TODO: When the Max Complexity is reached, we should conjure a symbol + // instead of generating an Unknown value and propagate the taint info to it. + const unsigned MaxComp = 10000; // 100000 28X + + if (symLHS && symRHS && + (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) + return makeNonLoc(symLHS, Op, symRHS, ResultTy); + + if (symLHS && symLHS->computeComplexity() < MaxComp) + if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); - } - if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) { - symRHS = RHS.getAsSymExpr(); + if (symRHS && symRHS->computeComplexity() < MaxComp) + if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); - } - symLHS = LHS.getAsSymExpr(); - symRHS = RHS.getAsSymExpr(); - return makeNonLoc(symLHS, Op, symRHS, ResultTy); - } return UnknownVal(); } @@ -324,7 +353,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Are we casting from an array to a pointer? If so just pass on // the decayed value. - if (castTy->isPointerType()) + if (castTy->isPointerType() || castTy->isReferenceType()) return val; // Are we casting from an array to an integer? If so, cast the decayed @@ -340,9 +369,12 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Check for casts from a region to a specific type. if (const MemRegion *R = val.getAsRegion()) { + // Handle other casts of locations to integers. + if (castTy->isIntegerType()) + return evalCastFromLoc(loc::MemRegionVal(R), castTy); + // FIXME: We should handle the case where we strip off view layers to get // to a desugared type. - if (!Loc::isLocType(castTy)) { // FIXME: There can be gross cases where one casts the result of a function // (that returns a pointer) to some other value that happens to fit diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index b94aff4..8437f50 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -133,9 +133,9 @@ const MemRegion *SVal::getAsRegion() const { return 0; } -const MemRegion *loc::MemRegionVal::stripCasts() const { +const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const { const MemRegion *R = getRegion(); - return R ? R->StripCasts() : NULL; + return R ? R->StripCasts(StripBaseCasts) : NULL; } const void *nonloc::LazyCompoundVal::getStore() const { @@ -309,22 +309,6 @@ void Loc::dumpToStream(raw_ostream &os) const { case loc::MemRegionKind: os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString(); break; - case loc::ObjCPropRefKind: { - const ObjCPropertyRefExpr *E = cast<loc::ObjCPropRef>(this)->getPropRefExpr(); - os << "objc-prop{"; - if (E->isSuperReceiver()) - os << "super."; - else if (E->getBase()) - os << "<base>."; - - if (E->isImplicitProperty()) - os << E->getImplicitPropertyGetter()->getSelector().getAsString(); - else - os << E->getExplicitProperty()->getName(); - - os << "}"; - break; - } default: llvm_unreachable("Pretty-printing not implemented for this Loc."); } diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index a76a2da..5568f1c 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" @@ -71,9 +72,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond, ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, Loc Cond, bool Assumption) { - - BasicValueFactory &BasicVals = state->getBasicVals(); - switch (Cond.getSubKind()) { default: assert (false && "'Assume' not implemented for this Loc."); @@ -88,7 +86,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, while (SubR) { // FIXME: now we only find the first symbolic region. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR)) { - const llvm::APSInt &zero = BasicVals.getZeroWithPtrWidth(); + const llvm::APSInt &zero = getBasicVals().getZeroWithPtrWidth(); if (Assumption) return assumeSymNE(state, SymR->getSymbol(), zero, zero); else @@ -134,12 +132,17 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { } -ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol( - ProgramStateRef State, - SymbolRef Sym, - bool Assumption) { - QualType T = State->getSymbolManager().getType(Sym); - const llvm::APSInt &zero = State->getBasicVals().getValue(0, T); +ProgramStateRef +SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, + SymbolRef Sym, bool Assumption) { + BasicValueFactory &BVF = getBasicVals(); + QualType T = Sym->getType(BVF.getContext()); + + // None of the constraint solvers currently support non-integer types. + if (!T->isIntegerType()) + return State; + + const llvm::APSInt &zero = BVF.getValue(0, T); if (Assumption) return assumeSymNE(State, Sym, zero, zero); else @@ -158,8 +161,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return assumeAuxForSymbol(state, sym, Assumption); } - BasicValueFactory &BasicVals = state->getBasicVals(); - SymbolManager &SymMgr = state->getSymbolManager(); + BasicValueFactory &BasicVals = getBasicVals(); switch (Cond.getSubKind()) { default: @@ -184,7 +186,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, BinaryOperator::Opcode op = SE->getOpcode(); // Implicitly compare non-comparison expressions to 0. if (!BinaryOperator::isComparisonOp(op)) { - QualType T = SymMgr.getType(SE); + QualType T = SE->getType(BasicVals.getContext()); const llvm::APSInt &zero = BasicVals.getValue(0, T); op = (Assumption ? BO_NE : BO_EQ); return assumeSymRel(state, SE, op, zero); @@ -209,33 +211,20 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, } // end switch } -static llvm::APSInt computeAdjustment(const SymExpr *LHS, - SymbolRef &Sym) { - llvm::APSInt DefaultAdjustment; - DefaultAdjustment = 0; - - // First check if the LHS is a simple symbol reference. - if (isa<SymbolData>(LHS)) - return DefaultAdjustment; - - // Next, see if it's a "($sym+constant1)" expression. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS); - - // We cannot simplify "($sym1+$sym2)". - if (!SE) - return DefaultAdjustment; - - // Get the constant out of the expression "($sym+constant1)" or - // "<expr>+constant1". - Sym = SE->getLHS(); - switch (SE->getOpcode()) { - case BO_Add: - return SE->getRHS(); - case BO_Sub: - return -SE->getRHS(); - default: - // We cannot simplify non-additive operators. - return DefaultAdjustment; +static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) { + // Is it a "($sym+constant1)" expression? + if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { + BinaryOperator::Opcode Op = SE->getOpcode(); + if (Op == BO_Add || Op == BO_Sub) { + Sym = SE->getLHS(); + Adjustment = APSIntType(Adjustment).convert(SE->getRHS()); + + // Don't forget to negate the adjustment if it's being subtracted. + // This should happen /after/ promotion, in case the value being + // subtracted is, say, CHAR_MIN, and the promoted type is 'int'. + if (Op == BO_Sub) + Adjustment = -Adjustment; + } } } @@ -246,6 +235,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, assert(BinaryOperator::isComparisonOp(op) && "Non-comparison ops should be rewritten as comparisons to zero."); + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + + // Get the type used for calculating wraparound. + APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType(Ctx)); + // We only handle simple comparisons of the form "$sym == constant" // or "($sym+constant1) == constant2". // The adjustment is "constant1" in the above expression. It's used to @@ -254,28 +249,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to // the subclasses of SimpleConstraintManager to handle the adjustment. SymbolRef Sym = LHS; - llvm::APSInt Adjustment = computeAdjustment(LHS, Sym); - - // FIXME: This next section is a hack. It silently converts the integers to - // be of the same type as the symbol, which is not always correct. Really the - // comparisons should be performed using the Int's type, then mapped back to - // the symbol's range of values. - ProgramStateManager &StateMgr = state->getStateManager(); - ASTContext &Ctx = StateMgr.getContext(); - - QualType T = Sym->getType(Ctx); - assert(T->isIntegerType() || Loc::isLocType(T)); - unsigned bitwidth = Ctx.getTypeSize(T); - bool isSymUnsigned - = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T); - - // Convert the adjustment. - Adjustment.setIsUnsigned(isSymUnsigned); - Adjustment = Adjustment.extOrTrunc(bitwidth); - - // Convert the right-hand side integer. - llvm::APSInt ConvertedInt(Int, isSymUnsigned); - ConvertedInt = ConvertedInt.extOrTrunc(bitwidth); + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + computeAdjustment(Sym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); + llvm::APSInt ConvertedInt = ComparisonType.convert(Int); switch (op) { default: diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index e082d9d..088d70c 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -23,8 +23,10 @@ namespace ento { class SimpleConstraintManager : public ConstraintManager { SubEngine &SU; + BasicValueFactory &BVF; public: - SimpleConstraintManager(SubEngine &subengine) : SU(subengine) {} + SimpleConstraintManager(SubEngine &subengine, BasicValueFactory &BV) + : SU(subengine), BVF(BV) {} virtual ~SimpleConstraintManager(); //===------------------------------------------------------------------===// @@ -79,6 +81,8 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// + BasicValueFactory &getBasicVals() const { return BVF; } + bool canReasonAbout(SVal X) const; ProgramStateRef assumeAux(ProgramStateRef state, diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index d0558f1..ad58a07 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" @@ -106,9 +107,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { return UnknownVal(); llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() || - Loc::isLocType(castTy)); - i = i.extOrTrunc(Context.getTypeSize(castTy)); + BasicVals.getAPSIntType(castTy).apply(i); if (isLocType) return makeIntLocVal(i); @@ -139,9 +138,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { return makeLocAsInteger(val, BitWidth); llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() || - Loc::isLocType(castTy)); - i = i.extOrTrunc(BitWidth); + BasicVals.getAPSIntType(castTy).apply(i); return makeIntVal(i); } @@ -272,14 +269,40 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); // If we reach this point, the expression cannot be simplified. - // Make a SymbolVal for the entire expression. - return makeNonLoc(LHS, op, RHS, resultTy); + // Make a SymbolVal for the entire expression, after converting the RHS. + const llvm::APSInt *ConvertedRHS = &RHS; + if (BinaryOperator::isComparisonOp(op)) { + // We're looking for a type big enough to compare the symbolic value + // with the given constant. + // FIXME: This is an approximation of Sema::UsualArithmeticConversions. + ASTContext &Ctx = getContext(); + QualType SymbolType = LHS->getType(Ctx); + uint64_t ValWidth = RHS.getBitWidth(); + uint64_t TypeWidth = Ctx.getTypeSize(SymbolType); + + if (ValWidth < TypeWidth) { + // If the value is too small, extend it. + ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); + } else if (ValWidth == TypeWidth) { + // If the value is signed but the symbol is unsigned, do the comparison + // in unsigned space. [C99 6.3.1.8] + // (For the opposite case, the value is already unsigned.) + if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType()) + ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); + } + } else + ConvertedRHS = &BasicVals.Convert(resultTy, RHS); + + return makeNonLoc(LHS, op, *ConvertedRHS, resultTy); } SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy) { + NonLoc InputLHS = lhs; + NonLoc InputRHS = rhs; + // Handle trivial case where left-side and right-side are the same. if (lhs == rhs) switch (op) { @@ -304,7 +327,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, while (1) { switch (lhs.getSubKind()) { default: - return makeGenericVal(state, op, lhs, rhs, resultTy); + return makeSymExprValNN(state, op, lhs, rhs, resultTy); case nonloc::LocAsIntegerKind: { Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); switch (rhs.getSubKind()) { @@ -315,8 +338,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case nonloc::ConcreteIntKind: { // Transform the integer into a location and compare. llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue(); - i.setIsUnsigned(true); - i = i.extOrTrunc(Context.getTypeSize(Context.VoidPtrTy)); + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } default: @@ -327,86 +349,78 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, return makeTruthVal(true, resultTy); default: // This case also handles pointer arithmetic. - return makeGenericVal(state, op, lhs, rhs, resultTy); + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } } case nonloc::ConcreteIntKind: { - const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); - - // Is the RHS a symbol we can simplify? - // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case. - if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { - SymbolRef RSym = srhs->getSymbol(); - if (RSym->getType(Context)->isIntegerType()) { - if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { - // The symbol evaluates to a constant. - const llvm::APSInt *rhs_I; - if (BinaryOperator::isRelationalOp(op)) - rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant); - else - rhs_I = &BasicVals.Convert(resultTy, *Constant); - - rhs = nonloc::ConcreteInt(*rhs_I); - } + llvm::APSInt LHSValue = cast<nonloc::ConcreteInt>(lhs).getValue(); + + // If we're dealing with two known constants, just perform the operation. + if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { + llvm::APSInt RHSValue = *KnownRHSValue; + if (BinaryOperator::isComparisonOp(op)) { + // We're looking for a type big enough to compare the two values. + // FIXME: This is not correct. char + short will result in a promotion + // to int. Unfortunately we have lost types by this point. + APSIntType CompareType = std::max(APSIntType(LHSValue), + APSIntType(RHSValue)); + CompareType.apply(LHSValue); + CompareType.apply(RHSValue); + } else if (!BinaryOperator::isShiftOp(op)) { + APSIntType IntType = BasicVals.getAPSIntType(resultTy); + IntType.apply(LHSValue); + IntType.apply(RHSValue); } - } - if (isa<nonloc::ConcreteInt>(rhs)) { - return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs)); - } else { - const llvm::APSInt& lhsValue = lhsInt.getValue(); - - // Swap the left and right sides and flip the operator if doing so - // allows us to better reason about the expression (this is a form - // of expression canonicalization). - // While we're at it, catch some special cases for non-commutative ops. - NonLoc tmp = rhs; - rhs = lhs; - lhs = tmp; + const llvm::APSInt *Result = + BasicVals.evalAPSInt(op, LHSValue, RHSValue); + if (!Result) + return UndefinedVal(); - switch (op) { - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - op = ReverseComparison(op); - continue; - case BO_EQ: - case BO_NE: - case BO_Add: - case BO_Mul: - case BO_And: - case BO_Xor: - case BO_Or: - continue; - case BO_Shr: - if (lhsValue.isAllOnesValue() && lhsValue.isSigned()) - // At this point lhs and rhs have been swapped. - return rhs; - // FALL-THROUGH - case BO_Shl: - if (lhsValue == 0) - // At this point lhs and rhs have been swapped. - return rhs; - return makeGenericVal(state, op, rhs, lhs, resultTy); - default: - return makeGenericVal(state, op, rhs, lhs, resultTy); - } + return nonloc::ConcreteInt(*Result); + } + + // Swap the left and right sides and flip the operator if doing so + // allows us to better reason about the expression (this is a form + // of expression canonicalization). + // While we're at it, catch some special cases for non-commutative ops. + switch (op) { + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + op = ReverseComparison(op); + // FALL-THROUGH + case BO_EQ: + case BO_NE: + case BO_Add: + case BO_Mul: + case BO_And: + case BO_Xor: + case BO_Or: + std::swap(lhs, rhs); + continue; + case BO_Shr: + // (~0)>>a + if (LHSValue.isAllOnesValue() && LHSValue.isSigned()) + return evalCastFromNonLoc(lhs, resultTy); + // FALL-THROUGH + case BO_Shl: + // 0<<a and 0>>a + if (LHSValue == 0) + return evalCastFromNonLoc(lhs, resultTy); + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); + default: + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } case nonloc::SymbolValKind: { - nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs); + // We only handle LHS as simple symbols or SymIntExprs. + SymbolRef Sym = cast<nonloc::SymbolVal>(lhs).getSymbol(); // LHS is a symbolic expression. - if (selhs->isExpression()) { - - // Only handle LHS of the form "$sym op constant", at least for now. - const SymIntExpr *symIntExpr = - dyn_cast<SymIntExpr>(selhs->getSymbol()); - - if (!symIntExpr) - return makeGenericVal(state, op, lhs, rhs, resultTy); + if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) { // Is this a logical not? (!x is represented as x == 0.) if (op == BO_EQ && rhs.isZeroConstant()) { @@ -452,95 +466,57 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } // For now, only handle expressions whose RHS is a constant. - const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs); - if (!rhsInt) - return makeGenericVal(state, op, lhs, rhs, resultTy); - - // If both the LHS and the current expression are additive, - // fold their constants. - if (BinaryOperator::isAdditiveOp(op)) { - BinaryOperator::Opcode lop = symIntExpr->getOpcode(); - if (BinaryOperator::isAdditiveOp(lop)) { - // resultTy may not be the best type to convert to, but it's - // probably the best choice in expressions with mixed type - // (such as x+1U+2LL). The rules for implicit conversions should - // choose a reasonable type to preserve the expression, and will - // at least match how the value is going to be used. - const llvm::APSInt &first = - BasicVals.Convert(resultTy, symIntExpr->getRHS()); - const llvm::APSInt &second = - BasicVals.Convert(resultTy, rhsInt->getValue()); - const llvm::APSInt *newRHS; - if (lop == op) - newRHS = BasicVals.evalAPSInt(BO_Add, first, second); - else - newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); - return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); - } - } - - // Otherwise, make a SymbolVal out of the expression. - return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); - - // LHS is a simple symbol (not a symbolic expression). - } else { - nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); - SymbolRef Sym = slhs->getSymbol(); - QualType lhsType = Sym->getType(Context); - - // The conversion type is usually the result type, but not in the case - // of relational expressions. - QualType conversionType = resultTy; - if (BinaryOperator::isRelationalOp(op)) - conversionType = lhsType; - - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (lhsType->isIntegerType()) - if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { - // The symbol evaluates to a constant. If necessary, promote the - // folded constant (LHS) to the result type. - const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, - *Constant); - lhs = nonloc::ConcreteInt(lhs_I); - - // Also promote the RHS (if necessary). - - // For shifts, it is not necessary to promote the RHS. - if (BinaryOperator::isShiftOp(op)) + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) { + // If both the LHS and the current expression are additive, + // fold their constants and try again. + if (BinaryOperator::isAdditiveOp(op)) { + BinaryOperator::Opcode lop = symIntExpr->getOpcode(); + if (BinaryOperator::isAdditiveOp(lop)) { + // Convert the two constants to a common type, then combine them. + + // resultTy may not be the best type to convert to, but it's + // probably the best choice in expressions with mixed type + // (such as x+1U+2LL). The rules for implicit conversions should + // choose a reasonable type to preserve the expression, and will + // at least match how the value is going to be used. + APSIntType IntType = BasicVals.getAPSIntType(resultTy); + const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS()); + const llvm::APSInt &second = IntType.convert(*RHSValue); + + const llvm::APSInt *newRHS; + if (lop == op) + newRHS = BasicVals.evalAPSInt(BO_Add, first, second); + else + newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + + assert(newRHS && "Invalid operation despite common type!"); + rhs = nonloc::ConcreteInt(*newRHS); + lhs = nonloc::SymbolVal(symIntExpr->getLHS()); + op = lop; continue; - - // Other operators: do an implicit conversion. This shouldn't be - // necessary once we support truncation/extension of symbolic values. - if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ - rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, - rhs_I->getValue())); } - - continue; } - // Is the RHS a symbol we can simplify? - if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { - SymbolRef RSym = srhs->getSymbol(); - if (RSym->getType(Context)->isIntegerType()) { - if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { - // The symbol evaluates to a constant. - const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, - *Constant); - rhs = nonloc::ConcreteInt(rhs_I); - } - } + // Otherwise, make a SymIntExpr out of the expression. + return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy); } - if (isa<nonloc::ConcreteInt>(rhs)) { - return MakeSymIntVal(slhs->getSymbol(), op, - cast<nonloc::ConcreteInt>(rhs).getValue(), - resultTy); + + } else if (isa<SymbolData>(Sym)) { + // Does the symbol simplify to a constant? If so, "fold" the constant + // by setting 'lhs' to a ConcreteInt and try again. + if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { + lhs = nonloc::ConcreteInt(*Constant); + continue; } - return makeGenericVal(state, op, lhs, rhs, resultTy); + // Is the RHS a constant? + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) + return MakeSymIntVal(Sym, op, *RHSValue, resultTy); } + + // Give up -- this is not a symbolic expression we can handle. + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } } @@ -697,11 +673,18 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // regions, though. return UnknownVal(); - // If both values wrap regions, see if they're from different base regions. + const MemSpaceRegion *LeftMS = LeftMR->getMemorySpace(); + const MemSpaceRegion *RightMS = RightMR->getMemorySpace(); + const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); const MemRegion *LeftBase = LeftMR->getBaseRegion(); const MemRegion *RightBase = RightMR->getBaseRegion(); - if (LeftBase != RightBase && - !isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) { + + // If the two regions are from different known memory spaces they cannot be + // equal. Also, assume that no symbolic region (whose memory space is + // unknown) is on the stack. + if (LeftMS != RightMS && + ((LeftMS != UnknownMS && RightMS != UnknownMS) || + (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) { switch (op) { default: return UnknownVal(); @@ -712,24 +695,20 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } } - // The two regions are from the same base region. See if they're both a - // type of region we know how to compare. - const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); - const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); - - // Heuristic: assume that no symbolic region (whose memory space is - // unknown) is on the stack. - // FIXME: we should be able to be more precise once we can do better - // aliasing constraints for symbolic regions, but this is a reasonable, - // albeit unsound, assumption that holds most of the time. - if (isa<StackSpaceRegion>(LeftMS) ^ isa<StackSpaceRegion>(RightMS)) { + // If both values wrap regions, see if they're from different base regions. + // Note, heap base symbolic regions are assumed to not alias with + // each other; for example, we assume that malloc returns different address + // on each invocation. + if (LeftBase != RightBase && + ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) || + (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){ switch (op) { - default: - break; - case BO_EQ: - return makeTruthVal(false, resultTy); - case BO_NE: - return makeTruthVal(true, resultTy); + default: + return UnknownVal(); + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); } } @@ -885,6 +864,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, return evalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy); } } + return UnknownVal(); } // We are dealing with pointer arithmetic. diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 11748ae..3af60a1 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" @@ -23,10 +24,21 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr) : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} -StoreRef StoreManager::enterStackFrame(ProgramStateRef state, - const LocationContext *callerCtx, - const StackFrameContext *calleeCtx) { - return StoreRef(state->getStore(), *this); +StoreRef StoreManager::enterStackFrame(Store OldStore, + const CallEvent &Call, + const StackFrameContext *LCtx) { + StoreRef Store = StoreRef(OldStore, *this); + + SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings; + Call.getInitialStackFrameContents(LCtx, InitialBindings); + + for (CallEvent::BindingsTy::iterator I = InitialBindings.begin(), + E = InitialBindings.end(); + I != E; ++I) { + Store = Bind(Store.getStore(), I->first, I->second); + } + + return Store; } const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, @@ -210,6 +222,17 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) llvm_unreachable("unreachable"); } +SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) { + // Walk through the cast path to create nested CXXBaseRegions. + SVal Result = Derived; + for (CastExpr::path_const_iterator I = Cast->path_begin(), + E = Cast->path_end(); + I != E; ++I) { + Result = evalDerivedToBase(Result, (*I)->getType()); + } + return Result; +} + /// CastRetrievedVal - Used by subclasses of StoreManager to implement /// implicit casts that arise from loads from regions that are reinterpreted @@ -357,6 +380,3 @@ bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr, return true; } - -void SubRegionMap::anchor() { } -void SubRegionMap::Visitor::anchor() { } diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index adefb58..0bc192d 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -164,6 +164,13 @@ void SymExpr::symbol_iterator::expand() { llvm_unreachable("unhandled expansion case"); } +unsigned SymExpr::computeComplexity() const { + unsigned R = 0; + for (symbol_iterator I = symbol_begin(), E = symbol_end(); I != E; ++I) + R++; + return R; +} + const SymbolRegionValue* SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { llvm::FoldingSetNodeID profile; @@ -501,6 +508,9 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { return false; return true; } + // If no statement is provided, everything is this and parent contexts is live. + if (!Loc) + return true; return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal); } @@ -510,6 +520,10 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame(); if (VarContext == CurrentContext) { + // If no statemetnt is provided, everything is live. + if (!Loc) + return true; + if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl())) return true; diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index fe912df..e5b8553 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -42,6 +42,7 @@ public: bool supportsLogicalOpControlFlow() const { return true; } bool supportsAllBlockEdges() const { return true; } virtual bool useVerboseDescription() const { return true; } + virtual bool supportsCrossFileDiagnostics() const { return true; } }; } // end anonymous namespace @@ -58,7 +59,9 @@ void TextPathDiagnostics::FlushDiagnosticsImpl( for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { const PathDiagnostic *D = *it; - for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + + PathPieces FlatPath = D->path.flatten(/*ShouldFlattenMacros=*/true); + for (PathPieces::const_iterator I = FlatPath.begin(), E = FlatPath.end(); I != E; ++I) { unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 008f744..fcdaaea 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -22,6 +22,7 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CallGraph.h" +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" @@ -57,6 +58,7 @@ STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level)."); STATISTIC(NumBlocksInAnalyzedFunctions, "The # of basic blocks in the analyzed functions."); STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); +STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); //===----------------------------------------------------------------------===// // Special PathDiagnosticConsumers. @@ -102,7 +104,7 @@ public: /// The local declaration to all declarations ratio might be very small when /// working with a PCH file. SetOfDecls LocalTUDecls; - + // PD is owned by AnalysisManager. PathDiagnosticConsumer *PD; @@ -212,7 +214,6 @@ public: Opts.AnalysisPurgeOpt, Opts.EagerlyAssume, Opts.TrimGraph, Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, - Opts.CFGAddInitializers, Opts.EagerlyTrimEGraph, Opts.IPAMode, Opts.InlineMaxStackDepth, @@ -230,7 +231,7 @@ public: /// \brief Build the call graph for all the top level decls of this TU and /// use it to define the order in which the functions should be visited. - void HandleDeclsGallGraph(); + void HandleDeclsGallGraph(const unsigned LocalTUDeclsSize); /// \brief Run analyzes(syntax or path sensitive) on the given function. /// \param Mode - determines if we are requesting syntax only or path @@ -246,6 +247,7 @@ public: SetOfConstDecls *VisitedCallees); /// Visitors for the RecursiveASTVisitor. + bool shouldWalkTypesOfTypeLocs() const { return false; } /// Handle callbacks for arbitrary Decls. bool VisitDecl(Decl *D) { @@ -306,18 +308,22 @@ void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { if (isa<ObjCMethodDecl>(*I)) continue; - LocalTUDecls.insert(*I); + LocalTUDecls.push_back(*I); } } -void AnalysisConsumer::HandleDeclsGallGraph() { +void AnalysisConsumer::HandleDeclsGallGraph(const unsigned LocalTUDeclsSize) { // Otherwise, use the Callgraph to derive the order. // Build the Call Graph. CallGraph CG; + // Add all the top level declarations to the graph. - for (SetOfDecls::iterator I = LocalTUDecls.begin(), - E = LocalTUDecls.end(); I != E; ++I) - CG.addToCallGraph(*I); + // Note: CallGraph can trigger deserialization of more items from a pch + // (though HandleInterestingDecl); triggering additions to LocalTUDecls. + // We rely on random access to add the initially processed Decls to CG. + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + CG.addToCallGraph(LocalTUDecls[i]); + } // Find the top level nodes - children of root + the unreachable (parentless) // nodes. @@ -338,11 +344,11 @@ void AnalysisConsumer::HandleDeclsGallGraph() { // translation unit. This step is very important for performance. It ensures // that we analyze the root functions before the externally available // subroutines. - std::queue<CallGraphNode*> BFSQueue; + std::deque<CallGraphNode*> BFSQueue; for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); TI != TE; ++TI) - BFSQueue.push(*TI); + BFSQueue.push_back(*TI); // BFS over all of the functions, while skipping the ones inlined into // the previously processed functions. Use external Visited set, which is @@ -350,7 +356,14 @@ void AnalysisConsumer::HandleDeclsGallGraph() { SmallPtrSet<CallGraphNode*,24> Visited; while(!BFSQueue.empty()) { CallGraphNode *N = BFSQueue.front(); - BFSQueue.pop(); + BFSQueue.pop_front(); + + // Push the children into the queue. + for (CallGraphNode::const_iterator CI = N->begin(), + CE = N->end(); CI != CE; ++CI) { + if (!Visited.count(*CI)) + BFSQueue.push_back(*CI); + } // Skip the functions which have been processed already or previously // inlined. @@ -365,19 +378,13 @@ void AnalysisConsumer::HandleDeclsGallGraph() { (Mgr->InliningMode == All ? 0 : &VisitedCallees)); // Add the visited callees to the global visited set. - for (SetOfConstDecls::const_iterator I = VisitedCallees.begin(), - E = VisitedCallees.end(); I != E; ++I){ + for (SetOfConstDecls::iterator I = VisitedCallees.begin(), + E = VisitedCallees.end(); I != E; ++I) { CallGraphNode *VN = CG.getNode(*I); if (VN) Visited.insert(VN); } Visited.insert(N); - - // Push the children into the queue. - for (CallGraphNode::const_iterator CI = N->begin(), - CE = N->end(); CI != CE; ++CI) { - BFSQueue.push(*CI); - } } } @@ -402,12 +409,18 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { RecVisitorBR = &BR; // Process all the top level declarations. - for (SetOfDecls::iterator I = LocalTUDecls.begin(), - E = LocalTUDecls.end(); I != E; ++I) - TraverseDecl(*I); + // + // Note: TraverseDecl may modify LocalTUDecls, but only by appending more + // entries. Thus we don't use an iterator, but rely on LocalTUDecls + // random access. By doing so, we automatically compensate for iterators + // possibly being invalidated, although this is a bit slower. + const unsigned LocalTUDeclsSize = LocalTUDecls.size(); + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + TraverseDecl(LocalTUDecls[i]); + } if (Mgr->shouldInlineCall()) - HandleDeclsGallGraph(); + HandleDeclsGallGraph(LocalTUDeclsSize); // After all decls handled, run checkers on the entire TranslationUnit. checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); @@ -475,6 +488,12 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, return; DisplayFunction(D, Mode); + CFG *DeclCFG = Mgr->getCFG(D); + if (DeclCFG) { + unsigned CFGSize = DeclCFG->size(); + MaxCFGSize = MaxCFGSize < CFGSize ? CFGSize : MaxCFGSize; + } + // Clear the AnalysisManager of old AnalysisDeclContexts. Mgr->ClearContexts(); @@ -510,6 +529,10 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, if (!Mgr->getCFG(D)) return; + // See if the LiveVariables analysis scales. + if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) + return; + ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries); // Set the graph auditor. @@ -520,7 +543,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, } // Execute the worklist algorithm. - Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0), + Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), Mgr->getMaxNodes()); // Release the auditor (if any) so that it doesn't monitor the graph diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index bbcb085..06d1485 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -1,8 +1,5 @@ set(LLVM_NO_RTTI 1) -set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite - clangStaticAnalyzerCheckers) - include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../Checkers ) add_clang_library(clangStaticAnalyzerFrontend @@ -16,6 +13,18 @@ add_dependencies(clangStaticAnalyzerFrontend clangStaticAnalyzerCore ClangAttrClasses ClangAttrList + ClangCommentNodes ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticFrontend ClangStmtNodes ) + +target_link_libraries(clangStaticAnalyzerFrontend + clangBasic + clangLex + clangAST + clangFrontend + clangRewrite + clangStaticAnalyzerCheckers + ) diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index c06da0d..0229aed 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -118,7 +118,7 @@ CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts, for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) { if (checkerOpts[i].isUnclaimed()) - diags.Report(diag::warn_unknown_analyzer_checker) + diags.Report(diag::err_unknown_analyzer_checker) << checkerOpts[i].getName(); } |