/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //=== Iterator.cpp - Common functions for iterator checkers. -------*- C++ -*-// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // Defines common functions to be used by the itertor checkers . |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "Iterator.h" |
14 | | |
15 | | namespace clang { |
16 | | namespace ento { |
17 | | namespace iterator { |
18 | | |
19 | 18.9k | bool isIteratorType(const QualType &Type) { |
20 | 18.9k | if (Type->isPointerType()) |
21 | 1.85k | return true; |
22 | | |
23 | 17.0k | const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); |
24 | 17.0k | return isIterator(CRD); |
25 | 18.9k | } |
26 | | |
27 | 17.0k | bool isIterator(const CXXRecordDecl *CRD) { |
28 | 17.0k | if (!CRD) |
29 | 9.41k | return false; |
30 | | |
31 | 7.65k | const auto Name = CRD->getName(); |
32 | 7.65k | if (!(Name.ends_with_insensitive("iterator") || |
33 | 7.65k | Name.ends_with_insensitive("iter")209 || Name.ends_with_insensitive("it")209 )) |
34 | 209 | return false; |
35 | | |
36 | 7.44k | bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false, |
37 | 7.44k | HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false; |
38 | 143k | for (const auto *Method : CRD->methods()) { |
39 | 143k | if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) { |
40 | 26.2k | if (Ctor->isCopyConstructor()) { |
41 | 7.44k | HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public; |
42 | 7.44k | } |
43 | 26.2k | continue; |
44 | 26.2k | } |
45 | 117k | if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) { |
46 | 7.44k | HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public; |
47 | 7.44k | continue; |
48 | 7.44k | } |
49 | 109k | if (Method->isCopyAssignmentOperator()) { |
50 | 12 | HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public; |
51 | 12 | continue; |
52 | 12 | } |
53 | 109k | if (!Method->isOverloadedOperator()) |
54 | 7.43k | continue; |
55 | 102k | const auto OPK = Method->getOverloadedOperator(); |
56 | 102k | if (OPK == OO_PlusPlus) { |
57 | 14.8k | HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0)7.44k ; |
58 | 14.8k | HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1); |
59 | 14.8k | continue; |
60 | 14.8k | } |
61 | 87.3k | if (OPK == OO_Star) { |
62 | 7.44k | HasDerefOp = (Method->getNumParams() == 0); |
63 | 7.44k | continue; |
64 | 7.44k | } |
65 | 87.3k | } |
66 | | |
67 | 7.44k | return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp && |
68 | 7.44k | HasPostIncrOp && HasDerefOp; |
69 | 7.65k | } |
70 | | |
71 | 46 | bool isComparisonOperator(OverloadedOperatorKind OK) { |
72 | 46 | return OK == OO_EqualEqual || OK == OO_ExclaimEqual42 || OK == OO_Less32 || |
73 | 46 | OK == OO_LessEqual32 || OK == OO_Greater32 || OK == OO_GreaterEqual32 ; |
74 | 46 | } |
75 | | |
76 | 208 | bool isInsertCall(const FunctionDecl *Func) { |
77 | 208 | const auto *IdInfo = Func->getIdentifier(); |
78 | 208 | if (!IdInfo) |
79 | 36 | return false; |
80 | 172 | if (Func->getNumParams() < 2 || Func->getNumParams() > 326 ) |
81 | 146 | return false; |
82 | 26 | if (!isIteratorType(Func->getParamDecl(0)->getType())) |
83 | 0 | return false; |
84 | 26 | return IdInfo->getName() == "insert"; |
85 | 26 | } |
86 | | |
87 | 186 | bool isEmplaceCall(const FunctionDecl *Func) { |
88 | 186 | const auto *IdInfo = Func->getIdentifier(); |
89 | 186 | if (!IdInfo) |
90 | 36 | return false; |
91 | 150 | if (Func->getNumParams() < 2) |
92 | 146 | return false; |
93 | 4 | if (!isIteratorType(Func->getParamDecl(0)->getType())) |
94 | 0 | return false; |
95 | 4 | return IdInfo->getName() == "emplace"; |
96 | 4 | } |
97 | | |
98 | 222 | bool isEraseCall(const FunctionDecl *Func) { |
99 | 222 | const auto *IdInfo = Func->getIdentifier(); |
100 | 222 | if (!IdInfo) |
101 | 36 | return false; |
102 | 186 | if (Func->getNumParams() < 1 || Func->getNumParams() > 240 ) |
103 | 158 | return false; |
104 | 28 | if (!isIteratorType(Func->getParamDecl(0)->getType())) |
105 | 0 | return false; |
106 | 28 | if (Func->getNumParams() == 2 && |
107 | 28 | !isIteratorType(Func->getParamDecl(1)->getType())22 ) |
108 | 14 | return false; |
109 | 14 | return IdInfo->getName() == "erase"; |
110 | 28 | } |
111 | | |
112 | 208 | bool isEraseAfterCall(const FunctionDecl *Func) { |
113 | 208 | const auto *IdInfo = Func->getIdentifier(); |
114 | 208 | if (!IdInfo) |
115 | 36 | return false; |
116 | 172 | if (Func->getNumParams() < 1 || Func->getNumParams() > 226 ) |
117 | 158 | return false; |
118 | 14 | if (!isIteratorType(Func->getParamDecl(0)->getType())) |
119 | 0 | return false; |
120 | 14 | if (Func->getNumParams() == 2 && |
121 | 14 | !isIteratorType(Func->getParamDecl(1)->getType())) |
122 | 14 | return false; |
123 | 0 | return IdInfo->getName() == "erase_after"; |
124 | 14 | } |
125 | | |
126 | 48 | bool isAccessOperator(OverloadedOperatorKind OK) { |
127 | 48 | return isDereferenceOperator(OK) || isIncrementOperator(OK)40 || |
128 | 48 | isDecrementOperator(OK)24 || isRandomIncrOrDecrOperator(OK)16 ; |
129 | 48 | } |
130 | | |
131 | 20 | bool isAccessOperator(UnaryOperatorKind OK) { |
132 | 20 | return isDereferenceOperator(OK) || isIncrementOperator(OK)16 || |
133 | 20 | isDecrementOperator(OK)6 ; |
134 | 20 | } |
135 | | |
136 | 17 | bool isAccessOperator(BinaryOperatorKind OK) { |
137 | 17 | return isDereferenceOperator(OK) || isRandomIncrOrDecrOperator(OK); |
138 | 17 | } |
139 | | |
140 | 94 | bool isDereferenceOperator(OverloadedOperatorKind OK) { |
141 | 94 | return OK == OO_Star || OK == OO_Arrow78 || OK == OO_ArrowStar74 || |
142 | 94 | OK == OO_Subscript74 ; |
143 | 94 | } |
144 | | |
145 | 235 | bool isDereferenceOperator(UnaryOperatorKind OK) { |
146 | 235 | return OK == UO_Deref; |
147 | 235 | } |
148 | | |
149 | 116 | bool isDereferenceOperator(BinaryOperatorKind OK) { |
150 | 116 | return OK == BO_PtrMemI; |
151 | 116 | } |
152 | | |
153 | 1.12k | bool isIncrementOperator(OverloadedOperatorKind OK) { |
154 | 1.12k | return OK == OO_PlusPlus; |
155 | 1.12k | } |
156 | | |
157 | 1.47k | bool isIncrementOperator(UnaryOperatorKind OK) { |
158 | 1.47k | return OK == UO_PreInc || OK == UO_PostInc1.15k ; |
159 | 1.47k | } |
160 | | |
161 | 739 | bool isDecrementOperator(OverloadedOperatorKind OK) { |
162 | 739 | return OK == OO_MinusMinus; |
163 | 739 | } |
164 | | |
165 | 991 | bool isDecrementOperator(UnaryOperatorKind OK) { |
166 | 991 | return OK == UO_PreDec || OK == UO_PostDec801 ; |
167 | 991 | } |
168 | | |
169 | 1.17k | bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) { |
170 | 1.17k | return OK == OO_Plus || OK == OO_PlusEqual1.12k || OK == OO_Minus839 || |
171 | 1.17k | OK == OO_MinusEqual795 ; |
172 | 1.17k | } |
173 | | |
174 | 626 | bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK) { |
175 | 626 | return OK == BO_Add || OK == BO_AddAssign542 || |
176 | 626 | OK == BO_Sub394 || OK == BO_SubAssign348 ; |
177 | 626 | } |
178 | | |
179 | | const ContainerData *getContainerData(ProgramStateRef State, |
180 | 9.13k | const MemRegion *Cont) { |
181 | 9.13k | return State->get<ContainerMap>(Cont); |
182 | 9.13k | } |
183 | | |
184 | | const IteratorPosition *getIteratorPosition(ProgramStateRef State, |
185 | 29.6k | const SVal &Val) { |
186 | 29.6k | if (auto Reg = Val.getAsRegion()) { |
187 | 15.7k | Reg = Reg->getMostDerivedObjectRegion(); |
188 | 15.7k | return State->get<IteratorRegionMap>(Reg); |
189 | 15.7k | } else if (const auto 13.8k Sym13.8k = Val.getAsSymbol()) { |
190 | 736 | return State->get<IteratorSymbolMap>(Sym); |
191 | 13.1k | } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { |
192 | 12.9k | return State->get<IteratorRegionMap>(LCVal->getRegion()); |
193 | 12.9k | } |
194 | 169 | return nullptr; |
195 | 29.6k | } |
196 | | |
197 | | ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, |
198 | 10.5k | const IteratorPosition &Pos) { |
199 | 10.5k | if (auto Reg = Val.getAsRegion()) { |
200 | 6.80k | Reg = Reg->getMostDerivedObjectRegion(); |
201 | 6.80k | return State->set<IteratorRegionMap>(Reg, Pos); |
202 | 6.80k | } else if (const auto 3.73k Sym3.73k = Val.getAsSymbol()) { |
203 | 199 | return State->set<IteratorSymbolMap>(Sym, Pos); |
204 | 3.53k | } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { |
205 | 3.52k | return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos); |
206 | 3.52k | } |
207 | 6 | return nullptr; |
208 | 10.5k | } |
209 | | |
210 | | ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val, |
211 | | const MemRegion *Cont, const Stmt* S, |
212 | | const LocationContext *LCtx, |
213 | 479 | unsigned blockCount) { |
214 | 479 | auto &StateMgr = State->getStateManager(); |
215 | 479 | auto &SymMgr = StateMgr.getSymbolManager(); |
216 | 479 | auto &ACtx = StateMgr.getContext(); |
217 | | |
218 | 479 | auto Sym = SymMgr.conjureSymbol(S, LCtx, ACtx.LongTy, blockCount); |
219 | 479 | State = assumeNoOverflow(State, Sym, 4); |
220 | 479 | return setIteratorPosition(State, Val, |
221 | 479 | IteratorPosition::getPosition(Cont, Sym)); |
222 | 479 | } |
223 | | |
224 | | ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter, |
225 | | OverloadedOperatorKind Op, |
226 | 1.55k | const SVal &Distance) { |
227 | 1.55k | const auto *Pos = getIteratorPosition(State, Iter); |
228 | 1.55k | if (!Pos) |
229 | 145 | return nullptr; |
230 | | |
231 | 1.41k | auto &SymMgr = State->getStateManager().getSymbolManager(); |
232 | 1.41k | auto &SVB = State->getStateManager().getSValBuilder(); |
233 | 1.41k | auto &BVF = State->getStateManager().getBasicVals(); |
234 | | |
235 | 1.41k | assert ((Op == OO_Plus || Op == OO_PlusEqual || |
236 | 1.41k | Op == OO_Minus || Op == OO_MinusEqual) && |
237 | 1.41k | "Advance operator must be one of +, -, += and -=."); |
238 | 1.41k | auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual916 ) ? BO_Add896 : BO_Sub518 ; |
239 | 1.41k | const auto IntDistOp = Distance.getAs<nonloc::ConcreteInt>(); |
240 | 1.41k | if (!IntDistOp) |
241 | 10 | return nullptr; |
242 | | |
243 | | // For concrete integers we can calculate the new position |
244 | 1.40k | nonloc::ConcreteInt IntDist = *IntDistOp; |
245 | | |
246 | 1.40k | if (IntDist.getValue().isNegative()) { |
247 | 218 | IntDist = nonloc::ConcreteInt(BVF.getValue(-IntDist.getValue())); |
248 | 218 | BinOp = (BinOp == BO_Add) ? BO_Sub186 : BO_Add32 ; |
249 | 218 | } |
250 | 1.40k | const auto NewPos = |
251 | 1.40k | Pos->setTo(SVB.evalBinOp(State, BinOp, |
252 | 1.40k | nonloc::SymbolVal(Pos->getOffset()), |
253 | 1.40k | IntDist, SymMgr.getType(Pos->getOffset())) |
254 | 1.40k | .getAsSymbol()); |
255 | 1.40k | return setIteratorPosition(State, Iter, NewPos); |
256 | 1.41k | } |
257 | | |
258 | | // This function tells the analyzer's engine that symbols produced by our |
259 | | // checker, most notably iterator positions, are relatively small. |
260 | | // A distance between items in the container should not be very large. |
261 | | // By assuming that it is within around 1/8 of the address space, |
262 | | // we can help the analyzer perform operations on these symbols |
263 | | // without being afraid of integer overflows. |
264 | | // FIXME: Should we provide it as an API, so that all checkers could use it? |
265 | | ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, |
266 | 2.79k | long Scale) { |
267 | 2.79k | SValBuilder &SVB = State->getStateManager().getSValBuilder(); |
268 | 2.79k | BasicValueFactory &BV = SVB.getBasicValueFactory(); |
269 | | |
270 | 2.79k | QualType T = Sym->getType(); |
271 | 2.79k | assert(T->isSignedIntegerOrEnumerationType()); |
272 | 2.79k | APSIntType AT = BV.getAPSIntType(T); |
273 | | |
274 | 2.79k | ProgramStateRef NewState = State; |
275 | | |
276 | 2.79k | llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale); |
277 | 2.79k | SVal IsCappedFromAbove = |
278 | 2.79k | SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym), |
279 | 2.79k | nonloc::ConcreteInt(Max), SVB.getConditionType()); |
280 | 2.79k | if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) { |
281 | 2.79k | NewState = NewState->assume(*DV, true); |
282 | 2.79k | if (!NewState) |
283 | 0 | return State; |
284 | 2.79k | } |
285 | | |
286 | 2.79k | llvm::APSInt Min = -Max; |
287 | 2.79k | SVal IsCappedFromBelow = |
288 | 2.79k | SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym), |
289 | 2.79k | nonloc::ConcreteInt(Min), SVB.getConditionType()); |
290 | 2.79k | if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) { |
291 | 2.79k | NewState = NewState->assume(*DV, true); |
292 | 2.79k | if (!NewState) |
293 | 0 | return State; |
294 | 2.79k | } |
295 | | |
296 | 2.79k | return NewState; |
297 | 2.79k | } |
298 | | |
299 | | bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, |
300 | 2.09k | BinaryOperator::Opcode Opc) { |
301 | 2.09k | return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc); |
302 | 2.09k | } |
303 | | |
304 | | bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, |
305 | 2.83k | BinaryOperator::Opcode Opc) { |
306 | 2.83k | auto &SVB = State->getStateManager().getSValBuilder(); |
307 | | |
308 | 2.83k | const auto comparison = |
309 | 2.83k | SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType()); |
310 | | |
311 | 2.83k | assert(isa<DefinedSVal>(comparison) && |
312 | 2.83k | "Symbol comparison must be a `DefinedSVal`"); |
313 | | |
314 | 2.83k | return !State->assume(comparison.castAs<DefinedSVal>(), false); |
315 | 2.83k | } |
316 | | |
317 | | } // namespace iterator |
318 | | } // namespace ento |
319 | | } // namespace clang |