diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2009-12-15 18:49:47 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2009-12-15 18:49:47 +0000 |
commit | 77212133072dc40f070a280af8217032f55a9eb4 (patch) | |
tree | 2fd5819f49caecc5f520219b6b9254fe94ebb138 /lib/Analysis | |
parent | 4b08eb6308ca90a6c08e2fc79d100821b1b1f6aa (diff) | |
download | FreeBSD-src-77212133072dc40f070a280af8217032f55a9eb4.zip FreeBSD-src-77212133072dc40f070a280af8217032f55a9eb4.tar.gz |
Update clang to 91430.
Diffstat (limited to 'lib/Analysis')
24 files changed, 1341 insertions, 818 deletions
diff --git a/lib/Analysis/AnalysisContext.cpp b/lib/Analysis/AnalysisContext.cpp index 339e2c9..05e5196 100644 --- a/lib/Analysis/AnalysisContext.cpp +++ b/lib/Analysis/AnalysisContext.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/PathSensitive/AnalysisContext.h" +#include "clang/Analysis/PathSensitive/MemRegion.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CFG.h" #include "clang/AST/Decl.h" @@ -35,8 +36,10 @@ Stmt *AnalysisContext::getBody() { return FD->getBody(); else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) return MD->getBody(); + else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) + return BD->getBody(); - llvm::llvm_unreachable("unknown code decl"); + llvm_unreachable("unknown code decl"); } const ImplicitParamDecl *AnalysisContext::getSelfDecl() const { @@ -80,73 +83,113 @@ AnalysisContext *AnalysisContextManager::getContext(const Decl *D) { return AC; } -void LocationContext::Profile(llvm::FoldingSetNodeID &ID, ContextKind k, - AnalysisContext *ctx, - const LocationContext *parent) { - ID.AddInteger(k); +const BlockDecl *BlockInvocationContext::getBlockDecl() const { + return Data.is<const BlockDataRegion*>() ? + Data.get<const BlockDataRegion*>()->getDecl() + : Data.get<const BlockDecl*>(); +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling. +//===----------------------------------------------------------------------===// + +void LocationContext::ProfileCommon(llvm::FoldingSetNodeID &ID, + ContextKind ck, + AnalysisContext *ctx, + const LocationContext *parent, + const void* data) { + ID.AddInteger(ck); ID.AddPointer(ctx); ID.AddPointer(parent); + ID.AddPointer(data); } -void StackFrameContext::Profile(llvm::FoldingSetNodeID &ID,AnalysisContext *ctx, - const LocationContext *parent, const Stmt *s) { - LocationContext::Profile(ID, StackFrame, ctx, parent); - ID.AddPointer(s); +void StackFrameContext::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getAnalysisContext(), getParent(), CallSite); } -void ScopeContext::Profile(llvm::FoldingSetNodeID &ID, AnalysisContext *ctx, - const LocationContext *parent, const Stmt *s) { - LocationContext::Profile(ID, Scope, ctx, parent); - ID.AddPointer(s); +void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getAnalysisContext(), getParent(), Enter); } -LocationContextManager::~LocationContextManager() { - clear(); +void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { + if (const BlockDataRegion *BR = getBlockRegion()) + Profile(ID, getAnalysisContext(), getParent(), BR); + else + Profile(ID, getAnalysisContext(), getParent(), + Data.get<const BlockDecl*>()); } -void LocationContextManager::clear() { - for (llvm::FoldingSet<LocationContext>::iterator I = Contexts.begin(), - E = Contexts.end(); I != E; ) { - LocationContext *LC = &*I; - ++I; - delete LC; - } +//===----------------------------------------------------------------------===// +// LocationContext creation. +//===----------------------------------------------------------------------===// + +template <typename LOC, typename DATA> +const LOC* +LocationContextManager::getLocationContext(AnalysisContext *ctx, + const LocationContext *parent, + const DATA *d) { + llvm::FoldingSetNodeID ID; + LOC::Profile(ID, ctx, parent, d); + void *InsertPos; - Contexts.clear(); + LOC *L = cast_or_null<LOC>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); + + if (!L) { + L = new LOC(ctx, parent, d); + Contexts.InsertNode(L, InsertPos); + } + return L; } -StackFrameContext* +const StackFrameContext* LocationContextManager::getStackFrame(AnalysisContext *ctx, const LocationContext *parent, const Stmt *s) { - llvm::FoldingSetNodeID ID; - StackFrameContext::Profile(ID, ctx, parent, s); - void *InsertPos; + return getLocationContext<StackFrameContext, Stmt>(ctx, parent, s); +} - StackFrameContext *f = - cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); - if (!f) { - f = new StackFrameContext(ctx, parent, s); - Contexts.InsertNode(f, InsertPos); - } - return f; +const ScopeContext * +LocationContextManager::getScope(AnalysisContext *ctx, + const LocationContext *parent, + const Stmt *s) { + return getLocationContext<ScopeContext, Stmt>(ctx, parent, s); } -ScopeContext *LocationContextManager::getScope(AnalysisContext *ctx, - const LocationContext *parent, - const Stmt *s) { - llvm::FoldingSetNodeID ID; - ScopeContext::Profile(ID, ctx, parent, s); - void *InsertPos; +const BlockInvocationContext * +LocationContextManager::getBlockInvocation(AnalysisContext *ctx, + const LocationContext *parent, + const BlockDataRegion *BR) { + return getLocationContext<BlockInvocationContext, BlockDataRegion>(ctx, + parent, + BR); +} - ScopeContext *scope = - cast_or_null<ScopeContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); +//===----------------------------------------------------------------------===// +// LocationContext methods. +//===----------------------------------------------------------------------===// - if (!scope) { - scope = new ScopeContext(ctx, parent, s); - Contexts.InsertNode(scope, InsertPos); +const StackFrameContext *LocationContext::getCurrentStackFrame() const { + const LocationContext *LC = this; + while (LC) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) + return SFC; + LC = LC->getParent(); } - return scope; + return NULL; +} + +const StackFrameContext * +LocationContext::getStackFrameForDeclContext(const DeclContext *DC) const { + const LocationContext *LC = this; + while (LC) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) { + if (cast<DeclContext>(SFC->getDecl()) == DC) + return SFC; + } + LC = LC->getParent(); + } + return NULL; } //===----------------------------------------------------------------------===// @@ -220,3 +263,21 @@ AnalysisContextManager::~AnalysisContextManager() { for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) delete I->second; } + +LocationContext::~LocationContext() {} + +LocationContextManager::~LocationContextManager() { + clear(); +} + +void LocationContextManager::clear() { + for (llvm::FoldingSet<LocationContext>::iterator I = Contexts.begin(), + E = Contexts.end(); I != E; ) { + LocationContext *LC = &*I; + ++I; + delete LC; + } + + Contexts.clear(); +} + diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp index 45fc11a..a38aaa7 100644 --- a/lib/Analysis/BasicStore.cpp +++ b/lib/Analysis/BasicStore.cpp @@ -68,14 +68,14 @@ public: } const GRState *BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr* cl, + const CompoundLiteralExpr*, + const LocationContext*, SVal val) { return state; } SVal getLValueVar(const VarDecl *VD, const LocationContext *LC); SVal getLValueString(const StringLiteral *S); - SVal getLValueCompoundLiteral(const CompoundLiteralExpr *CL); SVal getLValueIvar(const ObjCIvarDecl* D, SVal Base); SVal getLValueField(const FieldDecl *D, SVal Base); SVal getLValueElement(QualType elementType, SVal Offset, SVal Base); @@ -130,10 +130,6 @@ SVal BasicStoreManager::getLValueString(const StringLiteral* S) { return ValMgr.makeLoc(MRMgr.getStringRegion(S)); } -SVal BasicStoreManager::getLValueCompoundLiteral(const CompoundLiteralExpr* CL){ - return ValMgr.makeLoc(MRMgr.getCompoundLiteralRegion(CL)); -} - SVal BasicStoreManager::getLValueIvar(const ObjCIvarDecl* D, SVal Base) { if (Base.isUnknownOrUndef()) @@ -368,7 +364,7 @@ BasicStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, // Iterate over the variable bindings. for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { if (const VarRegion *VR = dyn_cast<VarRegion>(I.getKey())) { - if (SymReaper.isLive(Loc, VR->getDecl())) + if (SymReaper.isLive(Loc, VR)) RegionRoots.push_back(VR); else continue; diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp index c26a60a..e648269 100644 --- a/lib/Analysis/BugReporter.cpp +++ b/lib/Analysis/BugReporter.cpp @@ -204,10 +204,19 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, os << "Execution continues on line " << getSourceManager().getInstantiationLineNumber(Loc.asLocation()) << '.'; - else - os << "Execution jumps to the end of the " - << (isa<ObjCMethodDecl>(N->getLocationContext()->getDecl()) ? - "method" : "function") << '.'; + else { + os << "Execution jumps to the end of the "; + const Decl *D = N->getLocationContext()->getDecl(); + if (isa<ObjCMethodDecl>(D)) + os << "method"; + else if (isa<FunctionDecl>(D)) + os << "function"; + else { + assert(isa<BlockDecl>(D)); + os << "anonymous block"; + } + os << '.'; + } return Loc; } diff --git a/lib/Analysis/BuiltinFunctionChecker.cpp b/lib/Analysis/BuiltinFunctionChecker.cpp new file mode 100644 index 0000000..a89ad21 --- /dev/null +++ b/lib/Analysis/BuiltinFunctionChecker.cpp @@ -0,0 +1,76 @@ +//=== BuiltinFunctionChecker.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 evaluates clang builtin functions. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +namespace { + +class BuiltinFunctionChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void clang::RegisterBuiltinFunctionChecker(GRExprEngine &Eng) { + Eng.registerCheck(new BuiltinFunctionChecker()); +} + +bool BuiltinFunctionChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE){ + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + + if (!FD) + return false; + + unsigned id = FD->getBuiltinID(); + + if (!id) + return false; + + switch (id) { + case Builtin::BI__builtin_expect: { + // For __builtin_expect, just return the value of the subexpression. + assert (CE->arg_begin() != CE->arg_end()); + SVal X = state->getSVal(*(CE->arg_begin())); + C.GenerateNode(state->BindExpr(CE, X)); + return true; + } + + case Builtin::BI__builtin_alloca: { + // FIXME: Refactor into StoreManager itself? + MemRegionManager& RM = C.getStoreManager().getRegionManager(); + const MemRegion* R = + RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), + C.getPredecessor()->getLocationContext()); + + // Set the extent of the region in bytes. This enables us to use the + // SVal of the argument directly. If we save the extent in bits, we + // cannot represent values like symbol*8. + SVal Extent = state->getSVal(*(CE->arg_begin())); + state = C.getStoreManager().setExtent(state, R, Extent); + C.GenerateNode(state->BindExpr(CE, loc::MemRegionVal(R))); + return true; + } + } + + return false; +} diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index c97692f..e1a1e72 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -87,7 +87,6 @@ private: CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, bool alwaysAdd); CFGBlock *VisitBinaryOperator(BinaryOperator *B, bool alwaysAdd); CFGBlock *VisitBlockExpr(BlockExpr* E, bool alwaysAdd); - CFGBlock *VisitBlockDeclRefExpr(BlockDeclRefExpr* E, bool alwaysAdd); CFGBlock *VisitBreakStmt(BreakStmt *B); CFGBlock *VisitCallExpr(CallExpr *C, bool alwaysAdd); CFGBlock *VisitCaseStmt(CaseStmt *C); @@ -95,7 +94,9 @@ private: CFGBlock *VisitCompoundStmt(CompoundStmt *C); CFGBlock *VisitConditionalOperator(ConditionalOperator *C); CFGBlock *VisitContinueStmt(ContinueStmt *C); + CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S) { return NYS(); } CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); + CFGBlock *VisitCXXTryStmt(CXXTryStmt *S) { return NYS(); } CFGBlock *VisitDeclStmt(DeclStmt *DS); CFGBlock *VisitDeclSubExpr(Decl* D); CFGBlock *VisitDefaultStmt(DefaultStmt *D); @@ -292,9 +293,6 @@ tryAgain: case Stmt::BlockExprClass: return VisitBlockExpr(cast<BlockExpr>(S), alwaysAdd); - case Stmt::BlockDeclRefExprClass: - return VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(S), alwaysAdd); - case Stmt::BreakStmtClass: return VisitBreakStmt(cast<BreakStmt>(S)); @@ -468,12 +466,6 @@ CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, bool alwaysAdd) { return Block; } -CFGBlock *CFGBuilder::VisitBlockDeclRefExpr(BlockDeclRefExpr* E, - bool alwaysAdd) { - // FIXME - return NYS(); -} - CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) { // "break" is a control-flow statement. Thus we stop processing the current // block. diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp index b95f981..9639ad9 100644 --- a/lib/Analysis/CFRefCount.cpp +++ b/lib/Analysis/CFRefCount.cpp @@ -675,11 +675,9 @@ template <> struct DenseMapInfo<ObjCSummaryKey> { RHS.getSelector()); } - static bool isPod() { - return DenseMapInfo<ObjCInterfaceDecl*>::isPod() && - DenseMapInfo<Selector>::isPod(); - } }; +template <> +struct isPodLike<ObjCSummaryKey> { static const bool value = true; }; } // end llvm namespace namespace { @@ -1984,8 +1982,9 @@ public: Expr* Ex, Expr* Receiver, const RetainSummary& Summ, + const MemRegion *Callee, ExprIterator arg_beg, ExprIterator arg_end, - ExplodedNode* Pred); + ExplodedNode* Pred, const GRState *state); virtual void EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Eng, @@ -1998,7 +1997,8 @@ public: GRExprEngine& Engine, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, - ExplodedNode* Pred); + ExplodedNode* Pred, + const GRState *state); bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst, GRExprEngine& Engine, @@ -2776,11 +2776,9 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, Expr* Ex, Expr* Receiver, const RetainSummary& Summ, + const MemRegion *Callee, ExprIterator arg_beg, ExprIterator arg_end, - ExplodedNode* Pred) { - - // Get the state. - const GRState *state = Builder.GetState(Pred); + ExplodedNode* Pred, const GRState *state) { // Evaluate the effect of the arguments. RefVal::Kind hasErr = (RefVal::Kind) 0; @@ -2788,6 +2786,8 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, Expr* ErrorExpr = NULL; SymbolRef ErrorSym = 0; + llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate; + for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { SVal V = state->getSValAsScalarOrLoc(*I); SymbolRef Sym = V.getAsLocSymbol(); @@ -2810,16 +2810,8 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, continue; // Invalidate the value of the variable passed by reference. - - // 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 = Builder.getCurrentBlockCount(); - StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); - const MemRegion *R = MR->getRegion(); + // Are we dealing with an ElementRegion? If the element type is // a basic integer type (e.g., char, int) and the underying region // is a variable region then strip off the ElementRegion. @@ -2843,14 +2835,11 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, } // FIXME: What about layers of ElementRegions? } - - StoreManager::InvalidatedSymbols IS; - state = StoreMgr.InvalidateRegion(state, R, *I, Count, &IS); - for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), - E = IS.end(); I!=E; ++I) { - // Remove any existing reference-count binding. - state = state->remove<RefBindings>(*I); - } + + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + RegionsToInvalidate.push_back(R); + continue; } else { // Nuke all other arguments passed by reference. @@ -2866,6 +2855,36 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, goto tryAgain; } } + + // Block calls result in all captured values passed-via-reference to be + // invalidated. + if (const BlockDataRegion *BR = dyn_cast_or_null<BlockDataRegion>(Callee)) { + RegionsToInvalidate.push_back(BR); + } + + // Invalidate regions we designed for invalidation use the batch invalidation + // API. + if (!RegionsToInvalidate.empty()) { + // 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 = Builder.getCurrentBlockCount(); + StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); + + + StoreManager::InvalidatedSymbols IS; + state = StoreMgr.InvalidateRegions(state, RegionsToInvalidate.data(), + RegionsToInvalidate.data() + + RegionsToInvalidate.size(), + Ex, Count, &IS); + for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), + E = IS.end(); I!=E; ++I) { + // Remove any existing reference-count binding. + state = state->remove<RefBindings>(*I); + } + } // Evaluate the effect on the message receiver. if (!ErrorExpr && Receiver) { @@ -3012,35 +3031,24 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst, } assert(Summ); - EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, - CE->arg_begin(), CE->arg_end(), Pred); + EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(), + CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred)); } void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, - ExplodedNode* Pred) { - // FIXME: Since we moved the nil check into a checker, we could get nil - // receiver here. Need a better way to check such case. - if (Expr* Receiver = ME->getReceiver()) { - const GRState *state = Pred->getState(); - DefinedOrUnknownSVal L=cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); - if (!state->Assume(L, true)) { - Dst.Add(Pred); - return; - } - } - + ExplodedNode* Pred, + const GRState *state) { RetainSummary *Summ = ME->getReceiver() - ? Summaries.getInstanceMethodSummary(ME, Builder.GetState(Pred), - Pred->getLocationContext()) + ? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext()) : Summaries.getClassMethodSummary(ME); assert(Summ && "RetainSummary is null"); - EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, - ME->arg_begin(), ME->arg_end(), Pred); + EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, NULL, + ME->arg_begin(), ME->arg_end(), Pred, state); } namespace { @@ -3671,7 +3679,24 @@ void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C, if (I == E) return; - state = state->scanReachableSymbols<StopTrackingCallback>(I, E).getState(); + // FIXME: For now we invalidate the tracking of all symbols passed to blocks + // via captured variables, even though captured variables result in a copy + // and in implicit increment/decrement of a retain count. + llvm::SmallVector<const MemRegion*, 10> Regions; + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + MemRegionManager &MemMgr = C.getValueManager().getRegionManager(); + + for ( ; I != E; ++I) { + const VarRegion *VR = *I; + if (VR->getSuperRegion() == R) { + VR = MemMgr.getVarRegion(VR->getDecl(), LC); + } + Regions.push_back(VR); + } + + state = + state->scanReachableSymbols<StopTrackingCallback>(Regions.data(), + Regions.data() + Regions.size()).getState(); C.addTransition(state); } diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 409292d..521f1be 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -10,10 +10,10 @@ add_clang_library(clangAnalysis BasicValueFactory.cpp BugReporter.cpp BugReporterVisitors.cpp + BuiltinFunctionChecker.cpp CFG.cpp CFRefCount.cpp CallAndMessageChecker.cpp - CallGraph.cpp CallInliner.cpp CastToStructChecker.cpp CheckDeadStores.cpp @@ -37,8 +37,10 @@ add_clang_library(clangAnalysis MallocChecker.cpp ManagerRegistry.cpp MemRegion.cpp + NoReturnFunctionChecker.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp + OSAtomicChecker.cpp PathDiagnostic.cpp PointerArithChecker.cpp PointerSubChecker.cpp diff --git a/lib/Analysis/CallAndMessageChecker.cpp b/lib/Analysis/CallAndMessageChecker.cpp index d8dd16c..c287354 100644 --- a/lib/Analysis/CallAndMessageChecker.cpp +++ b/lib/Analysis/CallAndMessageChecker.cpp @@ -41,6 +41,7 @@ public: void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME); private: void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); @@ -148,28 +149,12 @@ void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, } } } +} - // Check if the receiver was nil and then returns a value that may - // be garbage. - if (const Expr *Receiver = ME->getReceiver()) { - DefinedOrUnknownSVal receiverVal = - cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); - - const GRState *notNullState, *nullState; - llvm::tie(notNullState, nullState) = state->Assume(receiverVal); - - if (nullState && !notNullState) { - HandleNilReceiver(C, nullState, ME); - C.setDoneEvaluating(); // FIXME: eventually remove. - return; - } - - assert(notNullState); - state = notNullState; - } - - // Add a state transition if the state has changed. - C.addTransition(state); +bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C, + const ObjCMessageExpr *ME) { + HandleNilReceiver(C, C.getState(), ME); + return true; // Nil receiver is not handled elsewhere. } void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C, diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp deleted file mode 100644 index c1040f0..0000000 --- a/lib/Analysis/CallGraph.cpp +++ /dev/null @@ -1,150 +0,0 @@ -//== CallGraph.cpp - Call graph building ------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the CallGraph and CGBuilder classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/CallGraph.h" - -#include "clang/AST/ASTContext.h" -#include "clang/AST/StmtVisitor.h" - -#include "llvm/Support/GraphWriter.h" - -using namespace clang; -using namespace idx; - -namespace { -class CGBuilder : public StmtVisitor<CGBuilder> { - - CallGraph &G; - FunctionDecl *FD; - - Entity CallerEnt; - - CallGraphNode *CallerNode; - -public: - CGBuilder(CallGraph &g, FunctionDecl *fd, Entity E, CallGraphNode *N) - : G(g), FD(fd), CallerEnt(E), CallerNode(N) {} - - void VisitStmt(Stmt *S) { VisitChildren(S); } - - void VisitCallExpr(CallExpr *CE); - - void VisitChildren(Stmt *S) { - for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I != E;++I) - if (*I) - static_cast<CGBuilder*>(this)->Visit(*I); - } -}; -} - -void CGBuilder::VisitCallExpr(CallExpr *CE) { - if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) { - Entity Ent = Entity::get(CalleeDecl, G.getProgram()); - CallGraphNode *CalleeNode = G.getOrInsertFunction(Ent); - CallerNode->addCallee(ASTLocation(FD, CE), CalleeNode); - } -} - -CallGraph::CallGraph() : Root(0) { - ExternalCallingNode = getOrInsertFunction(Entity()); -} - -CallGraph::~CallGraph() { - if (!FunctionMap.empty()) { - for (FunctionMapTy::iterator I = FunctionMap.begin(), E = FunctionMap.end(); - I != E; ++I) - delete I->second; - FunctionMap.clear(); - } -} - -void CallGraph::addTU(ASTContext& Ctx) { - DeclContext *DC = Ctx.getTranslationUnitDecl(); - for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); - I != E; ++I) { - - if (FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { - if (FD->isThisDeclarationADefinition()) { - // Set caller's ASTContext. - Entity Ent = Entity::get(FD, Prog); - CallGraphNode *Node = getOrInsertFunction(Ent); - CallerCtx[Node] = &Ctx; - - // If this function has external linkage, anything could call it. - if (FD->isGlobal()) - ExternalCallingNode->addCallee(idx::ASTLocation(), Node); - - // Set root node to 'main' function. - if (FD->getNameAsString() == "main") - Root = Node; - - CGBuilder builder(*this, FD, Ent, Node); - builder.Visit(FD->getBody()); - } - } - } -} - -CallGraphNode *CallGraph::getOrInsertFunction(Entity F) { - CallGraphNode *&Node = FunctionMap[F]; - if (Node) - return Node; - - return Node = new CallGraphNode(F); -} - -Decl *CallGraph::getDecl(CallGraphNode *Node) { - // Get the function's context. - ASTContext *Ctx = CallerCtx[Node]; - - return Node->getDecl(*Ctx); -} - -void CallGraph::print(llvm::raw_ostream &os) { - for (iterator I = begin(), E = end(); I != E; ++I) { - if (I->second->hasCallee()) { - os << "function: " << I->first.getPrintableName() - << " calls:\n"; - for (CallGraphNode::iterator CI = I->second->begin(), - CE = I->second->end(); CI != CE; ++CI) { - os << " " << CI->second->getName(); - } - os << '\n'; - } - } -} - -void CallGraph::dump() { - print(llvm::errs()); -} - -void CallGraph::ViewCallGraph() const { - llvm::ViewGraph(*this, "CallGraph"); -} - -namespace llvm { - -template <> -struct DOTGraphTraits<CallGraph> : public DefaultDOTGraphTraits { - - DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} - - static std::string getNodeLabel(const CallGraphNode *Node, - const CallGraph &CG) { - return Node->getName(); - - } - -}; - -} diff --git a/lib/Analysis/CheckDeadStores.cpp b/lib/Analysis/CheckDeadStores.cpp index ad63eb4..db9016f 100644 --- a/lib/Analysis/CheckDeadStores.cpp +++ b/lib/Analysis/CheckDeadStores.cpp @@ -84,7 +84,8 @@ public: const LiveVariables::AnalysisDataTy& AD, const LiveVariables::ValTy& Live) { - if (VD->hasLocalStorage() && !Live(VD, AD) && !VD->getAttr<UnusedAttr>()) + if (VD->hasLocalStorage() && !Live(VD, AD) && + !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) Report(VD, dsk, Ex->getSourceRange().getBegin(), Val->getSourceRange()); } @@ -185,6 +186,10 @@ public: if (V->hasLocalStorage()) if (Expr* E = V->getInit()) { + // Don't warn on C++ objects (yet) until we can show that their + // constructors/destructors don't have side effects. + if (isa<CXXConstructExpr>(E)) + return; // A dead initialization is a variable that is dead after it // is initialized. We don't flag warnings for those variables // marked 'unused'. diff --git a/lib/Analysis/CheckSecuritySyntaxOnly.cpp b/lib/Analysis/CheckSecuritySyntaxOnly.cpp index e6ab17a..3214101 100644 --- a/lib/Analysis/CheckSecuritySyntaxOnly.cpp +++ b/lib/Analysis/CheckSecuritySyntaxOnly.cpp @@ -23,6 +23,7 @@ class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; IdentifierInfo *II_gets; IdentifierInfo *II_getpw; + IdentifierInfo *II_mktemp; enum { num_rands = 9 }; IdentifierInfo *II_rand[num_rands]; IdentifierInfo *II_random; @@ -31,7 +32,8 @@ class WalkAST : public StmtVisitor<WalkAST> { public: WalkAST(BugReporter &br) : BR(br), - II_gets(0), II_getpw(0), II_rand(), II_random(0), II_setid() {} + II_gets(0), II_getpw(0), II_mktemp(0), + II_rand(), II_random(0), II_setid() {} // Statement visitor methods. void VisitCallExpr(CallExpr *CE); @@ -48,6 +50,7 @@ public: void CheckLoopConditionForFloat(const ForStmt *FS); void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); void CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD); void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); void CheckUncheckedReturnValue(CallExpr *CE); @@ -79,6 +82,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { if (const FunctionDecl *FD = CE->getDirectCallee()) { CheckCall_gets(CE, FD); CheckCall_getpw(CE, FD); + CheckCall_mktemp(CE, FD); CheckCall_rand(CE, FD); CheckCall_random(CE, FD); } @@ -288,6 +292,42 @@ void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { } //===----------------------------------------------------------------------===// +// Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). +// CWE-377: Insecure Temporary File +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) + return; + + const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType()); + if(!FPT) + return; + + // Verify that the funcion takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Verify that the argument is Pointer Type. + const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); + if (!PT) + return; + + // Verify that the argument is a 'char*'. + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a waring. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", + "Security", + "Call to function 'mktemp' is insecure as it always " + "creates or uses insecure temporary file", + CE->getLocStart(), &R, 1); +} + + +//===----------------------------------------------------------------------===// // Check: Linear congruent random number generators should not be used // Originally: <rdar://problem/63371000> // CWE-338: Use of cryptographically weak prng diff --git a/lib/Analysis/Checker.cpp b/lib/Analysis/Checker.cpp index 0d907e5..fb9d04d 100644 --- a/lib/Analysis/Checker.cpp +++ b/lib/Analysis/Checker.cpp @@ -24,10 +24,10 @@ CheckerContext::~CheckerContext() { // if we are building sinks or we generated a node and decided to not // add it as a transition. if (Dst.size() == size && !B.BuildSinks && !B.HasGeneratedNode) { - if (state && state != B.GetState(Pred)) { + if (ST && ST != B.GetState(Pred)) { static int autoTransitionTag = 0; B.Tag = &autoTransitionTag; - addTransition(state); + addTransition(ST); } else Dst.Add(Pred); diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 20820d4..51e6a54 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -116,53 +116,91 @@ public: // Checker worklist routines. //===----------------------------------------------------------------------===// -bool GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, +void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, bool isPrevisit) { if (Checkers.empty()) { Dst.insert(Src); - return false; + return; } ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; - bool stopProcessingAfterCurrentChecker = false; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) - { - ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst - : (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } void *tag = I->first; Checker *checker = I->second; for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) { - // FIXME: Halting evaluation of the checkers is something we may - // not support later. The design is still evolving. - if (checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, - tag, isPrevisit)) { - if (CurrSet != &Dst) - Dst.insert(*CurrSet); - - stopProcessingAfterCurrentChecker = true; - continue; - } - assert(stopProcessingAfterCurrentChecker == false && - "Inconsistent setting of 'stopProcessingAfterCurrentChecker'"); - } - - if (stopProcessingAfterCurrentChecker) - return true; - - // Continue on to the next checker. Update the current NodeSet. + NI != NE; ++NI) + checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit); PrevSet = CurrSet; } // Don't autotransition. The CheckerContext objects should do this // automatically. - return false; +} + +void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, + ExplodedNodeSet &Dst, + const GRState *state, + ExplodedNode *Pred) { + bool Evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_EvalNilReceiver(DstTmp, *Builder, *this, ME, Pred, state, + tag)) { + Evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the Dst. + DstTmp.clear(); + } + + if (Evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); +} + +// CheckerEvalCall returns true if one of the checkers processed the node. +// This may return void when all call evaluation logic goes to some checker +// in the future. +bool GRExprEngine::CheckerEvalCall(const CallExpr *CE, + ExplodedNodeSet &Dst, + ExplodedNode *Pred) { + bool Evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_EvalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) { + Evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the DstTmp set. + DstTmp.clear(); + } + + if (Evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); + + return Evaluated; } // FIXME: This is largely copy-paste from CheckerVisit(). Need to @@ -173,7 +211,7 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, SVal location, SVal val, bool isPrevisit) { if (Checkers.empty()) { - Dst = Src; + Dst.insert(Src); return; } @@ -182,10 +220,14 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) { - ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst - : (PrevSet == &Tmp) ? &Src : &Tmp; + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } - CurrSet->clear(); void *tag = I->first; Checker *checker = I->second; @@ -227,6 +269,11 @@ static void RegisterInternalChecks(GRExprEngine &Eng) { RegisterUndefinedAssignmentChecker(Eng); RegisterUndefBranchChecker(Eng); RegisterUndefResultChecker(Eng); + + // This is not a checker yet. + RegisterNoReturnFunctionChecker(Eng); + RegisterBuiltinFunctionChecker(Eng); + RegisterOSAtomicChecker(Eng); } GRExprEngine::GRExprEngine(AnalysisManager &mgr) @@ -347,8 +394,9 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { Builder->setAuditor(BatchAuditor.get()); // Create the cleaned state. - SymbolReaper SymReaper(Builder->getBasePredecessor()->getLiveVariables(), - SymMgr); + const ExplodedNode *BasePred = Builder->getBasePredecessor(); + SymbolReaper SymReaper(BasePred->getLiveVariables(), SymMgr, + BasePred->getLocationContext()->getCurrentStackFrame()); CleanedState = AMgr.shouldPurgeDead() ? StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt, SymReaper) : EntryNode->getState(); @@ -371,16 +419,20 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { CleanedState, SymReaper); if (Checkers.empty()) - Tmp = Tmp2; + Tmp.insert(Tmp2); else { ExplodedNodeSet Tmp3; ExplodedNodeSet *SrcSet = &Tmp2; for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); I != E; ++I) { - ExplodedNodeSet *DstSet = (I+1 == E) ? &Tmp - : (SrcSet == &Tmp2) ? &Tmp3 - : &Tmp2; - DstSet->clear(); + ExplodedNodeSet *DstSet = 0; + if (I+1 == E) + DstSet = &Tmp; + else { + DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; + DstSet->clear(); + } + void *tag = I->first; Checker *checker = I->second; for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); @@ -441,6 +493,41 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { } switch (S->getStmtClass()) { + // C++ stuff we don't support yet. + case Stmt::CXXMemberCallExprClass: + case Stmt::CXXNamedCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::CXXThisExprClass: + case Stmt::CXXThrowExprClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXZeroInitValueExprClass: + case Stmt::CXXNewExprClass: + case Stmt::CXXDeleteExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnaryTypeTraitExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::CXXConstructExprClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXExprWithTemporariesClass: + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::CXXCatchStmtClass: + case Stmt::CXXTryStmtClass: { + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } default: // Cases we intentionally have "default" handle: @@ -456,6 +543,10 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { case Stmt::AsmStmtClass: VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); break; + + case Stmt::BlockDeclRefExprClass: + VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(S), Pred, Dst, false); + break; case Stmt::BlockExprClass: VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); @@ -627,6 +718,10 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(Ex), Pred, Dst, true); return; + case Stmt::BlockDeclRefExprClass: + VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(Ex), Pred, Dst, true); + return; + case Stmt::DeclRefExprClass: VisitDeclRefExpr(cast<DeclRefExpr>(Ex), Pred, Dst, true); return; @@ -1118,9 +1213,20 @@ void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst, bool asLValue) { + VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); +} + +void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, bool asLValue) { + VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); +} + +void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, bool asLValue) { const GRState *state = GetState(Pred); - const NamedDecl *D = Ex->getDecl(); if (const VarDecl* VD = dyn_cast<VarDecl>(D)) { @@ -1354,10 +1460,14 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) { - ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst - : (PrevSet == &Tmp) ? &Src : &Tmp; + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } - CurrSet->clear(); void *tag = I->first; Checker *checker = I->second; @@ -1376,232 +1486,8 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, } //===----------------------------------------------------------------------===// -// Transfer function: OSAtomics. -// -// FIXME: Eventually refactor into a more "plugin" infrastructure. -//===----------------------------------------------------------------------===// - -// Mac OS X: -// http://developer.apple.com/documentation/Darwin/Reference/Manpages/man3 -// atomic.3.html -// -static bool EvalOSAtomicCompareAndSwap(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - CallExpr* CE, ExplodedNode* Pred) { - - // Not enough arguments to match OSAtomicCompareAndSwap? - if (CE->getNumArgs() != 3) - return false; - - ASTContext &C = Engine.getContext(); - Expr *oldValueExpr = CE->getArg(0); - QualType oldValueType = C.getCanonicalType(oldValueExpr->getType()); - - Expr *newValueExpr = CE->getArg(1); - QualType newValueType = C.getCanonicalType(newValueExpr->getType()); - - // Do the types of 'oldValue' and 'newValue' match? - if (oldValueType != newValueType) - return false; - - Expr *theValueExpr = CE->getArg(2); - const PointerType *theValueType = - theValueExpr->getType()->getAs<PointerType>(); - - // theValueType not a pointer? - if (!theValueType) - return false; - - QualType theValueTypePointee = - C.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); - - // The pointee must match newValueType and oldValueType. - if (theValueTypePointee != newValueType) - return false; - - static unsigned magic_load = 0; - static unsigned magic_store = 0; - - const void *OSAtomicLoadTag = &magic_load; - const void *OSAtomicStoreTag = &magic_store; - - // Load 'theValue'. - const GRState *state = Pred->getState(); - ExplodedNodeSet Tmp; - SVal location = state->getSVal(theValueExpr); - // Here we should use the value type of the region as the load type. - const MemRegion *R = location.getAsRegion(); - QualType LoadTy; - if (R) - LoadTy = cast<TypedRegion>(R)->getValueType(C); - Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag, - LoadTy); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); - I != E; ++I) { - - ExplodedNode *N = *I; - const GRState *stateLoad = N->getState(); - SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); - SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); - - // FIXME: Issue an error. - if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { - return false; - } - - DefinedOrUnknownSVal theValueVal = - cast<DefinedOrUnknownSVal>(theValueVal_untested); - DefinedOrUnknownSVal oldValueVal = - cast<DefinedOrUnknownSVal>(oldValueVal_untested); - - SValuator &SVator = Engine.getSValuator(); - - // Perform the comparison. - DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad, theValueVal, - oldValueVal); - - const GRState *stateEqual = stateLoad->Assume(Cmp, true); - - // Were they equal? - if (stateEqual) { - // Perform the store. - ExplodedNodeSet TmpStore; - SVal val = stateEqual->getSVal(newValueExpr); - - // Handle implicit value casts. - if (const TypedRegion *R = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - llvm::tie(state, val) = SVator.EvalCast(val, state, R->getValueType(C), - newValueExpr->getType()); - } - - Engine.EvalStore(TmpStore, NULL, theValueExpr, N, stateEqual, location, - val, OSAtomicStoreTag); - - // Now bind the result of the comparison. - for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), - E2 = TmpStore.end(); I2 != E2; ++I2) { - ExplodedNode *predNew = *I2; - const GRState *stateNew = predNew->getState(); - SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType()); - Engine.MakeNode(Dst, CE, predNew, stateNew->BindExpr(CE, Res)); - } - } - - // Were they not equal? - if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) { - SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); - Engine.MakeNode(Dst, CE, N, stateNotEqual->BindExpr(CE, Res)); - } - } - - return true; -} - -static bool EvalOSAtomic(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, - ExplodedNode* Pred) { - const FunctionDecl* FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - const char *FName = FD->getNameAsCString(); - - // Check for compare and swap. - if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 || - strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0) - return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, Pred); - - // FIXME: Other atomics. - return false; -} - -//===----------------------------------------------------------------------===// // Transfer function: Function calls. //===----------------------------------------------------------------------===// -static void MarkNoReturnFunction(const FunctionDecl *FD, CallExpr *CE, - const GRState *state, - GRStmtNodeBuilder *Builder) { - if (!FD) - return; - - if (FD->getAttr<NoReturnAttr>() || - FD->getAttr<AnalyzerNoReturnAttr>()) - Builder->BuildSinks = true; - else { - // HACK: Some functions are not marked noreturn, and don't return. - // Here are a few hardwired ones. If this takes too long, we can - // potentially cache these results. - using llvm::StringRef; - bool BuildSinks - = llvm::StringSwitch<bool>(StringRef(FD->getIdentifier()->getName())) - .Case("exit", true) - .Case("panic", true) - .Case("error", true) - .Case("Assert", true) - // FIXME: This is just a wrapper around throwing an exception. - // Eventually inter-procedural analysis should handle this easily. - .Case("ziperr", true) - .Case("assfail", true) - .Case("db_error", true) - .Case("__assert", true) - .Case("__assert_rtn", true) - .Case("__assert_fail", true) - .Case("dtrace_assfail", true) - .Case("yy_fatal_error", true) - .Case("_XCAssertionFailureHandler", true) - .Case("_DTAssertionFailureHandler", true) - .Case("_TSAssertionFailureHandler", true) - .Default(false); - - if (BuildSinks) - Builder->BuildSinks = true; - } -} - -bool GRExprEngine::EvalBuiltinFunction(const FunctionDecl *FD, CallExpr *CE, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - if (!FD) - return false; - - unsigned id = FD->getBuiltinID(); - if (!id) - return false; - - const GRState *state = Pred->getState(); - - switch (id) { - case Builtin::BI__builtin_expect: { - // For __builtin_expect, just return the value of the subexpression. - assert (CE->arg_begin() != CE->arg_end()); - SVal X = state->getSVal(*(CE->arg_begin())); - MakeNode(Dst, CE, Pred, state->BindExpr(CE, X)); - return true; - } - - case Builtin::BI__builtin_alloca: { - // FIXME: Refactor into StoreManager itself? - MemRegionManager& RM = getStateManager().getRegionManager(); - const MemRegion* R = - RM.getAllocaRegion(CE, Builder->getCurrentBlockCount()); - - // Set the extent of the region in bytes. This enables us to use the - // SVal of the argument directly. If we save the extent in bits, we - // cannot represent values like symbol*8. - SVal Extent = state->getSVal(*(CE->arg_begin())); - state = getStoreManager().setExtent(state, R, Extent); - MakeNode(Dst, CE, Pred, state->BindExpr(CE, loc::MemRegionVal(R))); - return true; - } - } - - return false; -} void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, CallExpr::arg_iterator AI, @@ -1659,6 +1545,8 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred, } // Finally, evaluate the function call. + ExplodedNodeSet DstTmp3; + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); DI != DE; ++DI) { @@ -1667,39 +1555,39 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred, // FIXME: Add support for symbolic function calls (calls involving // function pointer values that are symbolic). - - // Check for the "noreturn" attribute. - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - const FunctionDecl* FD = L.getAsFunctionDecl(); - - MarkNoReturnFunction(FD, CE, state, Builder); - - // Evaluate the call. - if (EvalBuiltinFunction(FD, CE, *DI, Dst)) - continue; - - // Dispatch to the plug-in transfer function. - SaveOr OldHasGen(Builder->HasGeneratedNode); - Pred = *DI; + ExplodedNodeSet DstChecker; - // Dispatch to transfer function logic to handle the call itself. - // FIXME: Allow us to chain together transfer functions. - assert(Builder && "GRStmtNodeBuilder must be defined."); - ExplodedNodeSet DstTmp; + // If the callee is processed by a checker, skip the rest logic. + if (CheckerEvalCall(CE, DstChecker, *DI)) + DstTmp3.insert(DstChecker); + else { + for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), + DE_Checker = DstChecker.end(); + DI_Checker != DE_Checker; ++DI_Checker) { + + // Dispatch to the plug-in transfer function. + unsigned OldSize = DstTmp3.size(); + SaveOr OldHasGen(Builder->HasGeneratedNode); + Pred = *DI_Checker; + + // Dispatch to transfer function logic to handle the call itself. + // FIXME: Allow us to chain together transfer functions. + assert(Builder && "GRStmtNodeBuilder must be defined."); - if (!EvalOSAtomic(DstTmp, *this, *Builder, CE, L, Pred)) - getTF().EvalCall(DstTmp, *this, *Builder, CE, L, Pred); + getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred); - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && DstTmp.empty() && - !Builder->HasGeneratedNode) - MakeNode(DstTmp, CE, Pred, state); - - // Perform the post-condition check of the CallExpr. - CheckerVisit(CE, Dst, DstTmp, false); + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && DstTmp3.size() == OldSize && + !Builder->HasGeneratedNode) + MakeNode(DstTmp3, CE, Pred, state); + } + } } + + // Perform the post-condition check of the CallExpr. + CheckerVisit(CE, Dst, DstTmp3, false); } //===----------------------------------------------------------------------===// @@ -1922,10 +1810,7 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, ExplodedNodeSet Src, DstTmp; Src.Add(Pred); - if (CheckerVisit(ME, DstTmp, Src, true)) { - Dst.insert(DstTmp); - return; - } + CheckerVisit(ME, DstTmp, Src, true); unsigned size = Dst.size(); @@ -1934,10 +1819,38 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, Pred = *DI; bool RaisesException = false; - if (ME->getReceiver()) { + if (const Expr *Receiver = ME->getReceiver()) { + const GRState *state = Pred->getState(); + + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = + cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); + + const GRState *notNilState, *nilState; + 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 handle must be nil, and merge the rest two into non-nil. + if (nilState && !notNilState) { + CheckerEvalNilReceiver(ME, Dst, nilState, Pred); + return; + } + + assert(notNilState); + // Check if the "raise" message was sent. if (ME->getSelector() == RaiseSel) RaisesException = true; + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + SaveOr OldHasGen(Builder->HasGeneratedNode); + EvalObjCMessageExpr(Dst, ME, Pred, notNilState); } else { @@ -1984,17 +1897,17 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, RaisesException = true; break; } } - } - // Check if we raise an exception. For now treat these as sinks. Eventually - // we will want to handle exceptions properly. - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - if (RaisesException) - Builder->BuildSinks = true; + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + if (RaisesException) + Builder->BuildSinks = true; - // Dispatch to plug-in transfer function. - SaveOr OldHasGen(Builder->HasGeneratedNode); - EvalObjCMessageExpr(Dst, ME, Pred); + // Dispatch to plug-in transfer function. + SaveOr OldHasGen(Builder->HasGeneratedNode); + EvalObjCMessageExpr(Dst, ME, Pred, Builder->GetState(Pred)); + } } // Handle the case where no nodes where generated. Auto-generate that @@ -2052,10 +1965,12 @@ void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { const GRState* state = GetState(*I); SVal ILV = state->getSVal(ILE); - state = state->bindCompoundLiteral(CL, ILV); + const LocationContext *LC = (*I)->getLocationContext(); + state = state->bindCompoundLiteral(CL, LC, ILV); - if (asLValue) - MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL))); + if (asLValue) { + MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); + } else MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); } diff --git a/lib/Analysis/GRExprEngineInternalChecks.h b/lib/Analysis/GRExprEngineInternalChecks.h index 5b7a757..e2354ed 100644 --- a/lib/Analysis/GRExprEngineInternalChecks.h +++ b/lib/Analysis/GRExprEngineInternalChecks.h @@ -37,5 +37,8 @@ void RegisterUndefinedAssignmentChecker(GRExprEngine &Eng); void RegisterUndefBranchChecker(GRExprEngine &Eng); void RegisterUndefResultChecker(GRExprEngine &Eng); +void RegisterNoReturnFunctionChecker(GRExprEngine &Eng); +void RegisterBuiltinFunctionChecker(GRExprEngine &Eng); +void RegisterOSAtomicChecker(GRExprEngine &Eng); } // end clang namespace #endif diff --git a/lib/Analysis/GRState.cpp b/lib/Analysis/GRState.cpp index a56859d..7415fa5 100644 --- a/lib/Analysis/GRState.cpp +++ b/lib/Analysis/GRState.cpp @@ -312,10 +312,10 @@ bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); for ( ; I != E; ++I) { - if (S.scan(*I)) - return true; + if (!S.scan(*I)) + return false; } - return false; + return true; } bool GRState::scanReachableSymbols(const MemRegion * const *I, @@ -323,10 +323,10 @@ bool GRState::scanReachableSymbols(const MemRegion * const *I, SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); for ( ; I != E; ++I) { - if (S.scan(*I)) - return true; + if (!S.scan(*I)) + return false; } - return false; + return true; } //===----------------------------------------------------------------------===// diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp index 204c7b3..2ed070a 100644 --- a/lib/Analysis/MallocChecker.cpp +++ b/lib/Analysis/MallocChecker.cpp @@ -22,10 +22,11 @@ using namespace clang; namespace { -struct RefState { +class RefState { enum Kind { Allocated, Released, Escaped } K; const Stmt *S; +public: RefState(Kind k, const Stmt *s) : K(k), S(s) {} bool isAllocated() const { return K == Allocated; } @@ -51,19 +52,25 @@ class RegionState {}; class MallocChecker : public CheckerVisitor<MallocChecker> { BuiltinBug *BT_DoubleFree; BuiltinBug *BT_Leak; - IdentifierInfo *II_malloc; - IdentifierInfo *II_free; + IdentifierInfo *II_malloc, *II_free, *II_realloc; public: - MallocChecker() : BT_DoubleFree(0), BT_Leak(0), II_malloc(0), II_free(0) {} + MallocChecker() + : BT_DoubleFree(0), BT_Leak(0), II_malloc(0), II_free(0), II_realloc(0) {} static void *getTag(); - void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper); void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); private: void MallocMem(CheckerContext &C, const CallExpr *CE); + const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state); void FreeMem(CheckerContext &C, const CallExpr *CE); + const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state); + + void ReallocMem(CheckerContext &C, const CallExpr *CE); }; } // end anonymous namespace @@ -84,39 +91,71 @@ void *MallocChecker::getTag() { return &x; } -void MallocChecker::PostVisitCallExpr(CheckerContext &C, const CallExpr *CE) { - const FunctionDecl *FD = CE->getDirectCallee(); +bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) - return; + return false; ASTContext &Ctx = C.getASTContext(); 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 (FD->getIdentifier() == II_malloc) { MallocMem(C, CE); - return; + return true; } if (FD->getIdentifier() == II_free) { FreeMem(C, CE); - return; + return true; + } + + if (FD->getIdentifier() == II_realloc) { + ReallocMem(C, CE); + return true; } + + return false; } void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - SVal CallVal = state->getSVal(CE); - SymbolRef Sym = CallVal.getAsLocSymbol(); + const GRState *state = MallocMemAux(C, CE, C.getState()); + C.addTransition(state); +} + +const GRState *MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + const GRState *state) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + ValueManager &ValMgr = C.getValueManager(); + + SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); + + state = state->BindExpr(CE, RetVal); + + SymbolRef Sym = RetVal.getAsLocSymbol(); assert(Sym); // Set the symbol's state to Allocated. - C.addTransition(state->set<RegionState>(Sym, RefState::getAllocated(CE))); + return state->set<RegionState>(Sym, RefState::getAllocated(CE)); } void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); + const GRState *state = FreeMemAux(C, CE, C.getState()); + + if (state) + C.addTransition(state); +} + +const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state) { SVal ArgVal = state->getSVal(CE->getArg(0)); SymbolRef Sym = ArgVal.getAsLocSymbol(); assert(Sym); @@ -136,13 +175,59 @@ void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { BT_DoubleFree->getDescription(), N); C.EmitReport(R); } - return; + return NULL; } // Normal free. - const GRState *FreedState - = state->set<RegionState>(Sym, RefState::getReleased(CE)); - C.addTransition(FreedState); + return state->set<RegionState>(Sym, RefState::getReleased(CE)); +} + +void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Arg0 = CE->getArg(0); + DefinedOrUnknownSVal Arg0Val=cast<DefinedOrUnknownSVal>(state->getSVal(Arg0)); + + ValueManager &ValMgr = C.getValueManager(); + SValuator &SVator = C.getSValuator(); + + DefinedOrUnknownSVal PtrEQ = SVator.EvalEQ(state, Arg0Val, ValMgr.makeNull()); + + // If the ptr is NULL, the call is equivalent to malloc(size). + if (const GRState *stateEqual = state->Assume(PtrEQ, true)) { + // Hack: set the NULL symbolic region to released to suppress false warning. + // In the future we should add more states for allocated regions, e.g., + // CheckedNull, CheckedNonNull. + + SymbolRef Sym = Arg0Val.getAsLocSymbol(); + if (Sym) + stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE)); + + const GRState *stateMalloc = MallocMemAux(C, CE, stateEqual); + C.addTransition(stateMalloc); + } + + if (const GRState *stateNotEqual = state->Assume(PtrEQ, false)) { + const Expr *Arg1 = CE->getArg(1); + DefinedOrUnknownSVal Arg1Val = + cast<DefinedOrUnknownSVal>(stateNotEqual->getSVal(Arg1)); + DefinedOrUnknownSVal SizeZero = SVator.EvalEQ(stateNotEqual, Arg1Val, + ValMgr.makeIntValWithPtrWidth(0, false)); + + if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) { + const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero); + if (stateFree) + C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); + } + + if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) { + const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero); + if (stateFree) { + // FIXME: We should copy the content of the original buffer. + const GRState *stateRealloc = MallocMemAux(C, CE, stateFree); + C.addTransition(stateRealloc); + } + } + } } void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp index af8bd16..bc3a5b7 100644 --- a/lib/Analysis/MemRegion.cpp +++ b/lib/Analysis/MemRegion.cpp @@ -22,6 +22,110 @@ using namespace clang; //===----------------------------------------------------------------------===// +// MemRegion Construction. +//===----------------------------------------------------------------------===// + +template<typename RegionTy> struct MemRegionManagerTrait; + +template <typename RegionTy, typename A1> +RegionTy* MemRegionManager::getRegion(const A1 a1) { + + const typename MemRegionManagerTrait<RegionTy>::SuperRegionTy *superRegion = + MemRegionManagerTrait<RegionTy>::getSuperRegion(*this, a1); + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate<RegionTy>(); + new (R) RegionTy(a1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template <typename RegionTy, typename A1> +RegionTy* MemRegionManager::getSubRegion(const A1 a1, + const MemRegion *superRegion) { + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate<RegionTy>(); + new (R) RegionTy(a1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template <typename RegionTy, typename A1, typename A2> +RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { + + const typename MemRegionManagerTrait<RegionTy>::SuperRegionTy *superRegion = + MemRegionManagerTrait<RegionTy>::getSuperRegion(*this, a1, a2); + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate<RegionTy>(); + new (R) RegionTy(a1, a2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template <typename RegionTy, typename A1, typename A2> +RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, + const MemRegion *superRegion) { + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate<RegionTy>(); + new (R) RegionTy(a1, a2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template <typename RegionTy, typename A1, typename A2, typename A3> +RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, + const MemRegion *superRegion) { + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate<RegionTy>(); + new (R) RegionTy(a1, a2, a3, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +//===----------------------------------------------------------------------===// // Object destruction. //===----------------------------------------------------------------------===// @@ -61,10 +165,24 @@ MemRegionManager* SubRegion::getMemRegionManager() const { } while (1); } +const StackFrameContext *VarRegion::getStackFrame() const { + const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace()); + return SSR ? SSR->getStackFrame() : NULL; +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling. +//===----------------------------------------------------------------------===// + void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger((unsigned)getKind()); } +void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned)getKind()); + ID.AddPointer(getStackFrame()); +} + void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const StringLiteral* Str, const MemRegion* superRegion) { @@ -109,7 +227,7 @@ void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { } void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { - VarRegion::ProfileRegion(ID, getDecl(), LC, superRegion); + VarRegion::ProfileRegion(ID, getDecl(), superRegion); } void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, @@ -148,27 +266,29 @@ void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { } void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const BlockDecl *BD, CanQualType, - const MemRegion*) { + const BlockDecl *BD, CanQualType, + const AnalysisContext *AC, + const MemRegion*) { ID.AddInteger(MemRegion::BlockTextRegionKind); ID.AddPointer(BD); } void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockTextRegion::ProfileRegion(ID, BD, locTy, superRegion); + BlockTextRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); } void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockTextRegion *BC, const LocationContext *LC, - const MemRegion *) { + const MemRegion *sReg) { ID.AddInteger(MemRegion::BlockDataRegionKind); ID.AddPointer(BC); ID.AddPointer(LC); + ID.AddPointer(sReg); } void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockDataRegion::ProfileRegion(ID, BC, LC, NULL); + BlockDataRegion::ProfileRegion(ID, BC, LC, getSuperRegion()); } //===----------------------------------------------------------------------===// @@ -249,36 +369,58 @@ void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const { // MemRegionManager methods. //===----------------------------------------------------------------------===// -MemSpaceRegion* MemRegionManager::LazyAllocate(MemSpaceRegion*& region) { +template <typename REG> +const REG *MemRegionManager::LazyAllocate(REG*& region) { if (!region) { - region = (MemSpaceRegion*) A.Allocate<MemSpaceRegion>(); - new (region) MemSpaceRegion(this); + region = (REG*) A.Allocate<REG>(); + new (region) REG(this); } return region; } -MemSpaceRegion* MemRegionManager::getStackRegion() { - return LazyAllocate(stack); +template <typename REG, typename ARG> +const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { + if (!region) { + region = (REG*) A.Allocate<REG>(); + new (region) REG(this, a); + } + + return region; } -MemSpaceRegion* MemRegionManager::getStackArgumentsRegion() { - return LazyAllocate(stackArguments); +const StackLocalsSpaceRegion* +MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { + assert(STC); + if (STC == cachedStackLocalsFrame) + return cachedStackLocalsRegion; + cachedStackLocalsFrame = STC; + return LazyAllocate(cachedStackLocalsRegion, STC); } -MemSpaceRegion* MemRegionManager::getGlobalsRegion() { +const StackArgumentsSpaceRegion * +MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { + assert(STC); + if (STC == cachedStackArgumentsFrame) + return cachedStackArgumentsRegion; + + cachedStackArgumentsFrame = STC; + return LazyAllocate(cachedStackArgumentsRegion, STC); +} + +const GlobalsSpaceRegion *MemRegionManager::getGlobalsRegion() { return LazyAllocate(globals); } -MemSpaceRegion* MemRegionManager::getHeapRegion() { +const HeapSpaceRegion *MemRegionManager::getHeapRegion() { return LazyAllocate(heap); } -MemSpaceRegion* MemRegionManager::getUnknownRegion() { +const MemSpaceRegion *MemRegionManager::getUnknownRegion() { return LazyAllocate(unknown); } -MemSpaceRegion* MemRegionManager::getCodeRegion() { +const MemSpaceRegion *MemRegionManager::getCodeRegion() { return LazyAllocate(code); } @@ -286,40 +428,79 @@ MemSpaceRegion* MemRegionManager::getCodeRegion() { // Constructing regions. //===----------------------------------------------------------------------===// -StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str) { - return getRegion<StringRegion>(Str); +const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str) { + return getSubRegion<StringRegion>(Str, getGlobalsRegion()); } -VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, - const LocationContext *LC) { +const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, + const LocationContext *LC) { + const MemRegion *sReg = 0; - // FIXME: Once we implement scope handling, we will need to properly lookup - // 'D' to the proper LocationContext. For now, just strip down to the - // StackFrame. - while (!isa<StackFrameContext>(LC)) - LC = LC->getParent(); + if (D->hasLocalStorage()) { + // 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); - return getRegion<VarRegion>(D, LC); + if (!STC) + sReg = getUnknownRegion(); + else { + sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D) + ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC)) + : static_cast<const MemRegion*>(getStackLocalsRegion(STC)); + } + } + else { + sReg = getGlobalsRegion(); + } + + return getSubRegion<VarRegion>(D, sReg); } -BlockDataRegion *MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, - const LocationContext *LC) -{ - // FIXME: Once we implement scope handling, we will need to properly lookup - // 'D' to the proper LocationContext. For now, just strip down to the - // StackFrame. - while (!isa<StackFrameContext>(LC)) - LC = LC->getParent(); +const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, + const MemRegion *superR) { + return getSubRegion<VarRegion>(D, superR); +} + +const BlockDataRegion * +MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, + const LocationContext *LC) { + const MemRegion *sReg = 0; - return getSubRegion<BlockDataRegion>(BC, LC, getStackRegion()); + if (LC) { + // FIXME: Once we implement scope handling, we want the parent region + // to be the scope. + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + else { + // We allow 'LC' to be NULL for cases where want BlockDataRegions + // without context-sensitivity. + sReg = getUnknownRegion(); + } + + return getSubRegion<BlockDataRegion>(BC, LC, sReg); } -CompoundLiteralRegion* -MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL) { - return getRegion<CompoundLiteralRegion>(CL); +const CompoundLiteralRegion* +MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, + const LocationContext *LC) { + + const MemRegion *sReg = 0; + + if (CL->isFileScope()) + sReg = getGlobalsRegion(); + else { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + + return getSubRegion<CompoundLiteralRegion>(CL, sReg); } -ElementRegion* +const ElementRegion* MemRegionManager::getElementRegion(QualType elementType, SVal Idx, const MemRegion* superRegion, ASTContext& Ctx){ @@ -342,41 +523,47 @@ MemRegionManager::getElementRegion(QualType elementType, SVal Idx, return R; } -FunctionTextRegion * +const FunctionTextRegion * MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { - return getRegion<FunctionTextRegion>(FD); + return getSubRegion<FunctionTextRegion>(FD, getCodeRegion()); } -BlockTextRegion *MemRegionManager::getBlockTextRegion(const BlockDecl *BD, - CanQualType locTy) { - return getRegion<BlockTextRegion>(BD, locTy); +const BlockTextRegion * +MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, + AnalysisContext *AC) { + return getSubRegion<BlockTextRegion>(BD, locTy, AC, getCodeRegion()); } /// getSymbolicRegion - Retrieve or create a "symbolic" memory region. -SymbolicRegion* MemRegionManager::getSymbolicRegion(SymbolRef sym) { - return getRegion<SymbolicRegion>(sym); +const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { + return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); } -FieldRegion* MemRegionManager::getFieldRegion(const FieldDecl* d, - const MemRegion* superRegion) { +const FieldRegion * +MemRegionManager::getFieldRegion(const FieldDecl* d, + const MemRegion* superRegion){ return getSubRegion<FieldRegion>(d, superRegion); } -ObjCIvarRegion* +const ObjCIvarRegion* MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, const MemRegion* superRegion) { return getSubRegion<ObjCIvarRegion>(d, superRegion); } -ObjCObjectRegion* +const ObjCObjectRegion* MemRegionManager::getObjCObjectRegion(const ObjCInterfaceDecl* d, const MemRegion* superRegion) { return getSubRegion<ObjCObjectRegion>(d, superRegion); } -AllocaRegion* MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt) { - return getRegion<AllocaRegion>(E, cnt); +const AllocaRegion* +MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC)); } const MemSpaceRegion *MemRegion::getMemorySpace() const { @@ -392,52 +579,30 @@ const MemSpaceRegion *MemRegion::getMemorySpace() const { } bool MemRegion::hasStackStorage() const { - if (const MemSpaceRegion *MS = getMemorySpace()) { - MemRegionManager *Mgr = getMemRegionManager(); - return MS == Mgr->getStackRegion() || MS == Mgr->getStackArgumentsRegion(); - } - - return false; + return isa<StackSpaceRegion>(getMemorySpace()); } bool MemRegion::hasHeapStorage() const { - if (const MemSpaceRegion *MS = getMemorySpace()) - return MS == getMemRegionManager()->getHeapRegion(); - - return false; + return isa<HeapSpaceRegion>(getMemorySpace()); } bool MemRegion::hasHeapOrStackStorage() const { - if (const MemSpaceRegion *MS = getMemorySpace()) { - MemRegionManager *Mgr = getMemRegionManager(); - return MS == Mgr->getHeapRegion() - || MS == Mgr->getStackRegion() - || MS == Mgr->getStackArgumentsRegion(); - } - return false; + const MemSpaceRegion *MS = getMemorySpace(); + return isa<StackSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS); } bool MemRegion::hasGlobalsStorage() const { - if (const MemSpaceRegion *MS = getMemorySpace()) - return MS == getMemRegionManager()->getGlobalsRegion(); - - return false; + return isa<GlobalsSpaceRegion>(getMemorySpace()); } bool MemRegion::hasParametersStorage() const { - if (const MemSpaceRegion *MS = getMemorySpace()) - return MS == getMemRegionManager()->getStackArgumentsRegion(); - - return false; + return isa<StackArgumentsSpaceRegion>(getMemorySpace()); } bool MemRegion::hasGlobalsOrParametersStorage() const { - if (const MemSpaceRegion *MS = getMemorySpace()) { - MemRegionManager *Mgr = getMemRegionManager(); - return MS == Mgr->getGlobalsRegion() - || MS == Mgr->getStackArgumentsRegion(); - } - return false; + const MemSpaceRegion *MS = getMemorySpace(); + return isa<StackArgumentsSpaceRegion>(MS) || + isa<GlobalsSpaceRegion>(MS); } // getBaseRegion strips away all elements and fields, and get the base region @@ -543,7 +708,7 @@ void BlockDataRegion::LazyInitializeReferencedVars() { if (ReferencedVars) return; - AnalysisContext *AC = LC->getAnalysisContext(); + AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); AnalysisContext::referenced_decls_iterator I, E; llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); @@ -558,10 +723,25 @@ void BlockDataRegion::LazyInitializeReferencedVars() { typedef BumpVector<const MemRegion*> VarVec; VarVec *BV = (VarVec*) A.Allocate<VarVec>(); - new (BV) VarVec(BC, (E - I) / sizeof(*I)); + new (BV) VarVec(BC, E - I); - for ( ; I != E; ++I) - BV->push_back(MemMgr.getVarRegion(*I, LC), BC); + for ( ; I != E; ++I) { + const VarDecl *VD = *I; + const VarRegion *VR = 0; + + if (!VD->getAttr<BlocksAttr>()) + VR = MemMgr.getVarRegion(VD, this); + else { + if (LC) + VR = MemMgr.getVarRegion(VD, LC); + else { + VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + } + } + + assert(VR); + BV->push_back(VR, BC); + } ReferencedVars = BV; } @@ -573,7 +753,8 @@ BlockDataRegion::referenced_vars_begin() const { BumpVector<const MemRegion*> *Vec = static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - return Vec == (void*) 0x1 ? NULL : Vec->begin(); + return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? + NULL : Vec->begin()); } BlockDataRegion::referenced_vars_iterator @@ -583,5 +764,6 @@ BlockDataRegion::referenced_vars_end() const { BumpVector<const MemRegion*> *Vec = static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - return Vec == (void*) 0x1 ? NULL : Vec->end(); + return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? + NULL : Vec->end()); } diff --git a/lib/Analysis/NoReturnFunctionChecker.cpp b/lib/Analysis/NoReturnFunctionChecker.cpp new file mode 100644 index 0000000..6806273 --- /dev/null +++ b/lib/Analysis/NoReturnFunctionChecker.cpp @@ -0,0 +1,80 @@ +//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NoReturnFunctionChecker, which evaluates functions that do not +// return to the caller. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/Checker.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +namespace { + +class NoReturnFunctionChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void clang::RegisterNoReturnFunctionChecker(GRExprEngine &Eng) { + Eng.registerCheck(new NoReturnFunctionChecker()); +} + +bool NoReturnFunctionChecker::EvalCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + bool BuildSinks = false; + + if (FD->getAttr<NoReturnAttr>() || FD->getAttr<AnalyzerNoReturnAttr>()) + BuildSinks = true; + else { + // HACK: Some functions are not marked noreturn, and don't return. + // Here are a few hardwired ones. If this takes too long, we can + // potentially cache these results. + using llvm::StringRef; + BuildSinks + = llvm::StringSwitch<bool>(StringRef(FD->getIdentifier()->getName())) + .Case("exit", true) + .Case("panic", true) + .Case("error", true) + .Case("Assert", true) + // FIXME: This is just a wrapper around throwing an exception. + // Eventually inter-procedural analysis should handle this easily. + .Case("ziperr", true) + .Case("assfail", true) + .Case("db_error", true) + .Case("__assert", true) + .Case("__assert_rtn", true) + .Case("__assert_fail", true) + .Case("dtrace_assfail", true) + .Case("yy_fatal_error", true) + .Case("_XCAssertionFailureHandler", true) + .Case("_DTAssertionFailureHandler", true) + .Case("_TSAssertionFailureHandler", true) + .Default(false); + } + + if (!BuildSinks) + return false; + + C.GenerateSink(CE); + return true; +} diff --git a/lib/Analysis/OSAtomicChecker.cpp b/lib/Analysis/OSAtomicChecker.cpp new file mode 100644 index 0000000..5a89345 --- /dev/null +++ b/lib/Analysis/OSAtomicChecker.cpp @@ -0,0 +1,196 @@ +//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates OSAtomic functions. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +namespace { + +class OSAtomicChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); + +private: + bool EvalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); +}; + +} + +void clang::RegisterOSAtomicChecker(GRExprEngine &Eng) { + Eng.registerCheck(new OSAtomicChecker()); +} + +bool OSAtomicChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl* FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + const char *FName = FD->getNameAsCString(); + + // Check for compare and swap. + if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 || + strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0) + return EvalOSAtomicCompareAndSwap(C, CE); + + // FIXME: Other atomics. + return false; +} + +bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, + const CallExpr *CE) { + // Not enough arguments to match OSAtomicCompareAndSwap? + if (CE->getNumArgs() != 3) + return false; + + ASTContext &Ctx = C.getASTContext(); + const Expr *oldValueExpr = CE->getArg(0); + QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); + + const Expr *newValueExpr = CE->getArg(1); + QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); + + // Do the types of 'oldValue' and 'newValue' match? + if (oldValueType != newValueType) + return false; + + const Expr *theValueExpr = CE->getArg(2); + const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>(); + + // theValueType not a pointer? + if (!theValueType) + return false; + + QualType theValueTypePointee = + Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); + + // The pointee must match newValueType and oldValueType. + if (theValueTypePointee != newValueType) + return false; + + static unsigned magic_load = 0; + static unsigned magic_store = 0; + + const void *OSAtomicLoadTag = &magic_load; + const void *OSAtomicStoreTag = &magic_store; + + // Load 'theValue'. + GRExprEngine &Engine = C.getEngine(); + const GRState *state = C.getState(); + ExplodedNodeSet Tmp; + SVal location = state->getSVal(theValueExpr); + // Here we should use the value type of the region as the load type. + QualType LoadTy; + if (const MemRegion *R = location.getAsRegion()) { + // We must be careful, as SymbolicRegions aren't typed. + const MemRegion *strippedR = R->StripCasts(); + // FIXME: This isn't quite the right solution. One test case in 'test/Analysis/NSString.m' + // is giving the wrong result. + const TypedRegion *typedR = + isa<SymbolicRegion>(strippedR) ? cast<TypedRegion>(R) : + dyn_cast<TypedRegion>(strippedR); + + if (typedR) { + LoadTy = typedR->getValueType(Ctx); + location = loc::MemRegionVal(typedR); + } + } + Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(), + state, location, OSAtomicLoadTag, LoadTy); + + if (Tmp.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode *N = *I; + const GRState *stateLoad = N->getState(); + SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); + SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); + + // FIXME: Issue an error. + if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { + return false; + } + + DefinedOrUnknownSVal theValueVal = + cast<DefinedOrUnknownSVal>(theValueVal_untested); + DefinedOrUnknownSVal oldValueVal = + cast<DefinedOrUnknownSVal>(oldValueVal_untested); + + SValuator &SVator = Engine.getSValuator(); + + // Perform the comparison. + DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal); + + const GRState *stateEqual = stateLoad->Assume(Cmp, true); + + // Were they equal? + if (stateEqual) { + // Perform the store. + ExplodedNodeSet TmpStore; + SVal val = stateEqual->getSVal(newValueExpr); + + // Handle implicit value casts. + if (const TypedRegion *R = + dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx), + newValueExpr->getType()); + } + + Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N, + stateEqual, location, val, OSAtomicStoreTag); + + if (TmpStore.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + // Now bind the result of the comparison. + for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), + E2 = TmpStore.end(); I2 != E2; ++I2) { + ExplodedNode *predNew = *I2; + const GRState *stateNew = predNew->getState(); + SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType()); + C.GenerateNode(stateNew->BindExpr(CE, Res), predNew); + } + } + + // Were they not equal? + if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) { + SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); + C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N); + } + } + + return true; +} diff --git a/lib/Analysis/PathDiagnostic.cpp b/lib/Analysis/PathDiagnostic.cpp index 800496a..734570a 100644 --- a/lib/Analysis/PathDiagnostic.cpp +++ b/lib/Analysis/PathDiagnostic.cpp @@ -36,30 +36,16 @@ bool PathDiagnosticMacroPiece::containsEvent() const { return false; } -static size_t GetNumCharsToLastNonPeriod(const char *s) { - const char *start = s; - const char *lastNonPeriod = 0; - - for ( ; *s != '\0' ; ++s) - if (*s != '.') lastNonPeriod = s; - - if (!lastNonPeriod) - return 0; - - return (lastNonPeriod - start) + 1; -} - -static inline size_t GetNumCharsToLastNonPeriod(const std::string &s) { - return s.empty () ? 0 : GetNumCharsToLastNonPeriod(&s[0]); +static llvm::StringRef StripTrailingDots(llvm::StringRef s) { + for (llvm::StringRef::size_type i = s.size(); i != 0; --i) + if (s[i - 1] != '.') + return s.substr(0, i); + return ""; } -PathDiagnosticPiece::PathDiagnosticPiece(const std::string& s, +PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s, Kind k, DisplayHint hint) - : str(s, 0, GetNumCharsToLastNonPeriod(s)), kind(k), Hint(hint) {} - -PathDiagnosticPiece::PathDiagnosticPiece(const char* s, Kind k, - DisplayHint hint) - : str(s, GetNumCharsToLastNonPeriod(s)), kind(k), Hint(hint) {} + : str(StripTrailingDots(s)), kind(k), Hint(hint) {} PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) : kind(k), Hint(hint) {} @@ -89,20 +75,12 @@ void PathDiagnostic::resetPath(bool deletePieces) { } -PathDiagnostic::PathDiagnostic(const char* bugtype, const char* desc, - const char* category) - : Size(0), - BugType(bugtype, GetNumCharsToLastNonPeriod(bugtype)), - Desc(desc, GetNumCharsToLastNonPeriod(desc)), - Category(category, GetNumCharsToLastNonPeriod(category)) {} - -PathDiagnostic::PathDiagnostic(const std::string& bugtype, - const std::string& desc, - const std::string& category) +PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, + llvm::StringRef category) : Size(0), - BugType(bugtype, 0, GetNumCharsToLastNonPeriod(bugtype)), - Desc(desc, 0, GetNumCharsToLastNonPeriod(desc)), - Category(category, 0, GetNumCharsToLastNonPeriod(category)) {} + BugType(StripTrailingDots(bugtype)), + Desc(StripTrailingDots(desc)), + Category(StripTrailingDots(category)) {} void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info) { @@ -126,8 +104,7 @@ void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, Info.FormatDiagnostic(StrC); PathDiagnosticPiece *P = - new PathDiagnosticEventPiece(Info.getLocation(), - std::string(StrC.begin(), StrC.end())); + new PathDiagnosticEventPiece(Info.getLocation(), StrC.str()); for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) P->addRange(Info.getRange(i)); diff --git a/lib/Analysis/RegionStore.cpp b/lib/Analysis/RegionStore.cpp index e645172..b5eeb1e 100644 --- a/lib/Analysis/RegionStore.cpp +++ b/lib/Analysis/RegionStore.cpp @@ -269,7 +269,15 @@ public: const GRState *InvalidateRegion(const GRState *state, const MemRegion *R, const Expr *E, unsigned Count, - InvalidatedSymbols *IS); + InvalidatedSymbols *IS) { + return RegionStoreManager::InvalidateRegions(state, &R, &R+1, E, Count, IS); + } + + const GRState *InvalidateRegions(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS); private: void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, @@ -279,7 +287,9 @@ public: const GRState *Bind(const GRState *state, Loc LV, SVal V); const GRState *BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr* CL, SVal V); + const CompoundLiteralExpr* CL, + const LocationContext *LC, + SVal V); const GRState *BindDecl(const GRState *ST, const VarRegion *VR, SVal InitVal); @@ -460,16 +470,14 @@ void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, B = RBFactory.Remove(B, R); } -const GRState *RegionStoreManager::InvalidateRegion(const GRState *state, - const MemRegion *R, - const Expr *Ex, - unsigned Count, - InvalidatedSymbols *IS) { +const GRState *RegionStoreManager::InvalidateRegions(const GRState *state, + const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, + unsigned Count, + InvalidatedSymbols *IS) { ASTContext& Ctx = StateMgr.getContext(); - // Strip away casts. - R = R->StripCasts(); - // Get the mapping of regions -> subregions. llvm::OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(state->getStore())); @@ -478,10 +486,14 @@ const GRState *RegionStoreManager::InvalidateRegion(const GRState *state, llvm::DenseMap<const MemRegion *, unsigned> Visited; llvm::SmallVector<const MemRegion *, 10> WorkList; - WorkList.push_back(R); + + for ( ; I != E; ++I) { + // Strip away casts. + WorkList.push_back((*I)->StripCasts()); + } while (!WorkList.empty()) { - R = WorkList.back(); + const MemRegion *R = WorkList.back(); WorkList.pop_back(); // Have we visited this region before? @@ -512,6 +524,19 @@ const GRState *RegionStoreManager::InvalidateRegion(const GRState *state, if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) IS->insert(SR->getSymbol()); } + + // BlockDataRegion? If so, invalidate captured variables that are passed + // by reference. + if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + for (BlockDataRegion::referenced_vars_iterator + I = BR->referenced_vars_begin(), E = BR->referenced_vars_end() ; + I != E; ++I) { + const VarRegion *VR = *I; + if (VR->getDecl()->getAttr<BlocksAttr>()) + WorkList.push_back(VR); + } + continue; + } // Handle the region itself. if (isa<AllocaRegion>(R) || isa<SymbolicRegion>(R) || @@ -592,15 +617,6 @@ SVal RegionStoreManager::getLValueVar(const VarDecl *VD, return loc::MemRegionVal(MRMgr.getVarRegion(VD, LC)); } -/// getLValueCompoundLiteral - Returns an SVal representing the lvalue -/// of a compound literal. Within RegionStore a compound literal -/// has an associated region, and the lvalue of the compound literal -/// is the lvalue of that region. -SVal -RegionStoreManager::getLValueCompoundLiteral(const CompoundLiteralExpr* CL) { - return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL)); -} - SVal RegionStoreManager::getLValueIvar(const ObjCIvarDecl* D, SVal Base) { return getLValueFieldOrIvar(D, Base); } @@ -707,7 +723,12 @@ DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, const MemRegion *R) { switch (R->getKind()) { - case MemRegion::MemSpaceRegionKind: + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::GlobalsSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: assert(0 && "Cannot index into a MemSpace"); return UnknownVal(); @@ -752,13 +773,6 @@ DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, // essentially are arrays of size 1. return ValMgr.makeIntVal(1, false); } - - case MemRegion::BEG_DECL_REGIONS: - case MemRegion::END_DECL_REGIONS: - case MemRegion::BEG_TYPED_REGIONS: - case MemRegion::END_TYPED_REGIONS: - assert(0 && "Infeasible region"); - return UnknownVal(); } assert(0 && "Unreachable"); @@ -797,9 +811,8 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { T = AT->getElementType(); SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ElementRegion* ER = MRMgr.getElementRegion(T, ZeroIdx, ArrayR, getContext()); - - return loc::MemRegionVal(ER); + return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, + getContext())); } //===----------------------------------------------------------------------===// @@ -864,16 +877,14 @@ SVal RegionStoreManager::EvalBinOp(const GRState *state, // Technically this can happen if people do funny things with casts. return UnknownVal(); - case MemRegion::MemSpaceRegionKind: + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::GlobalsSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: assert(0 && "Cannot perform pointer arithmetic on a MemSpace"); return UnknownVal(); - - case MemRegion::BEG_DECL_REGIONS: - case MemRegion::END_DECL_REGIONS: - case MemRegion::BEG_TYPED_REGIONS: - case MemRegion::END_TYPED_REGIONS: - assert(0 && "Infeasible region"); - return UnknownVal(); } SVal Idx = ER->getIndex(); @@ -1283,7 +1294,8 @@ SVal RegionStoreManager::RetrieveVar(const GRState *state, // Lazily derive a value for the VarRegion. const VarDecl *VD = R->getDecl(); - if (R->hasGlobalsOrParametersStorage()) + if (R->hasGlobalsOrParametersStorage() || + isa<UnknownSpaceRegion>(R->getMemorySpace())) return ValMgr.getRegionValueSymbolValOrUnknown(R, VD->getType()); return UndefinedVal(); @@ -1440,11 +1452,11 @@ const GRState *RegionStoreManager::BindDecl(const GRState *ST, // FIXME: this method should be merged into Bind(). const GRState * RegionStoreManager::BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr* CL, + const CompoundLiteralExpr *CL, + const LocationContext *LC, SVal V) { - - CompoundLiteralRegion* R = MRMgr.getCompoundLiteralRegion(CL); - return Bind(state, loc::MemRegionVal(R), V); + return Bind(state, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), + V); } const GRState *RegionStoreManager::setImplicitDefaultValue(const GRState *state, @@ -1497,8 +1509,8 @@ const GRState *RegionStoreManager::BindArray(const GRState *state, break; SVal Idx = ValMgr.makeArrayIndex(i); - ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, - getContext()); + const ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, + getContext()); SVal V = ValMgr.makeIntVal(str[j], sizeof(char)*8, true); state = Bind(state, loc::MemRegionVal(ER), V); @@ -1526,7 +1538,7 @@ const GRState *RegionStoreManager::BindArray(const GRState *state, break; SVal Idx = ValMgr.makeArrayIndex(i); - ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext()); + const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext()); if (CAT->getElementType()->isStructureType()) state = BindStruct(state, ER, *VI); @@ -1677,7 +1689,7 @@ void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, IntermediateVisited.insert(R); if (const VarRegion* VR = dyn_cast<VarRegion>(R)) { - if (SymReaper.isLive(Loc, VR->getDecl())) + if (SymReaper.isLive(Loc, VR)) WorkList.push_back(std::make_pair(&state, VR)); continue; } @@ -1753,14 +1765,16 @@ tryAgain: if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) SymReaper.markLive(SymR->getSymbol()); - // For BlockDataRegions, enqueue all VarRegions for that are referenced + // 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) - WorkList.push_back(std::make_pair(state_N, *RI)); - + RI != RE; ++RI) { + if ((*RI)->getDecl()->getAttr<BlocksAttr>()) + WorkList.push_back(std::make_pair(state_N, *RI)); + } // No possible data bindings on a BlockDataRegion. Continue to the // next region in the worklist. continue; @@ -1846,8 +1860,7 @@ GRState const *RegionStoreManager::EnterStackFrame(GRState const *state, // Copy the arg expression value to the arg variables. for (; AI != AE; ++AI, ++PI) { SVal ArgVal = state->getSVal(*AI); - MemRegion *R = MRMgr.getVarRegion(*PI, frame); - state = Bind(state, ValMgr.makeLoc(R), ArgVal); + state = Bind(state, ValMgr.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal); } return state; diff --git a/lib/Analysis/Store.cpp b/lib/Analysis/Store.cpp index 2fd573c..e6ff6e5 100644 --- a/lib/Analysis/Store.cpp +++ b/lib/Analysis/Store.cpp @@ -77,11 +77,12 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) // Process region cast according to the kind of the region being cast. switch (R->getKind()) { - case MemRegion::BEG_TYPED_REGIONS: - case MemRegion::MemSpaceRegionKind: - case MemRegion::BEG_DECL_REGIONS: - case MemRegion::END_DECL_REGIONS: - case MemRegion::END_TYPED_REGIONS: { + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + case MemRegion::GlobalsSpaceRegionKind: { assert(0 && "Invalid region cast"); break; } @@ -199,9 +200,33 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, QualType castTy) { if (castTy.isNull()) return V; - + assert(ValMgr.getContext().hasSameUnqualifiedType(castTy, R->getValueType(ValMgr.getContext()))); return V; } +const GRState *StoreManager::InvalidateRegions(const GRState *state, + const MemRegion * const *I, + const MemRegion * const *End, + const Expr *E, + unsigned Count, + InvalidatedSymbols *IS) { + for ( ; I != End ; ++I) + state = InvalidateRegion(state, *I, E, Count, IS); + + return state; +} + +//===----------------------------------------------------------------------===// +// Common getLValueXXX methods. +//===----------------------------------------------------------------------===// + +/// getLValueCompoundLiteral - Returns an SVal representing the lvalue +/// of a compound literal. Within RegionStore a compound literal +/// has an associated region, and the lvalue of the compound literal +/// is the lvalue of that region. +SVal StoreManager::getLValueCompoundLiteral(const CompoundLiteralExpr* CL, + const LocationContext *LC) { + return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)); +} diff --git a/lib/Analysis/SymbolManager.cpp b/lib/Analysis/SymbolManager.cpp index 22e1101..3fe36b0 100644 --- a/lib/Analysis/SymbolManager.cpp +++ b/lib/Analysis/SymbolManager.cpp @@ -220,4 +220,9 @@ bool SymbolReaper::isLive(SymbolRef sym) { return isa<SymbolRegionValue>(sym); } +bool SymbolReaper::isLive(const Stmt *Loc, const VarRegion *VR) const { + const StackFrameContext *SFC = VR->getStackFrame(); + return SFC == CurrentStackFrame ? Liveness.isLive(Loc, VR->getDecl()) : true; +} + SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/Analysis/ValueManager.cpp b/lib/Analysis/ValueManager.cpp index 22a8211..d091373 100644 --- a/lib/Analysis/ValueManager.cpp +++ b/lib/Analysis/ValueManager.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/PathSensitive/ValueManager.h" +#include "clang/Analysis/PathSensitive/AnalysisContext.h" using namespace clang; using namespace llvm; @@ -138,15 +139,15 @@ ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, } DefinedSVal ValueManager::getFunctionPointer(const FunctionDecl* FD) { - CodeTextRegion *R = MemMgr.getFunctionTextRegion(FD); - return loc::MemRegionVal(R); + return loc::MemRegionVal(MemMgr.getFunctionTextRegion(FD)); } DefinedSVal ValueManager::getBlockPointer(const BlockDecl *D, CanQualType locTy, const LocationContext *LC) { - BlockTextRegion *BC = MemMgr.getBlockTextRegion(D, locTy); - BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); + const BlockTextRegion *BC = + MemMgr.getBlockTextRegion(D, locTy, LC->getAnalysisContext()); + const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); return loc::MemRegionVal(BD); } |