blob: f426265f6713ce4c42fd8062bd7262989c386877 [file] [log] [blame]
Zhongxing Xu1ec4e972009-12-09 12:23:28 +00001//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker evaluates OSAtomic functions.
11//
12//===----------------------------------------------------------------------===//
13
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +000014#include "ClangSACheckers.h"
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000015#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +000016#include "clang/StaticAnalyzer/Core/CheckerManager.h"
17#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000018#include "clang/Basic/Builtins.h"
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000019
20using namespace clang;
Ted Kremenek9ef65372010-12-23 07:20:52 +000021using namespace ento;
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000022
23namespace {
24
Anna Zaksdff6ef92011-10-05 23:37:30 +000025class OSAtomicChecker : public Checker<eval::InlineCall> {
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000026public:
Anna Zaksdff6ef92011-10-05 23:37:30 +000027 bool inlineCall(const CallExpr *CE, ExprEngine &Eng,
28 ExplodedNode *Pred, ExplodedNodeSet &Dst) const;
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000029
30private:
Anna Zaksdff6ef92011-10-05 23:37:30 +000031 bool evalOSAtomicCompareAndSwap(const CallExpr *CE,
32 ExprEngine &Eng,
33 ExplodedNode *Pred,
34 ExplodedNodeSet &Dst) const;
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000035
Anna Zaksdff6ef92011-10-05 23:37:30 +000036 ExplodedNode *generateNode(const ProgramState *State,
37 ExplodedNode *Pred, const CallExpr *Statement,
38 StmtNodeBuilder &B, ExplodedNodeSet &Dst) const;
39};
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000040}
41
Anna Zaksdff6ef92011-10-05 23:37:30 +000042bool OSAtomicChecker::inlineCall(const CallExpr *CE,
43 ExprEngine &Eng,
44 ExplodedNode *Pred,
45 ExplodedNodeSet &Dst) const {
46 const ProgramState *state = Pred->getState();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000047 const Expr *Callee = CE->getCallee();
Ted Kremenek13976632010-02-08 16:18:51 +000048 SVal L = state->getSVal(Callee);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000049
Ted Kremenek9c378f72011-08-12 23:37:29 +000050 const FunctionDecl *FD = L.getAsFunctionDecl();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000051 if (!FD)
52 return false;
53
Ted Kremeneka0f77272009-12-16 06:03:24 +000054 const IdentifierInfo *II = FD->getIdentifier();
55 if (!II)
56 return false;
57
Chris Lattner5f9e2722011-07-23 10:55:15 +000058 StringRef FName(II->getName());
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000059
60 // Check for compare and swap.
Ted Kremeneka0f77272009-12-16 06:03:24 +000061 if (FName.startswith("OSAtomicCompareAndSwap") ||
62 FName.startswith("objc_atomicCompareAndSwap"))
Anna Zaksdff6ef92011-10-05 23:37:30 +000063 return evalOSAtomicCompareAndSwap(CE, Eng, Pred, Dst);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000064
65 // FIXME: Other atomics.
66 return false;
67}
68
Anna Zaksdff6ef92011-10-05 23:37:30 +000069ExplodedNode *OSAtomicChecker::generateNode(const ProgramState *State,
70 ExplodedNode *Pred,
71 const CallExpr *Statement,
72 StmtNodeBuilder &B,
73 ExplodedNodeSet &Dst) const {
74 ExplodedNode *N = B.generateNode(Statement, State, Pred, this);
75 if (N)
76 Dst.Add(N);
77 return N;
78}
79
80bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE,
81 ExprEngine &Eng,
82 ExplodedNode *Pred,
83 ExplodedNodeSet &Dst) const {
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000084 // Not enough arguments to match OSAtomicCompareAndSwap?
85 if (CE->getNumArgs() != 3)
86 return false;
87
Anna Zaksdff6ef92011-10-05 23:37:30 +000088 StmtNodeBuilder &Builder = Eng.getBuilder();
89 ASTContext &Ctx = Eng.getContext();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000090 const Expr *oldValueExpr = CE->getArg(0);
91 QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType());
92
93 const Expr *newValueExpr = CE->getArg(1);
94 QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType());
95
96 // Do the types of 'oldValue' and 'newValue' match?
97 if (oldValueType != newValueType)
98 return false;
99
100 const Expr *theValueExpr = CE->getArg(2);
101 const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>();
102
103 // theValueType not a pointer?
104 if (!theValueType)
105 return false;
106
107 QualType theValueTypePointee =
108 Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
109
110 // The pointee must match newValueType and oldValueType.
111 if (theValueTypePointee != newValueType)
112 return false;
113
Ted Kremenekca804532011-08-12 23:04:46 +0000114 static SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load");
115 static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store");
116
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000117 // Load 'theValue'.
Anna Zaksdff6ef92011-10-05 23:37:30 +0000118 const ProgramState *state = Pred->getState();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000119 ExplodedNodeSet Tmp;
Ted Kremenek13976632010-02-08 16:18:51 +0000120 SVal location = state->getSVal(theValueExpr);
Zhongxing Xu71f219a2010-06-23 02:06:56 +0000121 // Here we should use the value type of the region as the load type, because
122 // we are simulating the semantics of the function, not the semantics of
Zhongxing Xu24704462010-06-23 02:12:00 +0000123 // passing argument. So the type of theValue expr is not we are loading.
124 // But usually the type of the varregion is not the type we want either,
125 // we still need to do a CastRetrievedVal in store manager. So actually this
126 // LoadTy specifying can be omitted. But we put it here to emphasize the
127 // semantics.
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000128 QualType LoadTy;
Ted Kremenek96979342011-08-12 20:02:48 +0000129 if (const TypedValueRegion *TR =
130 dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
Zhongxing Xu018220c2010-08-11 06:10:55 +0000131 LoadTy = TR->getValueType();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000132 }
Anna Zaksdff6ef92011-10-05 23:37:30 +0000133 Eng.evalLoad(Tmp, theValueExpr, Pred,
Ted Kremenekca804532011-08-12 23:04:46 +0000134 state, location, &OSAtomicLoadTag, LoadTy);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000135
136 if (Tmp.empty()) {
137 // If no nodes were generated, other checkers must generated sinks. But
138 // since the builder state was restored, we set it manually to prevent
139 // auto transition.
140 // FIXME: there should be a better approach.
Anna Zaksdff6ef92011-10-05 23:37:30 +0000141 Builder.BuildSinks = true;
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000142 return true;
143 }
144
145 for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
146 I != E; ++I) {
147
148 ExplodedNode *N = *I;
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000149 const ProgramState *stateLoad = N->getState();
Ted Kremenek6b4f5672011-04-27 05:34:09 +0000150
151 // Use direct bindings from the environment since we are forcing a load
152 // from a location that the Environment would typically not be used
153 // to bind a value.
154 SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, true);
155
Ted Kremenek13976632010-02-08 16:18:51 +0000156 SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000157
158 // FIXME: Issue an error.
159 if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
160 return false;
161 }
162
163 DefinedOrUnknownSVal theValueVal =
164 cast<DefinedOrUnknownSVal>(theValueVal_untested);
165 DefinedOrUnknownSVal oldValueVal =
166 cast<DefinedOrUnknownSVal>(oldValueVal_untested);
167
Anna Zaksdff6ef92011-10-05 23:37:30 +0000168 SValBuilder &svalBuilder = Eng.getSValBuilder();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000169
170 // Perform the comparison.
Ted Kremenekca804532011-08-12 23:04:46 +0000171 DefinedOrUnknownSVal Cmp =
172 svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000173
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000174 const ProgramState *stateEqual = stateLoad->assume(Cmp, true);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000175
176 // Were they equal?
177 if (stateEqual) {
178 // Perform the store.
179 ExplodedNodeSet TmpStore;
Ted Kremenek13976632010-02-08 16:18:51 +0000180 SVal val = stateEqual->getSVal(newValueExpr);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000181
182 // Handle implicit value casts.
Ted Kremenek96979342011-08-12 20:02:48 +0000183 if (const TypedValueRegion *R =
184 dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
Ted Kremenek9c149532010-12-01 21:57:22 +0000185 val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType());
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000186 }
187
Anna Zaksdff6ef92011-10-05 23:37:30 +0000188 Eng.evalStore(TmpStore, NULL, theValueExpr, N,
Ted Kremenekca804532011-08-12 23:04:46 +0000189 stateEqual, location, val, &OSAtomicStoreTag);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000190
191 if (TmpStore.empty()) {
192 // If no nodes were generated, other checkers must generated sinks. But
193 // since the builder state was restored, we set it manually to prevent
194 // auto transition.
195 // FIXME: there should be a better approach.
Anna Zaksdff6ef92011-10-05 23:37:30 +0000196 Builder.BuildSinks = true;
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000197 return true;
198 }
199
200 // Now bind the result of the comparison.
201 for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
202 E2 = TmpStore.end(); I2 != E2; ++I2) {
203 ExplodedNode *predNew = *I2;
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000204 const ProgramState *stateNew = predNew->getState();
Ted Kremenekc50e6df2010-01-11 02:33:26 +0000205 // Check for 'void' return type if we have a bogus function prototype.
206 SVal Res = UnknownVal();
207 QualType T = CE->getType();
208 if (!T->isVoidType())
Anna Zaksdff6ef92011-10-05 23:37:30 +0000209 Res = Eng.getSValBuilder().makeTruthVal(true, T);
210 generateNode(stateNew->BindExpr(CE, Res), predNew, CE, Builder, Dst);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000211 }
212 }
213
214 // Were they not equal?
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000215 if (const ProgramState *stateNotEqual = stateLoad->assume(Cmp, false)) {
Ted Kremenekc50e6df2010-01-11 02:33:26 +0000216 // Check for 'void' return type if we have a bogus function prototype.
217 SVal Res = UnknownVal();
218 QualType T = CE->getType();
219 if (!T->isVoidType())
Anna Zaksdff6ef92011-10-05 23:37:30 +0000220 Res = Eng.getSValBuilder().makeTruthVal(false, CE->getType());
221 generateNode(stateNotEqual->BindExpr(CE, Res), N, CE, Builder, Dst);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000222 }
223 }
224
225 return true;
226}
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +0000227
228void ento::registerOSAtomicChecker(CheckerManager &mgr) {
229 mgr.registerChecker<OSAtomicChecker>();
230}