From: Ted Kremenek Date: Sat, 11 Apr 2009 00:11:10 +0000 (+0000) Subject: Implement analyzer support for OSCompareAndSwap. This required pushing "tagged" X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1670e403c48f3af4fceff3f6773a0e1cfc6c4eb3;p=clang Implement analyzer support for OSCompareAndSwap. This required pushing "tagged" ProgramPoints all the way through to GRCoreEngine. NSString.m now fails with RegionStoreManager because of the void** cast. Disabling use of region store for that test for now. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68845 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/PathDiagnostic.h b/include/clang/Analysis/PathDiagnostic.h index 33adcc99ad..8d53cdb679 100644 --- a/include/clang/Analysis/PathDiagnostic.h +++ b/include/clang/Analysis/PathDiagnostic.h @@ -96,6 +96,8 @@ public: bool isValid() const { return SM != 0; } + + const SourceManager& getSourceManager() const { assert(isValid());return *SM;} FullSourceLoc asLocation() const; SourceRange asRange() const; diff --git a/include/clang/Analysis/PathSensitive/BasicValueFactory.h b/include/clang/Analysis/PathSensitive/BasicValueFactory.h index 553f8a31bc..b694e9b299 100644 --- a/include/clang/Analysis/PathSensitive/BasicValueFactory.h +++ b/include/clang/Analysis/PathSensitive/BasicValueFactory.h @@ -126,8 +126,12 @@ public: return getValue(0, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); } + inline const llvm::APSInt& getTruthValue(bool b, QualType T) { + return getValue(b ? 1 : 0, Ctx.getTypeSize(T), false); + } + inline const llvm::APSInt& getTruthValue(bool b) { - return getValue(b ? 1 : 0, Ctx.getTypeSize(Ctx.IntTy), false); + return getTruthValue(b, Ctx.IntTy); } const CompoundValData* getCompoundValData(QualType T, diff --git a/include/clang/Analysis/PathSensitive/GRCoreEngine.h b/include/clang/Analysis/PathSensitive/GRCoreEngine.h index 5bd76a034b..fe8634edad 100644 --- a/include/clang/Analysis/PathSensitive/GRCoreEngine.h +++ b/include/clang/Analysis/PathSensitive/GRCoreEngine.h @@ -146,14 +146,23 @@ public: ExplodedNodeImpl* generateNodeImpl(Stmt* S, const void* State, ExplodedNodeImpl* Pred, - ProgramPoint::Kind K = ProgramPoint::PostStmtKind); + ProgramPoint::Kind K = ProgramPoint::PostStmtKind, + const void *tag = 0); ExplodedNodeImpl* generateNodeImpl(Stmt* S, const void* State, - ProgramPoint::Kind K = ProgramPoint::PostStmtKind) { + ProgramPoint::Kind K = ProgramPoint::PostStmtKind, + const void *tag = 0) { ExplodedNodeImpl* N = getLastNode(); assert (N && "Predecessor of new node is infeasible."); - return generateNodeImpl(S, State, N, K); + return generateNodeImpl(S, State, N, K, tag); + } + + ExplodedNodeImpl* + generateNodeImpl(Stmt* S, const void* State, const void *tag = 0) { + ExplodedNodeImpl* N = getLastNode(); + assert (N && "Predecessor of new node is infeasible."); + return generateNodeImpl(S, State, N, ProgramPoint::PostStmtKind, tag); } /// getStmt - Return the current block-level expression associated with @@ -183,7 +192,7 @@ public: GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb, StateManagerTy& mgr) : NB(nb), Mgr(mgr), Auditor(0), PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), - PointKind(ProgramPoint::PostStmtKind) { + PointKind(ProgramPoint::PostStmtKind), Tag(0) { CleanedState = getLastNode()->getState(); } @@ -204,7 +213,7 @@ public: ProgramPoint::Kind K) { HasGeneratedNode = true; if (PurgingDeadSymbols) K = ProgramPoint::PostPurgeDeadSymbolsKind; - return static_cast(NB.generateNodeImpl(S, St, Pred, K)); + return static_cast(NB.generateNodeImpl(S, St, Pred, K, Tag)); } NodeTy* generateNode(Stmt* S, const StateTy* St, NodeTy* Pred) { @@ -214,7 +223,7 @@ public: NodeTy* generateNode(Stmt* S, const StateTy* St, ProgramPoint::Kind K) { HasGeneratedNode = true; if (PurgingDeadSymbols) K = ProgramPoint::PostPurgeDeadSymbolsKind; - return static_cast(NB.generateNodeImpl(S, St, K)); + return static_cast(NB.generateNodeImpl(S, St, K, Tag)); } NodeTy* generateNode(Stmt* S, const StateTy* St) { @@ -286,6 +295,7 @@ public: bool BuildSinks; bool HasGeneratedNode; ProgramPoint::Kind PointKind; + const void *Tag; }; class GRBranchNodeBuilderImpl { diff --git a/include/clang/Analysis/PathSensitive/GRExprEngine.h b/include/clang/Analysis/PathSensitive/GRExprEngine.h index 29acc707da..bc036b6a19 100644 --- a/include/clang/Analysis/PathSensitive/GRExprEngine.h +++ b/include/clang/Analysis/PathSensitive/GRExprEngine.h @@ -538,11 +538,11 @@ protected: return StateMgr.AssumeInBound(St, Idx, UpperBound, Assumption, isFeasible); } +public: NodeTy* MakeNode(NodeSet& Dst, Stmt* S, NodeTy* Pred, const GRState* St, - ProgramPoint::Kind K = ProgramPoint::PostStmtKind) { - assert (Builder && "GRStmtNodeBuilder not present."); - return Builder->MakeNode(Dst, S, Pred, St, K); - } + ProgramPoint::Kind K = ProgramPoint::PostStmtKind, + const void *tag = 0); +protected: /// Visit - Transfer function logic for all statements. Dispatches to /// other functions that handle specific kinds of statements. @@ -673,6 +673,8 @@ protected: return X.isValid() ? getTF().EvalComplement(*this, cast(X)) : X; } +public: + SVal EvalBinOp(BinaryOperator::Opcode Op, NonLoc L, NonLoc R, QualType T) { return R.isValid() ? getTF().DetermEvalBinOpNN(*this, Op, L, R, T) : R; @@ -692,40 +694,41 @@ protected: SVal EvalBinOp(BinaryOperator::Opcode Op, SVal L, SVal R, QualType T); - void EvalCall(NodeSet& Dst, CallExpr* CE, SVal L, NodeTy* Pred) { - assert (Builder && "GRStmtNodeBuilder must be defined."); - getTF().EvalCall(Dst, *this, *Builder, CE, L, Pred); - } +protected: + + void EvalCall(NodeSet& Dst, CallExpr* CE, SVal L, NodeTy* Pred); void EvalObjCMessageExpr(NodeSet& Dst, ObjCMessageExpr* ME, NodeTy* Pred) { assert (Builder && "GRStmtNodeBuilder must be defined."); getTF().EvalObjCMessageExpr(Dst, *this, *Builder, ME, Pred); } + + void EvalReturn(NodeSet& Dst, ReturnStmt* s, NodeTy* Pred); + + const GRState* MarkBranch(const GRState* St, Stmt* Terminator, + bool branchTaken); /// EvalBind - Handle the semantics of binding a value to a specific location. /// This method is used by EvalStore, VisitDeclStmt, and others. void EvalBind(NodeSet& Dst, Expr* Ex, NodeTy* Pred, const GRState* St, SVal location, SVal Val); - void EvalStore(NodeSet& Dst, Expr* E, NodeTy* Pred, const GRState* St, - SVal TargetLV, SVal Val); - - void EvalStore(NodeSet& Dst, Expr* E, Expr* StoreE, NodeTy* Pred, - const GRState* St, SVal TargetLV, SVal Val); - - // FIXME: The "CheckOnly" option exists only because Array and Field - // loads aren't fully implemented. Eventually this option will go away. - +public: void EvalLoad(NodeSet& Dst, Expr* Ex, NodeTy* Pred, - const GRState* St, SVal location); + const GRState* St, SVal location, const void *tag = 0); NodeTy* EvalLocation(Stmt* Ex, NodeTy* Pred, - const GRState* St, SVal location); + const GRState* St, SVal location, + const void *tag = 0); + - void EvalReturn(NodeSet& Dst, ReturnStmt* s, NodeTy* Pred); + void EvalStore(NodeSet& Dst, Expr* E, NodeTy* Pred, const GRState* St, + SVal TargetLV, SVal Val, const void *tag = 0); + + void EvalStore(NodeSet& Dst, Expr* E, Expr* StoreE, NodeTy* Pred, + const GRState* St, SVal TargetLV, SVal Val, + const void *tag = 0); - const GRState* MarkBranch(const GRState* St, Stmt* Terminator, - bool branchTaken); }; } // end clang namespace diff --git a/include/clang/Analysis/PathSensitive/GRExprEngineBuilders.h b/include/clang/Analysis/PathSensitive/GRExprEngineBuilders.h index 074fb2233d..6c23745de2 100644 --- a/include/clang/Analysis/PathSensitive/GRExprEngineBuilders.h +++ b/include/clang/Analysis/PathSensitive/GRExprEngineBuilders.h @@ -52,6 +52,7 @@ class GRStmtNodeBuilderRef { const unsigned OldSize; const bool AutoCreateNode; SaveAndRestore OldSink; + SaveAndRestore OldTag; SaveOr OldHasGen; private: @@ -68,7 +69,7 @@ private: const Stmt* s, bool auto_create_node) : Dst(dst), B(builder), Eng(eng), Pred(pred), state(st), stmt(s), OldSize(Dst.size()), AutoCreateNode(auto_create_node), - OldSink(B.BuildSinks), OldHasGen(B.HasGeneratedNode) {} + OldSink(B.BuildSinks), OldTag(B.Tag), OldHasGen(B.HasGeneratedNode) {} public: diff --git a/include/clang/Analysis/PathSensitive/MemRegion.h b/include/clang/Analysis/PathSensitive/MemRegion.h index 4f720d2522..af63386136 100644 --- a/include/clang/Analysis/PathSensitive/MemRegion.h +++ b/include/clang/Analysis/PathSensitive/MemRegion.h @@ -320,6 +320,8 @@ public: static bool classof(const MemRegion* R) { return R->getKind() == TypedViewRegionKind; } + + const MemRegion *removeViews() const; }; diff --git a/include/clang/Analysis/PathSensitive/ValueManager.h b/include/clang/Analysis/PathSensitive/ValueManager.h index aeabec80de..9842983b2c 100644 --- a/include/clang/Analysis/PathSensitive/ValueManager.h +++ b/include/clang/Analysis/PathSensitive/ValueManager.h @@ -91,7 +91,9 @@ public: const llvm::APSInt& rhs, QualType T); NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const SymExpr *rhs, QualType T); + const SymExpr *rhs, QualType T); + + NonLoc makeTruthVal(bool b, QualType T); }; } // end clang namespace #endif diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h index 7c73a1f25d..1587371961 100644 --- a/include/clang/Analysis/ProgramPoint.h +++ b/include/clang/Analysis/ProgramPoint.h @@ -57,9 +57,9 @@ protected: : Data(reinterpret_cast(P1) | TwoPointers, reinterpret_cast(P2)), Tag(tag) {} - ProgramPoint(const void* P1, const void* P2, bool) + ProgramPoint(const void* P1, const void* P2, bool, const void *tag = 0) : Data(reinterpret_cast(P1) | Custom, - reinterpret_cast(P2)), Tag(0) {} + reinterpret_cast(P2)), Tag(tag) {} protected: void* getData1NoMask() const { @@ -173,10 +173,13 @@ protected: PostStmt(const Stmt* S, Kind k,const void *tag = 0) : ProgramPoint(S, k, tag) {} - PostStmt(const Stmt* S, const void* data) : ProgramPoint(S, data, true) {} + PostStmt(const Stmt* S, const void* data, bool, const void *tag =0) + : ProgramPoint(S, data, true, tag) {} public: - PostStmt(const Stmt* S) : ProgramPoint(S, PostStmtKind) {} + PostStmt(const Stmt* S, const void *tag = 0) + : ProgramPoint(S, PostStmtKind, tag) {} + Stmt* getStmt() const { return (Stmt*) getData1(); } @@ -200,7 +203,7 @@ class PostStmtCustom : public PostStmt { public: PostStmtCustom(const Stmt* S, const std::pair* TaggedData) - : PostStmt(S, TaggedData) { + : PostStmt(S, TaggedData, true) { assert(getKind() == PostStmtCustomKind); } @@ -219,8 +222,8 @@ public: class PostOutOfBoundsCheckFailed : public PostStmt { public: - PostOutOfBoundsCheckFailed(const Stmt* S) - : PostStmt(S, PostOutOfBoundsCheckFailedKind) {} + PostOutOfBoundsCheckFailed(const Stmt* S, const void *tag = 0) + : PostStmt(S, PostOutOfBoundsCheckFailedKind, tag) {} static bool classof(const ProgramPoint* Location) { return Location->getKind() == PostOutOfBoundsCheckFailedKind; @@ -229,8 +232,8 @@ public: class PostUndefLocationCheckFailed : public PostStmt { public: - PostUndefLocationCheckFailed(const Stmt* S) - : PostStmt(S, PostUndefLocationCheckFailedKind) {} + PostUndefLocationCheckFailed(const Stmt* S, const void *tag = 0) + : PostStmt(S, PostUndefLocationCheckFailedKind, tag) {} static bool classof(const ProgramPoint* Location) { return Location->getKind() == PostUndefLocationCheckFailedKind; @@ -239,8 +242,8 @@ public: class PostNullCheckFailed : public PostStmt { public: - PostNullCheckFailed(const Stmt* S) - : PostStmt(S, PostNullCheckFailedKind) {} + PostNullCheckFailed(const Stmt* S, const void *tag = 0) + : PostStmt(S, PostNullCheckFailedKind, tag) {} static bool classof(const ProgramPoint* Location) { return Location->getKind() == PostNullCheckFailedKind; @@ -269,7 +272,8 @@ public: class PostPurgeDeadSymbols : public PostStmt { public: - PostPurgeDeadSymbols(const Stmt* S) : PostStmt(S, PostPurgeDeadSymbolsKind) {} + PostPurgeDeadSymbols(const Stmt* S, const void *tag = 0) + : PostStmt(S, PostPurgeDeadSymbolsKind, tag) {} static bool classof(const ProgramPoint* Location) { return Location->getKind() == PostPurgeDeadSymbolsKind; diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp index 41776edbbb..566c1971b7 100644 --- a/lib/Analysis/BasicStore.cpp +++ b/lib/Analysis/BasicStore.cpp @@ -282,6 +282,23 @@ SVal BasicStoreManager::getLValueElement(const GRState* St, SVal Base, return UnknownVal(); } +static bool isHigherOrderVoidPtr(QualType T, ASTContext &C) { + bool foundPointer = false; + while (1) { + const PointerType *PT = T->getAsPointerType(); + if (!PT) { + if (!foundPointer) + return false; + + QualType X = C.getCanonicalType(T).getUnqualifiedType(); + return X == C.VoidTy; + } + + foundPointer = true; + T = PT->getPointeeType(); + } +} + SVal BasicStoreManager::Retrieve(const GRState* state, Loc loc, QualType T) { if (isa(loc)) @@ -294,6 +311,20 @@ SVal BasicStoreManager::Retrieve(const GRState* state, Loc loc, QualType T) { case loc::MemRegionKind: { const MemRegion* R = cast(loc).getRegion(); + if (const TypedViewRegion *TR = dyn_cast(R)) { + // Just support void**, void***, etc., for now. This is needed + // to handle OSCompareAndSwapPtr(). + ASTContext &Ctx = StateMgr.getContext(); + QualType T = TR->getLValueType(Ctx); + + if (!isHigherOrderVoidPtr(T, Ctx)) + return UnknownVal(); + + // Otherwise, strip the views. + // FIXME: Should we layer a TypedView on the result? + R = TR->removeViews(); + } + if (!(isa(R) || isa(R))) return UnknownVal(); diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp index 28f1a317c3..e4f27b60cf 100644 --- a/lib/Analysis/GRCoreEngine.cpp +++ b/lib/Analysis/GRCoreEngine.cpp @@ -368,47 +368,49 @@ void GRStmtNodeBuilderImpl::GenerateAutoTransition(ExplodedNodeImpl* N) { Eng.WList->Enqueue(Succ, B, Idx+1); } -static inline PostStmt GetPostLoc(Stmt* S, ProgramPoint::Kind K) { +static inline PostStmt GetPostLoc(Stmt* S, ProgramPoint::Kind K, + const void *tag) { switch (K) { default: assert(false && "Invalid PostXXXKind."); case ProgramPoint::PostStmtKind: - return PostStmt(S); + return PostStmt(S, tag); case ProgramPoint::PostLoadKind: - return PostLoad(S); + return PostLoad(S, tag); case ProgramPoint::PostUndefLocationCheckFailedKind: - return PostUndefLocationCheckFailed(S); + return PostUndefLocationCheckFailed(S, tag); case ProgramPoint::PostLocationChecksSucceedKind: - return PostLocationChecksSucceed(S); + return PostLocationChecksSucceed(S, tag); case ProgramPoint::PostOutOfBoundsCheckFailedKind: - return PostOutOfBoundsCheckFailed(S); + return PostOutOfBoundsCheckFailed(S, tag); case ProgramPoint::PostNullCheckFailedKind: - return PostNullCheckFailed(S); + return PostNullCheckFailed(S, tag); case ProgramPoint::PostStoreKind: - return PostStore(S); + return PostStore(S, tag); case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S); + return PostPurgeDeadSymbols(S, tag); } } ExplodedNodeImpl* GRStmtNodeBuilderImpl::generateNodeImpl(Stmt* S, const void* State, ExplodedNodeImpl* Pred, - ProgramPoint::Kind K) { - return generateNodeImpl(GetPostLoc(S, K), State, Pred); + ProgramPoint::Kind K, + const void *tag) { + return generateNodeImpl(GetPostLoc(S, K, tag), State, Pred); } ExplodedNodeImpl* GRStmtNodeBuilderImpl::generateNodeImpl(PostStmt Loc, const void* State, - ExplodedNodeImpl* Pred) { + ExplodedNodeImpl* Pred) { bool IsNew; ExplodedNodeImpl* N = Eng.G->getNodeImpl(Loc, State, &IsNew); N->addPredecessor(Pred); diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 4fe6fd6b90..9f049d5b33 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -530,6 +530,22 @@ bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const GRState*, return BC.getNumVisited(B->getBlockID()) < 3; } +//===----------------------------------------------------------------------===// +// Generic node creation. +//===----------------------------------------------------------------------===// + +GRExprEngine::NodeTy* GRExprEngine::MakeNode(NodeSet& Dst, Stmt* S, + NodeTy* Pred, + const GRState* St, + ProgramPoint::Kind K, + const void *tag) { + + assert (Builder && "GRStmtNodeBuilder not present."); + SaveAndRestore OldTag(Builder->Tag); + Builder->Tag = tag; + return Builder->MakeNode(Dst, S, Pred, St, K); +} + //===----------------------------------------------------------------------===// // Branch processing. //===----------------------------------------------------------------------===// @@ -1069,12 +1085,13 @@ void GRExprEngine::EvalBind(NodeSet& Dst, Expr* Ex, NodeTy* Pred, /// @param location The location to store the value /// @param Val The value to be stored void GRExprEngine::EvalStore(NodeSet& Dst, Expr* Ex, NodeTy* Pred, - const GRState* state, SVal location, SVal Val) { + const GRState* state, SVal location, SVal Val, + const void *tag) { assert (Builder && "GRStmtNodeBuilder must be defined."); // Evaluate the location (checks for bad dereferences). - Pred = EvalLocation(Ex, Pred, state, location); + Pred = EvalLocation(Ex, Pred, state, location, tag); if (!Pred) return; @@ -1084,15 +1101,18 @@ void GRExprEngine::EvalStore(NodeSet& Dst, Expr* Ex, NodeTy* Pred, // Proceed with the store. SaveAndRestore OldSPointKind(Builder->PointKind); - Builder->PointKind = ProgramPoint::PostStoreKind; + SaveAndRestore OldTag(Builder->Tag); + Builder->PointKind = ProgramPoint::PostStoreKind; + Builder->Tag = tag; EvalBind(Dst, Ex, Pred, state, location, Val); } void GRExprEngine::EvalLoad(NodeSet& Dst, Expr* Ex, NodeTy* Pred, - const GRState* state, SVal location) { + const GRState* state, SVal location, + const void *tag) { // Evaluate the location (checks for bad dereferences). - Pred = EvalLocation(Ex, Pred, state, location); + Pred = EvalLocation(Ex, Pred, state, location, tag); if (!Pred) return; @@ -1107,27 +1127,32 @@ void GRExprEngine::EvalLoad(NodeSet& Dst, Expr* Ex, NodeTy* Pred, if (location.isUnknown()) { // This is important. We must nuke the old binding. - MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, UnknownVal()), K); + MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, UnknownVal()), K, tag); } else { SVal V = GetSVal(state, cast(location), Ex->getType()); - MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, V), K); + MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, V), K, tag); } } void GRExprEngine::EvalStore(NodeSet& Dst, Expr* Ex, Expr* StoreE, NodeTy* Pred, - const GRState* state, SVal location, SVal Val) { + const GRState* state, SVal location, SVal Val, + const void *tag) { NodeSet TmpDst; - EvalStore(TmpDst, StoreE, Pred, state, location, Val); + EvalStore(TmpDst, StoreE, Pred, state, location, Val, tag); for (NodeSet::iterator I=TmpDst.begin(), E=TmpDst.end(); I!=E; ++I) - MakeNode(Dst, Ex, *I, (*I)->getState()); + MakeNode(Dst, Ex, *I, (*I)->getState(), ProgramPoint::PostStmtKind, tag); } GRExprEngine::NodeTy* GRExprEngine::EvalLocation(Stmt* Ex, NodeTy* Pred, const GRState* state, - SVal location) { + SVal location, + const void *tag) { + + SaveAndRestore OldTag(Builder->Tag); + Builder->Tag = tag; // Check for loads/stores from/to undefined values. if (location.isUndef()) { @@ -1234,9 +1259,145 @@ GRExprEngine::NodeTy* GRExprEngine::EvalLocation(Stmt* Ex, NodeTy* Pred, ProgramPoint::PostLocationChecksSucceedKind); } +//===----------------------------------------------------------------------===// +// 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, SVal L, + 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()->getAsPointerType(); + + // 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'. + GRStateManager &StateMgr = Engine.getStateManager(); + const GRState *state = Pred->getState(); + ExplodedNodeSet Tmp; + SVal location = StateMgr.GetSVal(state, theValueExpr); + Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode *N = *I; + const GRState *stateLoad = N->getState(); + SVal theValueVal = StateMgr.GetSVal(stateLoad, theValueExpr); + SVal oldValueVal = StateMgr.GetSVal(stateLoad, oldValueExpr); + + // Perform the comparison. + SVal Cmp = Engine.EvalBinOp(BinaryOperator::EQ, theValueVal, oldValueVal, + Engine.getContext().IntTy); + bool isFeasible = false; + const GRState *stateEqual = StateMgr.Assume(stateLoad, Cmp, true, + isFeasible); + + // Were they equal? + if (isFeasible) { + // Perform the store. + ExplodedNodeSet TmpStore; + Engine.EvalStore(TmpStore, theValueExpr, N, stateEqual, location, + StateMgr.GetSVal(stateEqual, newValueExpr), + 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, Engine.BindExpr(stateNew, CE, Res)); + } + } + + // Were they not equal? + isFeasible = false; + const GRState *stateNotEqual = StateMgr.Assume(stateLoad, Cmp, false, + isFeasible); + + if (isFeasible) { + SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); + Engine.MakeNode(Dst, CE, N, Engine.BindExpr(stateNotEqual, CE, Res)); + } + } + + return true; +} + +static bool EvalOSAtomic(ExplodedNodeSet& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder& Builder, + CallExpr* CE, SVal L, + ExplodedNode* Pred) { + + if (!isa(L)) + return false; + + const FunctionDecl *FD = cast(L).getDecl(); + const char *FName = FD->getNameAsCString(); + + // Check for compare and swap. + if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0) + return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, L, Pred); + + // FIXME: Other atomics. + return false; +} + //===----------------------------------------------------------------------===// // Transfer function: Function calls. //===----------------------------------------------------------------------===// + +void GRExprEngine::EvalCall(NodeSet& Dst, CallExpr* CE, SVal L, NodeTy* Pred) { + assert (Builder && "GRStmtNodeBuilder must be defined."); + + // FIXME: Allow us to chain together transfer functions. + if (EvalOSAtomic(Dst, *this, *Builder, CE, L, Pred)) + return; + + getTF().EvalCall(Dst, *this, *Builder, CE, L, Pred); +} + void GRExprEngine::VisitCall(CallExpr* CE, NodeTy* Pred, CallExpr::arg_iterator AI, CallExpr::arg_iterator AE, diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp index c8a43a556d..0990767378 100644 --- a/lib/Analysis/MemRegion.cpp +++ b/lib/Analysis/MemRegion.cpp @@ -481,6 +481,21 @@ bool MemRegionManager::hasStackStorage(const MemRegion* R) { SR = dyn_cast(R); } - + return false; } + + +//===----------------------------------------------------------------------===// +// View handling. +//===----------------------------------------------------------------------===// + +const MemRegion *TypedViewRegion::removeViews() const { + const SubRegion *SR = this; + const MemRegion *R = SR; + while (SR && isa(SR)) { + R = SR->getSuperRegion(); + SR = dyn_cast(R); + } + return R; +} diff --git a/lib/Analysis/SVals.cpp b/lib/Analysis/SVals.cpp index 87a1073f23..e911d2c097 100644 --- a/lib/Analysis/SVals.cpp +++ b/lib/Analysis/SVals.cpp @@ -271,6 +271,10 @@ NonLoc NonLoc::MakeIntTruthVal(BasicValueFactory& BasicVals, bool b) { return nonloc::ConcreteInt(BasicVals.getTruthValue(b)); } +NonLoc ValueManager::makeTruthVal(bool b, QualType T) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b, T)); +} + NonLoc NonLoc::MakeCompoundVal(QualType T, llvm::ImmutableList Vals, BasicValueFactory& BasicVals) { return nonloc::CompoundVal(BasicVals.getCompoundValData(T, Vals)); diff --git a/test/Analysis/NSString.m b/test/Analysis/NSString.m index 3e2155b7c1..b1e524f6c5 100644 --- a/test/Analysis/NSString.m +++ b/test/Analysis/NSString.m @@ -1,7 +1,9 @@ // RUN: clang-cc -analyze -checker-cfref -analyzer-store=basic -analyzer-constraints=basic -verify %s && -// RUN: clang-cc -analyze -checker-cfref -analyzer-store=basic -analyzer-constraints=range -verify %s && -// RUN: clang-cc -analyze -checker-cfref -analyzer-store=region -analyzer-constraints=basic -verify %s && -// RUN: clang-cc -analyze -checker-cfref -analyzer-store=region -analyzer-constraints=range -verify %s +// RUN: clang-cc -analyze -checker-cfref -analyzer-store=basic -analyzer-constraints=range -verify %s + + +// NOTWORK: clang-cc -analyze -checker-cfref -analyzer-store=region -analyzer-constraints=basic -verify %s && +// NOTWORK: clang-cc -analyze -checker-cfref -analyzer-store=region -analyzer-constraints=range -verify %s //===----------------------------------------------------------------------===// // The following code is reduced using delta-debugging from @@ -212,3 +214,15 @@ id testSharedClassFromFunction() { return [[SharedClass alloc] _init]; // no-warning } +// Test OSCompareAndSwap +_Bool OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ); + +void testOSCompareAndSwap() { + NSString *old = 0; + NSString *s = [[NSString alloc] init]; + if (!OSAtomicCompareAndSwapPtr(0, s, (void**) &old)) + [s release]; + else + [old release]; +} +