clang API Documentation
00001 //=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This checker evaluates OSAtomic functions. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "ClangSACheckers.h" 00015 #include "clang/StaticAnalyzer/Core/Checker.h" 00016 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00017 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00018 #include "clang/Basic/Builtins.h" 00019 00020 using namespace clang; 00021 using namespace ento; 00022 00023 namespace { 00024 00025 class OSAtomicChecker : public Checker<eval::InlineCall> { 00026 public: 00027 bool inlineCall(const CallExpr *CE, ExprEngine &Eng, 00028 ExplodedNode *Pred, ExplodedNodeSet &Dst) const; 00029 00030 private: 00031 bool evalOSAtomicCompareAndSwap(const CallExpr *CE, 00032 ExprEngine &Eng, 00033 ExplodedNode *Pred, 00034 ExplodedNodeSet &Dst) const; 00035 }; 00036 } 00037 00038 static StringRef getCalleeName(ProgramStateRef State, 00039 const CallExpr *CE, 00040 const LocationContext *LCtx) { 00041 const Expr *Callee = CE->getCallee(); 00042 SVal L = State->getSVal(Callee, LCtx); 00043 const FunctionDecl *funDecl = L.getAsFunctionDecl(); 00044 if (!funDecl) 00045 return StringRef(); 00046 IdentifierInfo *funI = funDecl->getIdentifier(); 00047 if (!funI) 00048 return StringRef(); 00049 return funI->getName(); 00050 } 00051 00052 bool OSAtomicChecker::inlineCall(const CallExpr *CE, 00053 ExprEngine &Eng, 00054 ExplodedNode *Pred, 00055 ExplodedNodeSet &Dst) const { 00056 StringRef FName = getCalleeName(Pred->getState(), 00057 CE, Pred->getLocationContext()); 00058 if (FName.empty()) 00059 return false; 00060 00061 // Check for compare and swap. 00062 if (FName.startswith("OSAtomicCompareAndSwap") || 00063 FName.startswith("objc_atomicCompareAndSwap")) 00064 return evalOSAtomicCompareAndSwap(CE, Eng, Pred, Dst); 00065 00066 // FIXME: Other atomics. 00067 return false; 00068 } 00069 00070 bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, 00071 ExprEngine &Eng, 00072 ExplodedNode *Pred, 00073 ExplodedNodeSet &Dst) const { 00074 // Not enough arguments to match OSAtomicCompareAndSwap? 00075 if (CE->getNumArgs() != 3) 00076 return false; 00077 00078 ASTContext &Ctx = Eng.getContext(); 00079 const Expr *oldValueExpr = CE->getArg(0); 00080 QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); 00081 00082 const Expr *newValueExpr = CE->getArg(1); 00083 QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); 00084 00085 // Do the types of 'oldValue' and 'newValue' match? 00086 if (oldValueType != newValueType) 00087 return false; 00088 00089 const Expr *theValueExpr = CE->getArg(2); 00090 const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>(); 00091 00092 // theValueType not a pointer? 00093 if (!theValueType) 00094 return false; 00095 00096 QualType theValueTypePointee = 00097 Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); 00098 00099 // The pointee must match newValueType and oldValueType. 00100 if (theValueTypePointee != newValueType) 00101 return false; 00102 00103 static SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load"); 00104 static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store"); 00105 00106 // Load 'theValue'. 00107 ProgramStateRef state = Pred->getState(); 00108 const LocationContext *LCtx = Pred->getLocationContext(); 00109 ExplodedNodeSet Tmp; 00110 SVal location = state->getSVal(theValueExpr, LCtx); 00111 // Here we should use the value type of the region as the load type, because 00112 // we are simulating the semantics of the function, not the semantics of 00113 // passing argument. So the type of theValue expr is not we are loading. 00114 // But usually the type of the varregion is not the type we want either, 00115 // we still need to do a CastRetrievedVal in store manager. So actually this 00116 // LoadTy specifying can be omitted. But we put it here to emphasize the 00117 // semantics. 00118 QualType LoadTy; 00119 if (const TypedValueRegion *TR = 00120 dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { 00121 LoadTy = TR->getValueType(); 00122 } 00123 Eng.evalLoad(Tmp, CE, theValueExpr, Pred, 00124 state, location, &OSAtomicLoadTag, LoadTy); 00125 00126 if (Tmp.empty()) { 00127 // If no nodes were generated, other checkers must have generated sinks. 00128 // We return an empty Dst. 00129 return true; 00130 } 00131 00132 for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); 00133 I != E; ++I) { 00134 00135 ExplodedNode *N = *I; 00136 ProgramStateRef stateLoad = N->getState(); 00137 00138 // Use direct bindings from the environment since we are forcing a load 00139 // from a location that the Environment would typically not be used 00140 // to bind a value. 00141 SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, LCtx, true); 00142 00143 SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr, LCtx); 00144 00145 // FIXME: Issue an error. 00146 if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { 00147 return false; 00148 } 00149 00150 DefinedOrUnknownSVal theValueVal = 00151 cast<DefinedOrUnknownSVal>(theValueVal_untested); 00152 DefinedOrUnknownSVal oldValueVal = 00153 cast<DefinedOrUnknownSVal>(oldValueVal_untested); 00154 00155 SValBuilder &svalBuilder = Eng.getSValBuilder(); 00156 00157 // Perform the comparison. 00158 DefinedOrUnknownSVal Cmp = 00159 svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); 00160 00161 ProgramStateRef stateEqual = stateLoad->assume(Cmp, true); 00162 00163 // Were they equal? 00164 if (stateEqual) { 00165 // Perform the store. 00166 ExplodedNodeSet TmpStore; 00167 SVal val = stateEqual->getSVal(newValueExpr, LCtx); 00168 00169 // Handle implicit value casts. 00170 if (const TypedValueRegion *R = 00171 dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { 00172 val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); 00173 } 00174 00175 Eng.evalStore(TmpStore, CE, theValueExpr, N, 00176 stateEqual, location, val, &OSAtomicStoreTag); 00177 00178 if (TmpStore.empty()) { 00179 // If no nodes were generated, other checkers must have generated sinks. 00180 // We return an empty Dst. 00181 return true; 00182 } 00183 00184 StmtNodeBuilder B(TmpStore, Dst, Eng.getBuilderContext()); 00185 // Now bind the result of the comparison. 00186 for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), 00187 E2 = TmpStore.end(); I2 != E2; ++I2) { 00188 ExplodedNode *predNew = *I2; 00189 ProgramStateRef stateNew = predNew->getState(); 00190 // Check for 'void' return type if we have a bogus function prototype. 00191 SVal Res = UnknownVal(); 00192 QualType T = CE->getType(); 00193 if (!T->isVoidType()) 00194 Res = Eng.getSValBuilder().makeTruthVal(true, T); 00195 B.generateNode(CE, predNew, stateNew->BindExpr(CE, LCtx, Res), 00196 false, this); 00197 } 00198 } 00199 00200 // Were they not equal? 00201 if (ProgramStateRef stateNotEqual = stateLoad->assume(Cmp, false)) { 00202 // Check for 'void' return type if we have a bogus function prototype. 00203 SVal Res = UnknownVal(); 00204 QualType T = CE->getType(); 00205 if (!T->isVoidType()) 00206 Res = Eng.getSValBuilder().makeTruthVal(false, CE->getType()); 00207 StmtNodeBuilder B(N, Dst, Eng.getBuilderContext()); 00208 B.generateNode(CE, N, stateNotEqual->BindExpr(CE, LCtx, Res), 00209 false, this); 00210 } 00211 } 00212 00213 return true; 00214 } 00215 00216 void ento::registerOSAtomicChecker(CheckerManager &mgr) { 00217 mgr.registerChecker<OSAtomicChecker>(); 00218 }