blob: b518ab42ab7f9229e6444980c0ff17553a25e02d [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
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000025class OSAtomicChecker : public Checker<eval::Call> {
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000026public:
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +000027 bool evalCall(const CallExpr *CE, CheckerContext &C) const;
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000028
29private:
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +000030 static bool evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000031};
32
33}
34
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +000035bool OSAtomicChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000036 const GRState *state = C.getState();
37 const Expr *Callee = CE->getCallee();
Ted Kremenek13976632010-02-08 16:18:51 +000038 SVal L = state->getSVal(Callee);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000039
40 const FunctionDecl* FD = L.getAsFunctionDecl();
41 if (!FD)
42 return false;
43
Ted Kremeneka0f77272009-12-16 06:03:24 +000044 const IdentifierInfo *II = FD->getIdentifier();
45 if (!II)
46 return false;
47
Chris Lattner5f9e2722011-07-23 10:55:15 +000048 StringRef FName(II->getName());
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000049
50 // Check for compare and swap.
Ted Kremeneka0f77272009-12-16 06:03:24 +000051 if (FName.startswith("OSAtomicCompareAndSwap") ||
52 FName.startswith("objc_atomicCompareAndSwap"))
Ted Kremenek9c149532010-12-01 21:57:22 +000053 return evalOSAtomicCompareAndSwap(C, CE);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000054
55 // FIXME: Other atomics.
56 return false;
57}
58
Ted Kremenek9c149532010-12-01 21:57:22 +000059bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000060 const CallExpr *CE) {
61 // Not enough arguments to match OSAtomicCompareAndSwap?
62 if (CE->getNumArgs() != 3)
63 return false;
64
65 ASTContext &Ctx = C.getASTContext();
66 const Expr *oldValueExpr = CE->getArg(0);
67 QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType());
68
69 const Expr *newValueExpr = CE->getArg(1);
70 QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType());
71
72 // Do the types of 'oldValue' and 'newValue' match?
73 if (oldValueType != newValueType)
74 return false;
75
76 const Expr *theValueExpr = CE->getArg(2);
77 const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>();
78
79 // theValueType not a pointer?
80 if (!theValueType)
81 return false;
82
83 QualType theValueTypePointee =
84 Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
85
86 // The pointee must match newValueType and oldValueType.
87 if (theValueTypePointee != newValueType)
88 return false;
89
90 static unsigned magic_load = 0;
91 static unsigned magic_store = 0;
92
93 const void *OSAtomicLoadTag = &magic_load;
94 const void *OSAtomicStoreTag = &magic_store;
95
96 // Load 'theValue'.
Argyrios Kyrtzidisd2592a32010-12-22 18:53:44 +000097 ExprEngine &Engine = C.getEngine();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000098 const GRState *state = C.getState();
99 ExplodedNodeSet Tmp;
Ted Kremenek13976632010-02-08 16:18:51 +0000100 SVal location = state->getSVal(theValueExpr);
Zhongxing Xu71f219a2010-06-23 02:06:56 +0000101 // Here we should use the value type of the region as the load type, because
102 // we are simulating the semantics of the function, not the semantics of
Zhongxing Xu24704462010-06-23 02:12:00 +0000103 // passing argument. So the type of theValue expr is not we are loading.
104 // But usually the type of the varregion is not the type we want either,
105 // we still need to do a CastRetrievedVal in store manager. So actually this
106 // LoadTy specifying can be omitted. But we put it here to emphasize the
107 // semantics.
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000108 QualType LoadTy;
Ted Kremenekc50e6df2010-01-11 02:33:26 +0000109 if (const TypedRegion *TR =
110 dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
Zhongxing Xu018220c2010-08-11 06:10:55 +0000111 LoadTy = TR->getValueType();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000112 }
Ted Kremenek9c149532010-12-01 21:57:22 +0000113 Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(),
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000114 state, location, OSAtomicLoadTag, LoadTy);
115
116 if (Tmp.empty()) {
117 // If no nodes were generated, other checkers must generated sinks. But
118 // since the builder state was restored, we set it manually to prevent
119 // auto transition.
120 // FIXME: there should be a better approach.
121 C.getNodeBuilder().BuildSinks = true;
122 return true;
123 }
124
125 for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
126 I != E; ++I) {
127
128 ExplodedNode *N = *I;
129 const GRState *stateLoad = N->getState();
Ted Kremenek6b4f5672011-04-27 05:34:09 +0000130
131 // Use direct bindings from the environment since we are forcing a load
132 // from a location that the Environment would typically not be used
133 // to bind a value.
134 SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, true);
135
Ted Kremenek13976632010-02-08 16:18:51 +0000136 SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000137
138 // FIXME: Issue an error.
139 if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
140 return false;
141 }
142
143 DefinedOrUnknownSVal theValueVal =
144 cast<DefinedOrUnknownSVal>(theValueVal_untested);
145 DefinedOrUnknownSVal oldValueVal =
146 cast<DefinedOrUnknownSVal>(oldValueVal_untested);
147
Ted Kremenek846eabd2010-12-01 21:28:31 +0000148 SValBuilder &svalBuilder = Engine.getSValBuilder();
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000149
150 // Perform the comparison.
Ted Kremenek9c149532010-12-01 21:57:22 +0000151 DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000152
Ted Kremenek28f47b92010-12-01 22:16:56 +0000153 const GRState *stateEqual = stateLoad->assume(Cmp, true);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000154
155 // Were they equal?
156 if (stateEqual) {
157 // Perform the store.
158 ExplodedNodeSet TmpStore;
Ted Kremenek13976632010-02-08 16:18:51 +0000159 SVal val = stateEqual->getSVal(newValueExpr);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000160
161 // Handle implicit value casts.
162 if (const TypedRegion *R =
163 dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
Ted Kremenek9c149532010-12-01 21:57:22 +0000164 val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType());
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000165 }
166
Ted Kremenek9c149532010-12-01 21:57:22 +0000167 Engine.evalStore(TmpStore, NULL, theValueExpr, N,
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000168 stateEqual, location, val, OSAtomicStoreTag);
169
170 if (TmpStore.empty()) {
171 // If no nodes were generated, other checkers must generated sinks. But
172 // since the builder state was restored, we set it manually to prevent
173 // auto transition.
174 // FIXME: there should be a better approach.
175 C.getNodeBuilder().BuildSinks = true;
176 return true;
177 }
178
179 // Now bind the result of the comparison.
180 for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
181 E2 = TmpStore.end(); I2 != E2; ++I2) {
182 ExplodedNode *predNew = *I2;
183 const GRState *stateNew = predNew->getState();
Ted Kremenekc50e6df2010-01-11 02:33:26 +0000184 // Check for 'void' return type if we have a bogus function prototype.
185 SVal Res = UnknownVal();
186 QualType T = CE->getType();
187 if (!T->isVoidType())
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000188 Res = Engine.getSValBuilder().makeTruthVal(true, T);
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000189 C.generateNode(stateNew->BindExpr(CE, Res), predNew);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000190 }
191 }
192
193 // Were they not equal?
Ted Kremenek28f47b92010-12-01 22:16:56 +0000194 if (const GRState *stateNotEqual = stateLoad->assume(Cmp, false)) {
Ted Kremenekc50e6df2010-01-11 02:33:26 +0000195 // Check for 'void' return type if we have a bogus function prototype.
196 SVal Res = UnknownVal();
197 QualType T = CE->getType();
198 if (!T->isVoidType())
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000199 Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType());
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000200 C.generateNode(stateNotEqual->BindExpr(CE, Res), N);
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000201 }
202 }
203
204 return true;
205}
Argyrios Kyrtzidisf0293662011-02-28 01:27:02 +0000206
207void ento::registerOSAtomicChecker(CheckerManager &mgr) {
208 mgr.registerChecker<OSAtomicChecker>();
209}