diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp new file mode 100644 index 0000000..7b724d2 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp @@ -0,0 +1,218 @@ +//=== 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 "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { + +class OSAtomicChecker : public Checker<eval::InlineCall> { +public: + bool inlineCall(const CallExpr *CE, ExprEngine &Eng, + ExplodedNode *Pred, ExplodedNodeSet &Dst) const; + +private: + bool evalOSAtomicCompareAndSwap(const CallExpr *CE, + ExprEngine &Eng, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) const; +}; +} + +static StringRef getCalleeName(ProgramStateRef State, + const CallExpr *CE, + const LocationContext *LCtx) { + const Expr *Callee = CE->getCallee(); + SVal L = State->getSVal(Callee, LCtx); + const FunctionDecl *funDecl = L.getAsFunctionDecl(); + if (!funDecl) + return StringRef(); + IdentifierInfo *funI = funDecl->getIdentifier(); + if (!funI) + return StringRef(); + return funI->getName(); +} + +bool OSAtomicChecker::inlineCall(const CallExpr *CE, + ExprEngine &Eng, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) const { + StringRef FName = getCalleeName(Pred->getState(), + CE, Pred->getLocationContext()); + if (FName.empty()) + return false; + + // Check for compare and swap. + if (FName.startswith("OSAtomicCompareAndSwap") || + FName.startswith("objc_atomicCompareAndSwap")) + return evalOSAtomicCompareAndSwap(CE, Eng, Pred, Dst); + + // FIXME: Other atomics. + return false; +} + +bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, + ExprEngine &Eng, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) const { + // Not enough arguments to match OSAtomicCompareAndSwap? + if (CE->getNumArgs() != 3) + return false; + + ASTContext &Ctx = Eng.getContext(); + 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 SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load"); + static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store"); + + // Load 'theValue'. + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + ExplodedNodeSet Tmp; + SVal location = state->getSVal(theValueExpr, LCtx); + // Here we should use the value type of the region as the load type, because + // we are simulating the semantics of the function, not the semantics of + // passing argument. So the type of theValue expr is not we are loading. + // But usually the type of the varregion is not the type we want either, + // we still need to do a CastRetrievedVal in store manager. So actually this + // LoadTy specifying can be omitted. But we put it here to emphasize the + // semantics. + QualType LoadTy; + if (const TypedValueRegion *TR = + dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { + LoadTy = TR->getValueType(); + } + Eng.evalLoad(Tmp, CE, theValueExpr, Pred, + state, location, &OSAtomicLoadTag, LoadTy); + + if (Tmp.empty()) { + // If no nodes were generated, other checkers must have generated sinks. + // We return an empty Dst. + return true; + } + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode *N = *I; + ProgramStateRef stateLoad = N->getState(); + + // Use direct bindings from the environment since we are forcing a load + // from a location that the Environment would typically not be used + // to bind a value. + SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, LCtx, true); + + SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr, LCtx); + + // 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); + + SValBuilder &svalBuilder = Eng.getSValBuilder(); + + // Perform the comparison. + DefinedOrUnknownSVal Cmp = + svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); + + ProgramStateRef stateEqual = stateLoad->assume(Cmp, true); + + // Were they equal? + if (stateEqual) { + // Perform the store. + ExplodedNodeSet TmpStore; + SVal val = stateEqual->getSVal(newValueExpr, LCtx); + + // Handle implicit value casts. + if (const TypedValueRegion *R = + dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { + val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); + } + + Eng.evalStore(TmpStore, CE, theValueExpr, N, + stateEqual, location, val, &OSAtomicStoreTag); + + if (TmpStore.empty()) { + // If no nodes were generated, other checkers must have generated sinks. + // We return an empty Dst. + return true; + } + + StmtNodeBuilder B(TmpStore, Dst, Eng.getBuilderContext()); + // Now bind the result of the comparison. + for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), + E2 = TmpStore.end(); I2 != E2; ++I2) { + ExplodedNode *predNew = *I2; + ProgramStateRef stateNew = predNew->getState(); + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Eng.getSValBuilder().makeTruthVal(true, T); + B.generateNode(CE, predNew, stateNew->BindExpr(CE, LCtx, Res), + false, this); + } + } + + // Were they not equal? + if (ProgramStateRef stateNotEqual = stateLoad->assume(Cmp, false)) { + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Eng.getSValBuilder().makeTruthVal(false, CE->getType()); + StmtNodeBuilder B(N, Dst, Eng.getBuilderContext()); + B.generateNode(CE, N, stateNotEqual->BindExpr(CE, LCtx, Res), + false, this); + } + } + + return true; +} + +void ento::registerOSAtomicChecker(CheckerManager &mgr) { + mgr.registerChecker<OSAtomicChecker>(); +} |