/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //= CStringChecker.cpp - Checks calls to C string functions --------*- 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 | | // This defines CStringChecker, which is an assortment of checks on calls |
10 | | // to functions in <string.h>. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "InterCheckerAPI.h" |
15 | | #include "clang/Basic/Builtins.h" |
16 | | #include "clang/Basic/CharInfo.h" |
17 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
18 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
19 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
22 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
23 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
24 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
25 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
26 | | #include "llvm/ADT/STLExtras.h" |
27 | | #include "llvm/ADT/SmallString.h" |
28 | | #include "llvm/ADT/StringExtras.h" |
29 | | #include "llvm/Support/raw_ostream.h" |
30 | | #include <functional> |
31 | | #include <optional> |
32 | | |
33 | | using namespace clang; |
34 | | using namespace ento; |
35 | | using namespace std::placeholders; |
36 | | |
37 | | namespace { |
38 | | struct AnyArgExpr { |
39 | | const Expr *Expression; |
40 | | unsigned ArgumentIndex; |
41 | | }; |
42 | | struct SourceArgExpr : AnyArgExpr {}; |
43 | | struct DestinationArgExpr : AnyArgExpr {}; |
44 | | struct SizeArgExpr : AnyArgExpr {}; |
45 | | |
46 | | using ErrorMessage = SmallString<128>; |
47 | | enum class AccessKind { write, read }; |
48 | | |
49 | | static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription, |
50 | 177 | AccessKind Access) { |
51 | 177 | ErrorMessage Message; |
52 | 177 | llvm::raw_svector_ostream Os(Message); |
53 | | |
54 | | // Function classification like: Memory copy function |
55 | 177 | Os << toUppercase(FunctionDescription.front()) |
56 | 177 | << &FunctionDescription.data()[1]; |
57 | | |
58 | 177 | if (Access == AccessKind::write) { |
59 | 127 | Os << " overflows the destination buffer"; |
60 | 127 | } else { // read access |
61 | 50 | Os << " accesses out-of-bound array element"; |
62 | 50 | } |
63 | | |
64 | 177 | return Message; |
65 | 177 | } |
66 | | |
67 | | enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 }; |
68 | | |
69 | | enum class CharKind { Regular = 0, Wide }; |
70 | | constexpr CharKind CK_Regular = CharKind::Regular; |
71 | | constexpr CharKind CK_Wide = CharKind::Wide; |
72 | | |
73 | 951 | static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) { |
74 | 951 | return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy751 |
75 | 951 | : Ctx.WideCharTy200 ); |
76 | 951 | } |
77 | | |
78 | | class CStringChecker : public Checker< eval::Call, |
79 | | check::PreStmt<DeclStmt>, |
80 | | check::LiveSymbols, |
81 | | check::DeadSymbols, |
82 | | check::RegionChanges |
83 | | > { |
84 | | mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap, |
85 | | BT_NotCString, BT_AdditionOverflow, BT_UninitRead; |
86 | | |
87 | | mutable const char *CurrentFunctionDescription = nullptr; |
88 | | |
89 | | public: |
90 | | /// The filter is used to filter out the diagnostics which are not enabled by |
91 | | /// the user. |
92 | | struct CStringChecksFilter { |
93 | | bool CheckCStringNullArg = false; |
94 | | bool CheckCStringOutOfBounds = false; |
95 | | bool CheckCStringBufferOverlap = false; |
96 | | bool CheckCStringNotNullTerm = false; |
97 | | bool CheckCStringUninitializedRead = false; |
98 | | |
99 | | CheckerNameRef CheckNameCStringNullArg; |
100 | | CheckerNameRef CheckNameCStringOutOfBounds; |
101 | | CheckerNameRef CheckNameCStringBufferOverlap; |
102 | | CheckerNameRef CheckNameCStringNotNullTerm; |
103 | | CheckerNameRef CheckNameCStringUninitializedRead; |
104 | | }; |
105 | | |
106 | | CStringChecksFilter Filter; |
107 | | |
108 | 715 | static void *getTag() { static int tag; return &tag; } |
109 | | |
110 | | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
111 | | void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; |
112 | | void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const; |
113 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
114 | | |
115 | | ProgramStateRef |
116 | | checkRegionChanges(ProgramStateRef state, |
117 | | const InvalidatedSymbols *, |
118 | | ArrayRef<const MemRegion *> ExplicitRegions, |
119 | | ArrayRef<const MemRegion *> Regions, |
120 | | const LocationContext *LCtx, |
121 | | const CallEvent *Call) const; |
122 | | |
123 | | using FnCheck = std::function<void(const CStringChecker *, CheckerContext &, |
124 | | const CallExpr *)>; |
125 | | |
126 | | CallDescriptionMap<FnCheck> Callbacks = { |
127 | | {{CDF_MaybeBuiltin, {"memcpy"}, 3}, |
128 | | std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)}, |
129 | | {{CDF_MaybeBuiltin, {"wmemcpy"}, 3}, |
130 | | std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)}, |
131 | | {{CDF_MaybeBuiltin, {"mempcpy"}, 3}, |
132 | | std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)}, |
133 | | {{CDF_None, {"wmempcpy"}, 3}, |
134 | | std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)}, |
135 | | {{CDF_MaybeBuiltin, {"memcmp"}, 3}, |
136 | | std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, |
137 | | {{CDF_MaybeBuiltin, {"wmemcmp"}, 3}, |
138 | | std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)}, |
139 | | {{CDF_MaybeBuiltin, {"memmove"}, 3}, |
140 | | std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)}, |
141 | | {{CDF_MaybeBuiltin, {"wmemmove"}, 3}, |
142 | | std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)}, |
143 | | {{CDF_MaybeBuiltin, {"memset"}, 3}, &CStringChecker::evalMemset}, |
144 | | {{CDF_MaybeBuiltin, {"explicit_memset"}, 3}, &CStringChecker::evalMemset}, |
145 | | {{CDF_MaybeBuiltin, {"strcpy"}, 2}, &CStringChecker::evalStrcpy}, |
146 | | {{CDF_MaybeBuiltin, {"strncpy"}, 3}, &CStringChecker::evalStrncpy}, |
147 | | {{CDF_MaybeBuiltin, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy}, |
148 | | {{CDF_MaybeBuiltin, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy}, |
149 | | {{CDF_MaybeBuiltin, {"strcat"}, 2}, &CStringChecker::evalStrcat}, |
150 | | {{CDF_MaybeBuiltin, {"strncat"}, 3}, &CStringChecker::evalStrncat}, |
151 | | {{CDF_MaybeBuiltin, {"strlcat"}, 3}, &CStringChecker::evalStrlcat}, |
152 | | {{CDF_MaybeBuiltin, {"strlen"}, 1}, &CStringChecker::evalstrLength}, |
153 | | {{CDF_MaybeBuiltin, {"wcslen"}, 1}, &CStringChecker::evalstrLength}, |
154 | | {{CDF_MaybeBuiltin, {"strnlen"}, 2}, &CStringChecker::evalstrnLength}, |
155 | | {{CDF_MaybeBuiltin, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength}, |
156 | | {{CDF_MaybeBuiltin, {"strcmp"}, 2}, &CStringChecker::evalStrcmp}, |
157 | | {{CDF_MaybeBuiltin, {"strncmp"}, 3}, &CStringChecker::evalStrncmp}, |
158 | | {{CDF_MaybeBuiltin, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp}, |
159 | | {{CDF_MaybeBuiltin, {"strncasecmp"}, 3}, |
160 | | &CStringChecker::evalStrncasecmp}, |
161 | | {{CDF_MaybeBuiltin, {"strsep"}, 2}, &CStringChecker::evalStrsep}, |
162 | | {{CDF_MaybeBuiltin, {"bcopy"}, 3}, &CStringChecker::evalBcopy}, |
163 | | {{CDF_MaybeBuiltin, {"bcmp"}, 3}, |
164 | | std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, |
165 | | {{CDF_MaybeBuiltin, {"bzero"}, 2}, &CStringChecker::evalBzero}, |
166 | | {{CDF_MaybeBuiltin, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero}, |
167 | | {{CDF_MaybeBuiltin, {"sprintf"}, 2}, &CStringChecker::evalSprintf}, |
168 | | {{CDF_MaybeBuiltin, {"snprintf"}, 2}, &CStringChecker::evalSnprintf}, |
169 | | }; |
170 | | |
171 | | // These require a bit of special handling. |
172 | | CallDescription StdCopy{{"std", "copy"}, 3}, |
173 | | StdCopyBackward{{"std", "copy_backward"}, 3}; |
174 | | |
175 | | FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const; |
176 | | void evalMemcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const; |
177 | | void evalMempcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const; |
178 | | void evalMemmove(CheckerContext &C, const CallExpr *CE, CharKind CK) const; |
179 | | void evalBcopy(CheckerContext &C, const CallExpr *CE) const; |
180 | | void evalCopyCommon(CheckerContext &C, const CallExpr *CE, |
181 | | ProgramStateRef state, SizeArgExpr Size, |
182 | | DestinationArgExpr Dest, SourceArgExpr Source, |
183 | | bool Restricted, bool IsMempcpy, CharKind CK) const; |
184 | | |
185 | | void evalMemcmp(CheckerContext &C, const CallExpr *CE, CharKind CK) const; |
186 | | |
187 | | void evalstrLength(CheckerContext &C, const CallExpr *CE) const; |
188 | | void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; |
189 | | void evalstrLengthCommon(CheckerContext &C, |
190 | | const CallExpr *CE, |
191 | | bool IsStrnlen = false) const; |
192 | | |
193 | | void evalStrcpy(CheckerContext &C, const CallExpr *CE) const; |
194 | | void evalStrncpy(CheckerContext &C, const CallExpr *CE) const; |
195 | | void evalStpcpy(CheckerContext &C, const CallExpr *CE) const; |
196 | | void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const; |
197 | | void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd, |
198 | | bool IsBounded, ConcatFnKind appendK, |
199 | | bool returnPtr = true) const; |
200 | | |
201 | | void evalStrcat(CheckerContext &C, const CallExpr *CE) const; |
202 | | void evalStrncat(CheckerContext &C, const CallExpr *CE) const; |
203 | | void evalStrlcat(CheckerContext &C, const CallExpr *CE) const; |
204 | | |
205 | | void evalStrcmp(CheckerContext &C, const CallExpr *CE) const; |
206 | | void evalStrncmp(CheckerContext &C, const CallExpr *CE) const; |
207 | | void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const; |
208 | | void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const; |
209 | | void evalStrcmpCommon(CheckerContext &C, |
210 | | const CallExpr *CE, |
211 | | bool IsBounded = false, |
212 | | bool IgnoreCase = false) const; |
213 | | |
214 | | void evalStrsep(CheckerContext &C, const CallExpr *CE) const; |
215 | | |
216 | | void evalStdCopy(CheckerContext &C, const CallExpr *CE) const; |
217 | | void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; |
218 | | void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; |
219 | | void evalMemset(CheckerContext &C, const CallExpr *CE) const; |
220 | | void evalBzero(CheckerContext &C, const CallExpr *CE) const; |
221 | | |
222 | | void evalSprintf(CheckerContext &C, const CallExpr *CE) const; |
223 | | void evalSnprintf(CheckerContext &C, const CallExpr *CE) const; |
224 | | void evalSprintfCommon(CheckerContext &C, const CallExpr *CE, bool IsBounded, |
225 | | bool IsBuiltin) const; |
226 | | |
227 | | // Utility methods |
228 | | std::pair<ProgramStateRef , ProgramStateRef > |
229 | | static assumeZero(CheckerContext &C, |
230 | | ProgramStateRef state, SVal V, QualType Ty); |
231 | | |
232 | | static ProgramStateRef setCStringLength(ProgramStateRef state, |
233 | | const MemRegion *MR, |
234 | | SVal strLength); |
235 | | static SVal getCStringLengthForRegion(CheckerContext &C, |
236 | | ProgramStateRef &state, |
237 | | const Expr *Ex, |
238 | | const MemRegion *MR, |
239 | | bool hypothetical); |
240 | | SVal getCStringLength(CheckerContext &C, |
241 | | ProgramStateRef &state, |
242 | | const Expr *Ex, |
243 | | SVal Buf, |
244 | | bool hypothetical = false) const; |
245 | | |
246 | | const StringLiteral *getCStringLiteral(CheckerContext &C, |
247 | | ProgramStateRef &state, |
248 | | const Expr *expr, |
249 | | SVal val) const; |
250 | | |
251 | | /// Invalidate the destination buffer determined by characters copied. |
252 | | static ProgramStateRef |
253 | | invalidateDestinationBufferBySize(CheckerContext &C, ProgramStateRef S, |
254 | | const Expr *BufE, SVal BufV, SVal SizeV, |
255 | | QualType SizeTy); |
256 | | |
257 | | /// Operation never overflows, do not invalidate the super region. |
258 | | static ProgramStateRef invalidateDestinationBufferNeverOverflows( |
259 | | CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV); |
260 | | |
261 | | /// We do not know whether the operation can overflow (e.g. size is unknown), |
262 | | /// invalidate the super region and escape related pointers. |
263 | | static ProgramStateRef invalidateDestinationBufferAlwaysEscapeSuperRegion( |
264 | | CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV); |
265 | | |
266 | | /// Invalidate the source buffer for escaping pointers. |
267 | | static ProgramStateRef invalidateSourceBuffer(CheckerContext &C, |
268 | | ProgramStateRef S, |
269 | | const Expr *BufE, SVal BufV); |
270 | | |
271 | | /// @param InvalidationTraitOperations Determine how to invlidate the |
272 | | /// MemRegion by setting the invalidation traits. Return true to cause pointer |
273 | | /// escape, or false otherwise. |
274 | | static ProgramStateRef invalidateBufferAux( |
275 | | CheckerContext &C, ProgramStateRef State, const Expr *Ex, SVal V, |
276 | | llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &, |
277 | | const MemRegion *)> |
278 | | InvalidationTraitOperations); |
279 | | |
280 | | static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, |
281 | | const MemRegion *MR); |
282 | | |
283 | | static bool memsetAux(const Expr *DstBuffer, SVal CharE, |
284 | | const Expr *Size, CheckerContext &C, |
285 | | ProgramStateRef &State); |
286 | | |
287 | | // Re-usable checks |
288 | | ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef State, |
289 | | AnyArgExpr Arg, SVal l) const; |
290 | | ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state, |
291 | | AnyArgExpr Buffer, SVal Element, |
292 | | AccessKind Access, |
293 | | CharKind CK = CharKind::Regular) const; |
294 | | ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State, |
295 | | AnyArgExpr Buffer, SizeArgExpr Size, |
296 | | AccessKind Access, |
297 | | CharKind CK = CharKind::Regular) const; |
298 | | ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state, |
299 | | SizeArgExpr Size, AnyArgExpr First, |
300 | | AnyArgExpr Second, |
301 | | CharKind CK = CharKind::Regular) const; |
302 | | void emitOverlapBug(CheckerContext &C, |
303 | | ProgramStateRef state, |
304 | | const Stmt *First, |
305 | | const Stmt *Second) const; |
306 | | |
307 | | void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, |
308 | | StringRef WarningMsg) const; |
309 | | void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State, |
310 | | const Stmt *S, StringRef WarningMsg) const; |
311 | | void emitNotCStringBug(CheckerContext &C, ProgramStateRef State, |
312 | | const Stmt *S, StringRef WarningMsg) const; |
313 | | void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const; |
314 | | void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State, |
315 | | const Expr *E) const; |
316 | | ProgramStateRef checkAdditionOverflow(CheckerContext &C, |
317 | | ProgramStateRef state, |
318 | | NonLoc left, |
319 | | NonLoc right) const; |
320 | | |
321 | | // Return true if the destination buffer of the copy function may be in bound. |
322 | | // Expects SVal of Size to be positive and unsigned. |
323 | | // Expects SVal of FirstBuf to be a FieldRegion. |
324 | | static bool isFirstBufInBound(CheckerContext &C, ProgramStateRef State, |
325 | | SVal BufVal, QualType BufTy, SVal LengthVal, |
326 | | QualType LengthTy); |
327 | | }; |
328 | | |
329 | | } //end anonymous namespace |
330 | | |
331 | | REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal) |
332 | | |
333 | | //===----------------------------------------------------------------------===// |
334 | | // Individual checks and utility methods. |
335 | | //===----------------------------------------------------------------------===// |
336 | | |
337 | | std::pair<ProgramStateRef , ProgramStateRef > |
338 | | CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, |
339 | 5.38k | QualType Ty) { |
340 | 5.38k | std::optional<DefinedSVal> val = V.getAs<DefinedSVal>(); |
341 | 5.38k | if (!val) |
342 | 15 | return std::pair<ProgramStateRef , ProgramStateRef >(state, state); |
343 | | |
344 | 5.36k | SValBuilder &svalBuilder = C.getSValBuilder(); |
345 | 5.36k | DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty); |
346 | 5.36k | return state->assume(svalBuilder.evalEQ(state, *val, zero)); |
347 | 5.38k | } |
348 | | |
349 | | ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, |
350 | | ProgramStateRef State, |
351 | 4.25k | AnyArgExpr Arg, SVal l) const { |
352 | | // If a previous check has failed, propagate the failure. |
353 | 4.25k | if (!State) |
354 | 0 | return nullptr; |
355 | | |
356 | 4.25k | ProgramStateRef stateNull, stateNonNull; |
357 | 4.25k | std::tie(stateNull, stateNonNull) = |
358 | 4.25k | assumeZero(C, State, l, Arg.Expression->getType()); |
359 | | |
360 | 4.25k | if (stateNull && !stateNonNull619 ) { |
361 | 187 | if (Filter.CheckCStringNullArg) { |
362 | 187 | SmallString<80> buf; |
363 | 187 | llvm::raw_svector_ostream OS(buf); |
364 | 187 | assert(CurrentFunctionDescription); |
365 | 187 | OS << "Null pointer passed as " << (Arg.ArgumentIndex + 1) |
366 | 187 | << llvm::getOrdinalSuffix(Arg.ArgumentIndex + 1) << " argument to " |
367 | 187 | << CurrentFunctionDescription; |
368 | | |
369 | 187 | emitNullArgBug(C, stateNull, Arg.Expression, OS.str()); |
370 | 187 | } |
371 | 187 | return nullptr; |
372 | 187 | } |
373 | | |
374 | | // From here on, assume that the value is non-null. |
375 | 4.06k | assert(stateNonNull); |
376 | 4.06k | return stateNonNull; |
377 | 4.06k | } |
378 | | |
379 | | // FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? |
380 | | ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, |
381 | | ProgramStateRef state, |
382 | | AnyArgExpr Buffer, SVal Element, |
383 | | AccessKind Access, |
384 | 2.00k | CharKind CK) const { |
385 | | |
386 | | // If a previous check has failed, propagate the failure. |
387 | 2.00k | if (!state) |
388 | 0 | return nullptr; |
389 | | |
390 | | // Check for out of bound array element access. |
391 | 2.00k | const MemRegion *R = Element.getAsRegion(); |
392 | 2.00k | if (!R) |
393 | 4 | return state; |
394 | | |
395 | 2.00k | const auto *ER = dyn_cast<ElementRegion>(R); |
396 | 2.00k | if (!ER) |
397 | 55 | return state; |
398 | | |
399 | 1.94k | SValBuilder &svalBuilder = C.getSValBuilder(); |
400 | 1.94k | ASTContext &Ctx = svalBuilder.getContext(); |
401 | | |
402 | | // Get the index of the accessed element. |
403 | 1.94k | NonLoc Idx = ER->getIndex(); |
404 | | |
405 | 1.94k | if (CK == CharKind::Regular) { |
406 | 1.63k | if (ER->getValueType() != Ctx.CharTy) |
407 | 1 | return state; |
408 | 1.63k | } else { |
409 | 312 | if (ER->getValueType() != Ctx.WideCharTy) |
410 | 0 | return state; |
411 | | |
412 | 312 | QualType SizeTy = Ctx.getSizeType(); |
413 | 312 | NonLoc WideSize = |
414 | 312 | svalBuilder |
415 | 312 | .makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(), |
416 | 312 | SizeTy) |
417 | 312 | .castAs<NonLoc>(); |
418 | 312 | SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy); |
419 | 312 | if (Offset.isUnknown()) |
420 | 0 | return state; |
421 | 312 | Idx = Offset.castAs<NonLoc>(); |
422 | 312 | } |
423 | | |
424 | | // Get the size of the array. |
425 | 1.94k | const auto *superReg = cast<SubRegion>(ER->getSuperRegion()); |
426 | 1.94k | DefinedOrUnknownSVal Size = |
427 | 1.94k | getDynamicExtent(state, superReg, C.getSValBuilder()); |
428 | | |
429 | 1.94k | ProgramStateRef StInBound, StOutBound; |
430 | 1.94k | std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size); |
431 | 1.94k | if (StOutBound && !StInBound372 ) { |
432 | | // These checks are either enabled by the CString out-of-bounds checker |
433 | | // explicitly or implicitly by the Malloc checker. |
434 | | // In the latter case we only do modeling but do not emit warning. |
435 | 203 | if (!Filter.CheckCStringOutOfBounds) |
436 | 26 | return nullptr; |
437 | | |
438 | | // Emit a bug report. |
439 | 177 | ErrorMessage Message = |
440 | 177 | createOutOfBoundErrorMsg(CurrentFunctionDescription, Access); |
441 | 177 | emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message); |
442 | 177 | return nullptr; |
443 | 203 | } |
444 | | |
445 | | // Ensure that we wouldn't read uninitialized value. |
446 | 1.74k | if (Access == AccessKind::read) { |
447 | 491 | if (Filter.CheckCStringUninitializedRead && |
448 | 491 | StInBound->getSVal(ER).isUndef()15 ) { |
449 | 3 | emitUninitializedReadBug(C, StInBound, Buffer.Expression); |
450 | 3 | return nullptr; |
451 | 3 | } |
452 | 491 | } |
453 | | |
454 | | // Array bound check succeeded. From this point forward the array bound |
455 | | // should always succeed. |
456 | 1.74k | return StInBound; |
457 | 1.74k | } |
458 | | |
459 | | ProgramStateRef |
460 | | CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, |
461 | | AnyArgExpr Buffer, SizeArgExpr Size, |
462 | 893 | AccessKind Access, CharKind CK) const { |
463 | | // If a previous check has failed, propagate the failure. |
464 | 893 | if (!State) |
465 | 54 | return nullptr; |
466 | | |
467 | 839 | SValBuilder &svalBuilder = C.getSValBuilder(); |
468 | 839 | ASTContext &Ctx = svalBuilder.getContext(); |
469 | | |
470 | 839 | QualType SizeTy = Size.Expression->getType(); |
471 | 839 | QualType PtrTy = getCharPtrType(Ctx, CK); |
472 | | |
473 | | // Check that the first buffer is non-null. |
474 | 839 | SVal BufVal = C.getSVal(Buffer.Expression); |
475 | 839 | State = checkNonNull(C, State, Buffer, BufVal); |
476 | 839 | if (!State) |
477 | 6 | return nullptr; |
478 | | |
479 | | // If out-of-bounds checking is turned off, skip the rest. |
480 | 833 | if (!Filter.CheckCStringOutOfBounds) |
481 | 159 | return State; |
482 | | |
483 | 674 | SVal BufStart = |
484 | 674 | svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); |
485 | | |
486 | | // Check if the first byte of the buffer is accessible. |
487 | 674 | State = CheckLocation(C, State, Buffer, BufStart, Access, CK); |
488 | 674 | if (!State) |
489 | 5 | return nullptr; |
490 | | |
491 | | // Get the access length and make sure it is known. |
492 | | // FIXME: This assumes the caller has already checked that the access length |
493 | | // is positive. And that it's unsigned. |
494 | 669 | SVal LengthVal = C.getSVal(Size.Expression); |
495 | 669 | std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); |
496 | 669 | if (!Length) |
497 | 12 | return State; |
498 | | |
499 | | // Compute the offset of the last element to be accessed: size-1. |
500 | 657 | NonLoc One = svalBuilder.makeIntVal(1, SizeTy).castAs<NonLoc>(); |
501 | 657 | SVal Offset = svalBuilder.evalBinOpNN(State, BO_Sub, *Length, One, SizeTy); |
502 | 657 | if (Offset.isUnknown()) |
503 | 4 | return nullptr; |
504 | 653 | NonLoc LastOffset = Offset.castAs<NonLoc>(); |
505 | | |
506 | | // Check that the first buffer is sufficiently long. |
507 | 653 | if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) { |
508 | | |
509 | 649 | SVal BufEnd = |
510 | 649 | svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); |
511 | 649 | State = CheckLocation(C, State, Buffer, BufEnd, Access, CK); |
512 | | |
513 | | // If the buffer isn't large enough, abort. |
514 | 649 | if (!State) |
515 | 92 | return nullptr; |
516 | 649 | } |
517 | | |
518 | | // Large enough or not, return this state! |
519 | 561 | return State; |
520 | 653 | } |
521 | | |
522 | | ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, |
523 | | ProgramStateRef state, |
524 | | SizeArgExpr Size, AnyArgExpr First, |
525 | | AnyArgExpr Second, |
526 | 662 | CharKind CK) const { |
527 | 662 | if (!Filter.CheckCStringBufferOverlap) |
528 | 176 | return state; |
529 | | |
530 | | // Do a simple check for overlap: if the two arguments are from the same |
531 | | // buffer, see if the end of the first is greater than the start of the second |
532 | | // or vice versa. |
533 | | |
534 | | // If a previous check has failed, propagate the failure. |
535 | 486 | if (!state) |
536 | 63 | return nullptr; |
537 | | |
538 | 423 | ProgramStateRef stateTrue, stateFalse; |
539 | | |
540 | | // Assume different address spaces cannot overlap. |
541 | 423 | if (First.Expression->getType()->getPointeeType().getAddressSpace() != |
542 | 423 | Second.Expression->getType()->getPointeeType().getAddressSpace()) |
543 | 0 | return state; |
544 | | |
545 | | // Get the buffer values and make sure they're known locations. |
546 | 423 | const LocationContext *LCtx = C.getLocationContext(); |
547 | 423 | SVal firstVal = state->getSVal(First.Expression, LCtx); |
548 | 423 | SVal secondVal = state->getSVal(Second.Expression, LCtx); |
549 | | |
550 | 423 | std::optional<Loc> firstLoc = firstVal.getAs<Loc>(); |
551 | 423 | if (!firstLoc) |
552 | 0 | return state; |
553 | | |
554 | 423 | std::optional<Loc> secondLoc = secondVal.getAs<Loc>(); |
555 | 423 | if (!secondLoc) |
556 | 1 | return state; |
557 | | |
558 | | // Are the two values the same? |
559 | 422 | SValBuilder &svalBuilder = C.getSValBuilder(); |
560 | 422 | std::tie(stateTrue, stateFalse) = |
561 | 422 | state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc)); |
562 | | |
563 | 422 | if (stateTrue && !stateFalse77 ) { |
564 | | // If the values are known to be equal, that's automatically an overlap. |
565 | 36 | emitOverlapBug(C, stateTrue, First.Expression, Second.Expression); |
566 | 36 | return nullptr; |
567 | 36 | } |
568 | | |
569 | | // assume the two expressions are not equal. |
570 | 386 | assert(stateFalse); |
571 | 386 | state = stateFalse; |
572 | | |
573 | | // Which value comes first? |
574 | 386 | QualType cmpTy = svalBuilder.getConditionType(); |
575 | 386 | SVal reverse = |
576 | 386 | svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy); |
577 | 386 | std::optional<DefinedOrUnknownSVal> reverseTest = |
578 | 386 | reverse.getAs<DefinedOrUnknownSVal>(); |
579 | 386 | if (!reverseTest) |
580 | 0 | return state; |
581 | | |
582 | 386 | std::tie(stateTrue, stateFalse) = state->assume(*reverseTest); |
583 | 386 | if (stateTrue) { |
584 | 361 | if (stateFalse) { |
585 | | // If we don't know which one comes first, we can't perform this test. |
586 | 333 | return state; |
587 | 333 | } else { |
588 | | // Switch the values so that firstVal is before secondVal. |
589 | 28 | std::swap(firstLoc, secondLoc); |
590 | | |
591 | | // Switch the Exprs as well, so that they still correspond. |
592 | 28 | std::swap(First, Second); |
593 | 28 | } |
594 | 361 | } |
595 | | |
596 | | // Get the length, and make sure it too is known. |
597 | 53 | SVal LengthVal = state->getSVal(Size.Expression, LCtx); |
598 | 53 | std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); |
599 | 53 | if (!Length) |
600 | 0 | return state; |
601 | | |
602 | | // Convert the first buffer's start address to char*. |
603 | | // Bail out if the cast fails. |
604 | 53 | ASTContext &Ctx = svalBuilder.getContext(); |
605 | 53 | QualType CharPtrTy = getCharPtrType(Ctx, CK); |
606 | 53 | SVal FirstStart = |
607 | 53 | svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType()); |
608 | 53 | std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); |
609 | 53 | if (!FirstStartLoc) |
610 | 0 | return state; |
611 | | |
612 | | // Compute the end of the first buffer. Bail out if THAT fails. |
613 | 53 | SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc, |
614 | 53 | *Length, CharPtrTy); |
615 | 53 | std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); |
616 | 53 | if (!FirstEndLoc) |
617 | 0 | return state; |
618 | | |
619 | | // Is the end of the first buffer past the start of the second buffer? |
620 | 53 | SVal Overlap = |
621 | 53 | svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy); |
622 | 53 | std::optional<DefinedOrUnknownSVal> OverlapTest = |
623 | 53 | Overlap.getAs<DefinedOrUnknownSVal>(); |
624 | 53 | if (!OverlapTest) |
625 | 0 | return state; |
626 | | |
627 | 53 | std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest); |
628 | | |
629 | 53 | if (stateTrue && !stateFalse29 ) { |
630 | | // Overlap! |
631 | 29 | emitOverlapBug(C, stateTrue, First.Expression, Second.Expression); |
632 | 29 | return nullptr; |
633 | 29 | } |
634 | | |
635 | | // assume the two expressions don't overlap. |
636 | 24 | assert(stateFalse); |
637 | 24 | return stateFalse; |
638 | 24 | } |
639 | | |
640 | | void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, |
641 | 65 | const Stmt *First, const Stmt *Second) const { |
642 | 65 | ExplodedNode *N = C.generateErrorNode(state); |
643 | 65 | if (!N) |
644 | 0 | return; |
645 | | |
646 | 65 | if (!BT_Overlap) |
647 | 11 | BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap, |
648 | 11 | categories::UnixAPI, "Improper arguments")); |
649 | | |
650 | | // Generate a report for this bug. |
651 | 65 | auto report = std::make_unique<PathSensitiveBugReport>( |
652 | 65 | *BT_Overlap, "Arguments must not be overlapping buffers", N); |
653 | 65 | report->addRange(First->getSourceRange()); |
654 | 65 | report->addRange(Second->getSourceRange()); |
655 | | |
656 | 65 | C.emitReport(std::move(report)); |
657 | 65 | } |
658 | | |
659 | | void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, |
660 | 187 | const Stmt *S, StringRef WarningMsg) const { |
661 | 187 | if (ExplodedNode *N = C.generateErrorNode(State)) { |
662 | 187 | if (!BT_Null) { |
663 | | // FIXME: This call uses the string constant 'categories::UnixAPI' as the |
664 | | // description of the bug; it should be replaced by a real description. |
665 | 16 | BT_Null.reset( |
666 | 16 | new BugType(Filter.CheckNameCStringNullArg, categories::UnixAPI)); |
667 | 16 | } |
668 | | |
669 | 187 | auto Report = |
670 | 187 | std::make_unique<PathSensitiveBugReport>(*BT_Null, WarningMsg, N); |
671 | 187 | Report->addRange(S->getSourceRange()); |
672 | 187 | if (const auto *Ex = dyn_cast<Expr>(S)) |
673 | 187 | bugreporter::trackExpressionValue(N, Ex, *Report); |
674 | 187 | C.emitReport(std::move(Report)); |
675 | 187 | } |
676 | 187 | } |
677 | | |
678 | | void CStringChecker::emitUninitializedReadBug(CheckerContext &C, |
679 | | ProgramStateRef State, |
680 | 3 | const Expr *E) const { |
681 | 3 | if (ExplodedNode *N = C.generateErrorNode(State)) { |
682 | 3 | const char *Msg = |
683 | 3 | "Bytes string function accesses uninitialized/garbage values"; |
684 | 3 | if (!BT_UninitRead) |
685 | 1 | BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead, |
686 | 1 | "Accessing unitialized/garbage values")); |
687 | | |
688 | 3 | auto Report = |
689 | 3 | std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N); |
690 | 3 | Report->addRange(E->getSourceRange()); |
691 | 3 | bugreporter::trackExpressionValue(N, E, *Report); |
692 | 3 | C.emitReport(std::move(Report)); |
693 | 3 | } |
694 | 3 | } |
695 | | |
696 | | void CStringChecker::emitOutOfBoundsBug(CheckerContext &C, |
697 | | ProgramStateRef State, const Stmt *S, |
698 | 177 | StringRef WarningMsg) const { |
699 | 177 | if (ExplodedNode *N = C.generateErrorNode(State)) { |
700 | 177 | if (!BT_Bounds) |
701 | 12 | BT_Bounds.reset(new BugType(Filter.CheckCStringOutOfBounds |
702 | 12 | ? Filter.CheckNameCStringOutOfBounds |
703 | 12 | : Filter.CheckNameCStringNullArg0 , |
704 | 12 | "Out-of-bound array access")); |
705 | | |
706 | | // FIXME: It would be nice to eventually make this diagnostic more clear, |
707 | | // e.g., by referencing the original declaration or by saying *why* this |
708 | | // reference is outside the range. |
709 | 177 | auto Report = |
710 | 177 | std::make_unique<PathSensitiveBugReport>(*BT_Bounds, WarningMsg, N); |
711 | 177 | Report->addRange(S->getSourceRange()); |
712 | 177 | C.emitReport(std::move(Report)); |
713 | 177 | } |
714 | 177 | } |
715 | | |
716 | | void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, |
717 | | const Stmt *S, |
718 | 54 | StringRef WarningMsg) const { |
719 | 54 | if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { |
720 | 54 | if (!BT_NotCString) { |
721 | | // FIXME: This call uses the string constant 'categories::UnixAPI' as the |
722 | | // description of the bug; it should be replaced by a real description. |
723 | 8 | BT_NotCString.reset( |
724 | 8 | new BugType(Filter.CheckNameCStringNotNullTerm, categories::UnixAPI)); |
725 | 8 | } |
726 | | |
727 | 54 | auto Report = |
728 | 54 | std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N); |
729 | | |
730 | 54 | Report->addRange(S->getSourceRange()); |
731 | 54 | C.emitReport(std::move(Report)); |
732 | 54 | } |
733 | 54 | } |
734 | | |
735 | | void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, |
736 | 0 | ProgramStateRef State) const { |
737 | 0 | if (ExplodedNode *N = C.generateErrorNode(State)) { |
738 | 0 | if (!BT_AdditionOverflow) { |
739 | | // FIXME: This call uses the word "API" as the description of the bug; |
740 | | // it should be replaced by a better error message (if this unlikely |
741 | | // situation continues to exist as a separate bug type). |
742 | 0 | BT_AdditionOverflow.reset( |
743 | 0 | new BugType(Filter.CheckNameCStringOutOfBounds, "API")); |
744 | 0 | } |
745 | | |
746 | | // This isn't a great error message, but this should never occur in real |
747 | | // code anyway -- you'd have to create a buffer longer than a size_t can |
748 | | // represent, which is sort of a contradiction. |
749 | 0 | const char *WarningMsg = |
750 | 0 | "This expression will create a string whose length is too big to " |
751 | 0 | "be represented as a size_t"; |
752 | |
|
753 | 0 | auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow, |
754 | 0 | WarningMsg, N); |
755 | 0 | C.emitReport(std::move(Report)); |
756 | 0 | } |
757 | 0 | } |
758 | | |
759 | | ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, |
760 | | ProgramStateRef state, |
761 | | NonLoc left, |
762 | 116 | NonLoc right) const { |
763 | | // If out-of-bounds checking is turned off, skip the rest. |
764 | 116 | if (!Filter.CheckCStringOutOfBounds) |
765 | 39 | return state; |
766 | | |
767 | | // If a previous check has failed, propagate the failure. |
768 | 77 | if (!state) |
769 | 0 | return nullptr; |
770 | | |
771 | 77 | SValBuilder &svalBuilder = C.getSValBuilder(); |
772 | 77 | BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); |
773 | | |
774 | 77 | QualType sizeTy = svalBuilder.getContext().getSizeType(); |
775 | 77 | const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); |
776 | 77 | NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); |
777 | | |
778 | 77 | SVal maxMinusRight; |
779 | 77 | if (isa<nonloc::ConcreteInt>(right)) { |
780 | 65 | maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, |
781 | 65 | sizeTy); |
782 | 65 | } else { |
783 | | // Try switching the operands. (The order of these two assignments is |
784 | | // important!) |
785 | 12 | maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left, |
786 | 12 | sizeTy); |
787 | 12 | left = right; |
788 | 12 | } |
789 | | |
790 | 77 | if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { |
791 | 77 | QualType cmpTy = svalBuilder.getConditionType(); |
792 | | // If left > max - right, we have an overflow. |
793 | 77 | SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, |
794 | 77 | *maxMinusRightNL, cmpTy); |
795 | | |
796 | 77 | ProgramStateRef stateOverflow, stateOkay; |
797 | 77 | std::tie(stateOverflow, stateOkay) = |
798 | 77 | state->assume(willOverflow.castAs<DefinedOrUnknownSVal>()); |
799 | | |
800 | 77 | if (stateOverflow && !stateOkay0 ) { |
801 | | // We have an overflow. Emit a bug report. |
802 | 0 | emitAdditionOverflowBug(C, stateOverflow); |
803 | 0 | return nullptr; |
804 | 0 | } |
805 | | |
806 | | // From now on, assume an overflow didn't occur. |
807 | 77 | assert(stateOkay); |
808 | 77 | state = stateOkay; |
809 | 77 | } |
810 | | |
811 | 77 | return state; |
812 | 77 | } |
813 | | |
814 | | ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state, |
815 | | const MemRegion *MR, |
816 | 416 | SVal strLength) { |
817 | 416 | assert(!strLength.isUndef() && "Attempt to set an undefined string length"); |
818 | | |
819 | 416 | MR = MR->StripCasts(); |
820 | | |
821 | 416 | switch (MR->getKind()) { |
822 | 0 | case MemRegion::StringRegionKind: |
823 | | // FIXME: This can happen if we strcpy() into a string region. This is |
824 | | // undefined [C99 6.4.5p6], but we should still warn about it. |
825 | 0 | return state; |
826 | | |
827 | 116 | case MemRegion::SymbolicRegionKind: |
828 | 116 | case MemRegion::AllocaRegionKind: |
829 | 408 | case MemRegion::NonParamVarRegionKind: |
830 | 408 | case MemRegion::ParamVarRegionKind: |
831 | 412 | case MemRegion::FieldRegionKind: |
832 | 412 | case MemRegion::ObjCIvarRegionKind: |
833 | | // These are the types we can currently track string lengths for. |
834 | 412 | break; |
835 | | |
836 | 4 | case MemRegion::ElementRegionKind: |
837 | | // FIXME: Handle element regions by upper-bounding the parent region's |
838 | | // string length. |
839 | 4 | return state; |
840 | | |
841 | 0 | default: |
842 | | // Other regions (mostly non-data) can't have a reliable C string length. |
843 | | // For now, just ignore the change. |
844 | | // FIXME: These are rare but not impossible. We should output some kind of |
845 | | // warning for things like strcpy((char[]){'a', 0}, "b"); |
846 | 0 | return state; |
847 | 416 | } |
848 | | |
849 | 412 | if (strLength.isUnknown()) |
850 | 51 | return state->remove<CStringLength>(MR); |
851 | | |
852 | 361 | return state->set<CStringLength>(MR, strLength); |
853 | 412 | } |
854 | | |
855 | | SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, |
856 | | ProgramStateRef &state, |
857 | | const Expr *Ex, |
858 | | const MemRegion *MR, |
859 | 1.32k | bool hypothetical) { |
860 | 1.32k | if (!hypothetical) { |
861 | | // If there's a recorded length, go ahead and return it. |
862 | 1.27k | const SVal *Recorded = state->get<CStringLength>(MR); |
863 | 1.27k | if (Recorded) |
864 | 685 | return *Recorded; |
865 | 1.27k | } |
866 | | |
867 | | // Otherwise, get a new symbol and update the state. |
868 | 641 | SValBuilder &svalBuilder = C.getSValBuilder(); |
869 | 641 | QualType sizeTy = svalBuilder.getContext().getSizeType(); |
870 | 641 | SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(), |
871 | 641 | MR, Ex, sizeTy, |
872 | 641 | C.getLocationContext(), |
873 | 641 | C.blockCount()); |
874 | | |
875 | 641 | if (!hypothetical) { |
876 | 589 | if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { |
877 | | // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4 |
878 | 589 | BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); |
879 | 589 | const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); |
880 | 589 | llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4); |
881 | 589 | const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt, |
882 | 589 | fourInt); |
883 | 589 | NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt); |
884 | 589 | SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, |
885 | 589 | maxLength, sizeTy); |
886 | 589 | state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true); |
887 | 589 | } |
888 | 589 | state = state->set<CStringLength>(MR, strLength); |
889 | 589 | } |
890 | | |
891 | 641 | return strLength; |
892 | 1.32k | } |
893 | | |
894 | | SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, |
895 | | const Expr *Ex, SVal Buf, |
896 | 2.56k | bool hypothetical) const { |
897 | 2.56k | const MemRegion *MR = Buf.getAsRegion(); |
898 | 2.56k | if (!MR) { |
899 | | // If we can't get a region, see if it's something we /know/ isn't a |
900 | | // C string. In the context of locations, the only time we can issue such |
901 | | // a warning is for labels. |
902 | 20 | if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { |
903 | 14 | if (Filter.CheckCStringNotNullTerm) { |
904 | 14 | SmallString<120> buf; |
905 | 14 | llvm::raw_svector_ostream os(buf); |
906 | 14 | assert(CurrentFunctionDescription); |
907 | 14 | os << "Argument to " << CurrentFunctionDescription |
908 | 14 | << " is the address of the label '" << Label->getLabel()->getName() |
909 | 14 | << "', which is not a null-terminated string"; |
910 | | |
911 | 14 | emitNotCStringBug(C, state, Ex, os.str()); |
912 | 14 | } |
913 | 14 | return UndefinedVal(); |
914 | 14 | } |
915 | | |
916 | | // If it's not a region and not a label, give up. |
917 | 6 | return UnknownVal(); |
918 | 20 | } |
919 | | |
920 | | // If we have a region, strip casts from it and see if we can figure out |
921 | | // its length. For anything we can't figure out, just return UnknownVal. |
922 | 2.54k | MR = MR->StripCasts(); |
923 | | |
924 | 2.54k | switch (MR->getKind()) { |
925 | 1.12k | case MemRegion::StringRegionKind: { |
926 | | // Modifying the contents of string regions is undefined [C99 6.4.5p6], |
927 | | // so we can assume that the byte length is the correct C string length. |
928 | 1.12k | SValBuilder &svalBuilder = C.getSValBuilder(); |
929 | 1.12k | QualType sizeTy = svalBuilder.getContext().getSizeType(); |
930 | 1.12k | const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral(); |
931 | 1.12k | return svalBuilder.makeIntVal(strLit->getLength(), sizeTy); |
932 | 0 | } |
933 | 656 | case MemRegion::SymbolicRegionKind: |
934 | 656 | case MemRegion::AllocaRegionKind: |
935 | 1.29k | case MemRegion::NonParamVarRegionKind: |
936 | 1.29k | case MemRegion::ParamVarRegionKind: |
937 | 1.32k | case MemRegion::FieldRegionKind: |
938 | 1.32k | case MemRegion::ObjCIvarRegionKind: |
939 | 1.32k | return getCStringLengthForRegion(C, state, Ex, MR, hypothetical); |
940 | 7 | case MemRegion::CompoundLiteralRegionKind: |
941 | | // FIXME: Can we track this? Is it necessary? |
942 | 7 | return UnknownVal(); |
943 | 47 | case MemRegion::ElementRegionKind: |
944 | | // FIXME: How can we handle this? It's not good enough to subtract the |
945 | | // offset from the base string length; consider "123\x00567" and &a[5]. |
946 | 47 | return UnknownVal(); |
947 | 40 | default: |
948 | | // Other regions (mostly non-data) can't have a reliable C string length. |
949 | | // In this case, an error is emitted and UndefinedVal is returned. |
950 | | // The caller should always be prepared to handle this case. |
951 | 40 | if (Filter.CheckCStringNotNullTerm) { |
952 | 40 | SmallString<120> buf; |
953 | 40 | llvm::raw_svector_ostream os(buf); |
954 | | |
955 | 40 | assert(CurrentFunctionDescription); |
956 | 40 | os << "Argument to " << CurrentFunctionDescription << " is "; |
957 | | |
958 | 40 | if (SummarizeRegion(os, C.getASTContext(), MR)) |
959 | 40 | os << ", which is not a null-terminated string"; |
960 | 0 | else |
961 | 0 | os << "not a null-terminated string"; |
962 | | |
963 | 40 | emitNotCStringBug(C, state, Ex, os.str()); |
964 | 40 | } |
965 | 40 | return UndefinedVal(); |
966 | 2.54k | } |
967 | 2.54k | } |
968 | | |
969 | | const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, |
970 | 752 | ProgramStateRef &state, const Expr *expr, SVal val) const { |
971 | | |
972 | | // Get the memory region pointed to by the val. |
973 | 752 | const MemRegion *bufRegion = val.getAsRegion(); |
974 | 752 | if (!bufRegion) |
975 | 5 | return nullptr; |
976 | | |
977 | | // Strip casts off the memory region. |
978 | 747 | bufRegion = bufRegion->StripCasts(); |
979 | | |
980 | | // Cast the memory region to a string region. |
981 | 747 | const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion); |
982 | 747 | if (!strRegion) |
983 | 2 | return nullptr; |
984 | | |
985 | | // Return the actual string in the string region. |
986 | 745 | return strRegion->getStringLiteral(); |
987 | 747 | } |
988 | | |
989 | | bool CStringChecker::isFirstBufInBound(CheckerContext &C, ProgramStateRef State, |
990 | | SVal BufVal, QualType BufTy, |
991 | 47 | SVal LengthVal, QualType LengthTy) { |
992 | | // If we do not know that the buffer is long enough we return 'true'. |
993 | | // Otherwise the parent region of this field region would also get |
994 | | // invalidated, which would lead to warnings based on an unknown state. |
995 | | |
996 | 47 | if (LengthVal.isUnknown()) |
997 | 1 | return false; |
998 | | |
999 | | // Originally copied from CheckBufferAccess and CheckLocation. |
1000 | 46 | SValBuilder &SB = C.getSValBuilder(); |
1001 | 46 | ASTContext &Ctx = C.getASTContext(); |
1002 | | |
1003 | 46 | QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); |
1004 | | |
1005 | 46 | std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); |
1006 | 46 | if (!Length) |
1007 | 0 | return true; // cf top comment. |
1008 | | |
1009 | | // Compute the offset of the last element to be accessed: size-1. |
1010 | 46 | NonLoc One = SB.makeIntVal(1, LengthTy).castAs<NonLoc>(); |
1011 | 46 | SVal Offset = SB.evalBinOpNN(State, BO_Sub, *Length, One, LengthTy); |
1012 | 46 | if (Offset.isUnknown()) |
1013 | 0 | return true; // cf top comment |
1014 | 46 | NonLoc LastOffset = Offset.castAs<NonLoc>(); |
1015 | | |
1016 | | // Check that the first buffer is sufficiently long. |
1017 | 46 | SVal BufStart = SB.evalCast(BufVal, PtrTy, BufTy); |
1018 | 46 | std::optional<Loc> BufLoc = BufStart.getAs<Loc>(); |
1019 | 46 | if (!BufLoc) |
1020 | 1 | return true; // cf top comment. |
1021 | | |
1022 | 45 | SVal BufEnd = SB.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); |
1023 | | |
1024 | | // Check for out of bound array element access. |
1025 | 45 | const MemRegion *R = BufEnd.getAsRegion(); |
1026 | 45 | if (!R) |
1027 | 0 | return true; // cf top comment. |
1028 | | |
1029 | 45 | const ElementRegion *ER = dyn_cast<ElementRegion>(R); |
1030 | 45 | if (!ER) |
1031 | 0 | return true; // cf top comment. |
1032 | | |
1033 | | // FIXME: Does this crash when a non-standard definition |
1034 | | // of a library function is encountered? |
1035 | 45 | assert(ER->getValueType() == C.getASTContext().CharTy && |
1036 | 45 | "isFirstBufInBound should only be called with char* ElementRegions"); |
1037 | | |
1038 | | // Get the size of the array. |
1039 | 45 | const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); |
1040 | 45 | DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, superReg, SB); |
1041 | | |
1042 | | // Get the index of the accessed element. |
1043 | 45 | DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); |
1044 | | |
1045 | 45 | ProgramStateRef StInBound = State->assumeInBound(Idx, SizeDV, true); |
1046 | | |
1047 | 45 | return static_cast<bool>(StInBound); |
1048 | 45 | } |
1049 | | |
1050 | | ProgramStateRef CStringChecker::invalidateDestinationBufferBySize( |
1051 | | CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV, |
1052 | 527 | SVal SizeV, QualType SizeTy) { |
1053 | 527 | auto InvalidationTraitOperations = |
1054 | 527 | [&C, S, BufTy = BufE->getType(), BufV, SizeV, |
1055 | 527 | SizeTy](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) { |
1056 | | // If destination buffer is a field region and access is in bound, do |
1057 | | // not invalidate its super region. |
1058 | 526 | if (MemRegion::FieldRegionKind == R->getKind() && |
1059 | 526 | isFirstBufInBound(C, S, BufV, BufTy, SizeV, SizeTy)47 ) { |
1060 | 38 | ITraits.setTrait( |
1061 | 38 | R, |
1062 | 38 | RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); |
1063 | 38 | } |
1064 | 526 | return false; |
1065 | 526 | }; |
1066 | | |
1067 | 527 | return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); |
1068 | 527 | } |
1069 | | |
1070 | | ProgramStateRef |
1071 | | CStringChecker::invalidateDestinationBufferAlwaysEscapeSuperRegion( |
1072 | 11 | CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) { |
1073 | 11 | auto InvalidationTraitOperations = [](RegionAndSymbolInvalidationTraits &, |
1074 | 11 | const MemRegion *R) { |
1075 | 11 | return isa<FieldRegion>(R); |
1076 | 11 | }; |
1077 | | |
1078 | 11 | return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); |
1079 | 11 | } |
1080 | | |
1081 | | ProgramStateRef CStringChecker::invalidateDestinationBufferNeverOverflows( |
1082 | 21 | CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) { |
1083 | 21 | auto InvalidationTraitOperations = |
1084 | 21 | [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) { |
1085 | 16 | if (MemRegion::FieldRegionKind == R->getKind()) |
1086 | 1 | ITraits.setTrait( |
1087 | 1 | R, |
1088 | 1 | RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); |
1089 | 16 | return false; |
1090 | 16 | }; |
1091 | | |
1092 | 21 | return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); |
1093 | 21 | } |
1094 | | |
1095 | | ProgramStateRef CStringChecker::invalidateSourceBuffer(CheckerContext &C, |
1096 | | ProgramStateRef S, |
1097 | | const Expr *BufE, |
1098 | 421 | SVal BufV) { |
1099 | 421 | auto InvalidationTraitOperations = |
1100 | 421 | [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) { |
1101 | 421 | ITraits.setTrait( |
1102 | 421 | R->getBaseRegion(), |
1103 | 421 | RegionAndSymbolInvalidationTraits::TK_PreserveContents); |
1104 | 421 | ITraits.setTrait(R, |
1105 | 421 | RegionAndSymbolInvalidationTraits::TK_SuppressEscape); |
1106 | 421 | return true; |
1107 | 421 | }; |
1108 | | |
1109 | 421 | return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); |
1110 | 421 | } |
1111 | | |
1112 | | ProgramStateRef CStringChecker::invalidateBufferAux( |
1113 | | CheckerContext &C, ProgramStateRef State, const Expr *E, SVal V, |
1114 | | llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &, |
1115 | | const MemRegion *)> |
1116 | 980 | InvalidationTraitOperations) { |
1117 | 980 | std::optional<Loc> L = V.getAs<Loc>(); |
1118 | 980 | if (!L) |
1119 | 0 | return State; |
1120 | | |
1121 | | // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes |
1122 | | // some assumptions about the value that CFRefCount can't. Even so, it should |
1123 | | // probably be refactored. |
1124 | 980 | if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { |
1125 | 974 | const MemRegion *R = MR->getRegion()->StripCasts(); |
1126 | | |
1127 | | // Are we dealing with an ElementRegion? If so, we should be invalidating |
1128 | | // the super-region. |
1129 | 974 | if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { |
1130 | 122 | R = ER->getSuperRegion(); |
1131 | | // FIXME: What about layers of ElementRegions? |
1132 | 122 | } |
1133 | | |
1134 | | // Invalidate this region. |
1135 | 974 | const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); |
1136 | 974 | RegionAndSymbolInvalidationTraits ITraits; |
1137 | 974 | bool CausesPointerEscape = InvalidationTraitOperations(ITraits, R); |
1138 | | |
1139 | 974 | return State->invalidateRegions(R, E, C.blockCount(), LCtx, |
1140 | 974 | CausesPointerEscape, nullptr, nullptr, |
1141 | 974 | &ITraits); |
1142 | 974 | } |
1143 | | |
1144 | | // If we have a non-region value by chance, just remove the binding. |
1145 | | // FIXME: is this necessary or correct? This handles the non-Region |
1146 | | // cases. Is it ever valid to store to these? |
1147 | 6 | return State->killBinding(*L); |
1148 | 980 | } |
1149 | | |
1150 | | bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, |
1151 | 40 | const MemRegion *MR) { |
1152 | 40 | switch (MR->getKind()) { |
1153 | 40 | case MemRegion::FunctionCodeRegionKind: { |
1154 | 40 | if (const auto *FD = cast<FunctionCodeRegion>(MR)->getDecl()) |
1155 | 40 | os << "the address of the function '" << *FD << '\''; |
1156 | 0 | else |
1157 | 0 | os << "the address of a function"; |
1158 | 40 | return true; |
1159 | 0 | } |
1160 | 0 | case MemRegion::BlockCodeRegionKind: |
1161 | 0 | os << "block text"; |
1162 | 0 | return true; |
1163 | 0 | case MemRegion::BlockDataRegionKind: |
1164 | 0 | os << "a block"; |
1165 | 0 | return true; |
1166 | 0 | case MemRegion::CXXThisRegionKind: |
1167 | 0 | case MemRegion::CXXTempObjectRegionKind: |
1168 | 0 | os << "a C++ temp object of type " |
1169 | 0 | << cast<TypedValueRegion>(MR)->getValueType(); |
1170 | 0 | return true; |
1171 | 0 | case MemRegion::NonParamVarRegionKind: |
1172 | 0 | os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType(); |
1173 | 0 | return true; |
1174 | 0 | case MemRegion::ParamVarRegionKind: |
1175 | 0 | os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType(); |
1176 | 0 | return true; |
1177 | 0 | case MemRegion::FieldRegionKind: |
1178 | 0 | os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType(); |
1179 | 0 | return true; |
1180 | 0 | case MemRegion::ObjCIvarRegionKind: |
1181 | 0 | os << "an instance variable of type " |
1182 | 0 | << cast<TypedValueRegion>(MR)->getValueType(); |
1183 | 0 | return true; |
1184 | 0 | default: |
1185 | 0 | return false; |
1186 | 40 | } |
1187 | 40 | } |
1188 | | |
1189 | | bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, |
1190 | | const Expr *Size, CheckerContext &C, |
1191 | 197 | ProgramStateRef &State) { |
1192 | 197 | SVal MemVal = C.getSVal(DstBuffer); |
1193 | 197 | SVal SizeVal = C.getSVal(Size); |
1194 | 197 | const MemRegion *MR = MemVal.getAsRegion(); |
1195 | 197 | if (!MR) |
1196 | 0 | return false; |
1197 | | |
1198 | | // We're about to model memset by producing a "default binding" in the Store. |
1199 | | // Our current implementation - RegionStore - doesn't support default bindings |
1200 | | // that don't cover the whole base region. So we should first get the offset |
1201 | | // and the base region to figure out whether the offset of buffer is 0. |
1202 | 197 | RegionOffset Offset = MR->getAsOffset(); |
1203 | 197 | const MemRegion *BR = Offset.getRegion(); |
1204 | | |
1205 | 197 | std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); |
1206 | 197 | if (!SizeNL) |
1207 | 0 | return false; |
1208 | | |
1209 | 197 | SValBuilder &svalBuilder = C.getSValBuilder(); |
1210 | 197 | ASTContext &Ctx = C.getASTContext(); |
1211 | | |
1212 | | // void *memset(void *dest, int ch, size_t count); |
1213 | | // For now we can only handle the case of offset is 0 and concrete char value. |
1214 | 197 | if (Offset.isValid() && !Offset.hasSymbolicOffset() && |
1215 | 197 | Offset.getOffset() == 0196 ) { |
1216 | | // Get the base region's size. |
1217 | 169 | DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, BR, svalBuilder); |
1218 | | |
1219 | 169 | ProgramStateRef StateWholeReg, StateNotWholeReg; |
1220 | 169 | std::tie(StateWholeReg, StateNotWholeReg) = |
1221 | 169 | State->assume(svalBuilder.evalEQ(State, SizeDV, *SizeNL)); |
1222 | | |
1223 | | // With the semantic of 'memset()', we should convert the CharVal to |
1224 | | // unsigned char. |
1225 | 169 | CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy); |
1226 | | |
1227 | 169 | ProgramStateRef StateNullChar, StateNonNullChar; |
1228 | 169 | std::tie(StateNullChar, StateNonNullChar) = |
1229 | 169 | assumeZero(C, State, CharVal, Ctx.UnsignedCharTy); |
1230 | | |
1231 | 169 | if (StateWholeReg && !StateNotWholeReg113 && StateNullChar113 && |
1232 | 169 | !StateNonNullChar91 ) { |
1233 | | // If the 'memset()' acts on the whole region of destination buffer and |
1234 | | // the value of the second argument of 'memset()' is zero, bind the second |
1235 | | // argument's value to the destination buffer with 'default binding'. |
1236 | | // FIXME: Since there is no perfect way to bind the non-zero character, we |
1237 | | // can only deal with zero value here. In the future, we need to deal with |
1238 | | // the binding of non-zero value in the case of whole region. |
1239 | 91 | State = State->bindDefaultZero(svalBuilder.makeLoc(BR), |
1240 | 91 | C.getLocationContext()); |
1241 | 91 | } else { |
1242 | | // If the destination buffer's extent is not equal to the value of |
1243 | | // third argument, just invalidate buffer. |
1244 | 78 | State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal, |
1245 | 78 | SizeVal, Size->getType()); |
1246 | 78 | } |
1247 | | |
1248 | 169 | if (StateNullChar && !StateNonNullChar116 ) { |
1249 | | // If the value of the second argument of 'memset()' is zero, set the |
1250 | | // string length of destination buffer to 0 directly. |
1251 | 116 | State = setCStringLength(State, MR, |
1252 | 116 | svalBuilder.makeZeroVal(Ctx.getSizeType())); |
1253 | 116 | } else if (53 !StateNullChar53 && StateNonNullChar53 ) { |
1254 | 53 | SVal NewStrLen = svalBuilder.getMetadataSymbolVal( |
1255 | 53 | CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(), |
1256 | 53 | C.getLocationContext(), C.blockCount()); |
1257 | | |
1258 | | // If the value of second argument is not zero, then the string length |
1259 | | // is at least the size argument. |
1260 | 53 | SVal NewStrLenGESize = svalBuilder.evalBinOp( |
1261 | 53 | State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType()); |
1262 | | |
1263 | 53 | State = setCStringLength( |
1264 | 53 | State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true), |
1265 | 53 | MR, NewStrLen); |
1266 | 53 | } |
1267 | 169 | } else { |
1268 | | // If the offset is not zero and char value is not concrete, we can do |
1269 | | // nothing but invalidate the buffer. |
1270 | 28 | State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal, |
1271 | 28 | SizeVal, Size->getType()); |
1272 | 28 | } |
1273 | 197 | return true; |
1274 | 197 | } |
1275 | | |
1276 | | //===----------------------------------------------------------------------===// |
1277 | | // evaluation of individual function calls. |
1278 | | //===----------------------------------------------------------------------===// |
1279 | | |
1280 | | void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, |
1281 | | ProgramStateRef state, SizeArgExpr Size, |
1282 | | DestinationArgExpr Dest, |
1283 | | SourceArgExpr Source, bool Restricted, |
1284 | 359 | bool IsMempcpy, CharKind CK) const { |
1285 | 359 | CurrentFunctionDescription = "memory copy function"; |
1286 | | |
1287 | | // See if the size argument is zero. |
1288 | 359 | const LocationContext *LCtx = C.getLocationContext(); |
1289 | 359 | SVal sizeVal = state->getSVal(Size.Expression, LCtx); |
1290 | 359 | QualType sizeTy = Size.Expression->getType(); |
1291 | | |
1292 | 359 | ProgramStateRef stateZeroSize, stateNonZeroSize; |
1293 | 359 | std::tie(stateZeroSize, stateNonZeroSize) = |
1294 | 359 | assumeZero(C, state, sizeVal, sizeTy); |
1295 | | |
1296 | | // Get the value of the Dest. |
1297 | 359 | SVal destVal = state->getSVal(Dest.Expression, LCtx); |
1298 | | |
1299 | | // If the size is zero, there won't be any actual memory access, so |
1300 | | // just bind the return value to the destination buffer and return. |
1301 | 359 | if (stateZeroSize && !stateNonZeroSize64 ) { |
1302 | 24 | stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal); |
1303 | 24 | C.addTransition(stateZeroSize); |
1304 | 24 | return; |
1305 | 24 | } |
1306 | | |
1307 | | // If the size can be nonzero, we have to check the other arguments. |
1308 | 335 | if (stateNonZeroSize) { |
1309 | 335 | state = stateNonZeroSize; |
1310 | | |
1311 | | // Ensure the destination is not null. If it is NULL there will be a |
1312 | | // NULL pointer dereference. |
1313 | 335 | state = checkNonNull(C, state, Dest, destVal); |
1314 | 335 | if (!state) |
1315 | 18 | return; |
1316 | | |
1317 | | // Get the value of the Src. |
1318 | 317 | SVal srcVal = state->getSVal(Source.Expression, LCtx); |
1319 | | |
1320 | | // Ensure the source is not null. If it is NULL there will be a |
1321 | | // NULL pointer dereference. |
1322 | 317 | state = checkNonNull(C, state, Source, srcVal); |
1323 | 317 | if (!state) |
1324 | 24 | return; |
1325 | | |
1326 | | // Ensure the accesses are valid and that the buffers do not overlap. |
1327 | 293 | state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK); |
1328 | 293 | state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK); |
1329 | | |
1330 | 293 | if (Restricted) |
1331 | 256 | state = CheckOverlap(C, state, Size, Dest, Source, CK); |
1332 | | |
1333 | 293 | if (!state) |
1334 | 119 | return; |
1335 | | |
1336 | | // If this is mempcpy, get the byte after the last byte copied and |
1337 | | // bind the expr. |
1338 | 174 | if (IsMempcpy) { |
1339 | | // Get the byte after the last byte copied. |
1340 | 59 | SValBuilder &SvalBuilder = C.getSValBuilder(); |
1341 | 59 | ASTContext &Ctx = SvalBuilder.getContext(); |
1342 | 59 | QualType CharPtrTy = getCharPtrType(Ctx, CK); |
1343 | 59 | SVal DestRegCharVal = |
1344 | 59 | SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType()); |
1345 | 59 | SVal lastElement = C.getSValBuilder().evalBinOp( |
1346 | 59 | state, BO_Add, DestRegCharVal, sizeVal, Dest.Expression->getType()); |
1347 | | // If we don't know how much we copied, we can at least |
1348 | | // conjure a return value for later. |
1349 | 59 | if (lastElement.isUnknown()) |
1350 | 11 | lastElement = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, |
1351 | 11 | C.blockCount()); |
1352 | | |
1353 | | // The byte after the last byte copied is the return value. |
1354 | 59 | state = state->BindExpr(CE, LCtx, lastElement); |
1355 | 115 | } else { |
1356 | | // All other copies return the destination buffer. |
1357 | | // (Well, bcopy() has a void return type, but this won't hurt.) |
1358 | 115 | state = state->BindExpr(CE, LCtx, destVal); |
1359 | 115 | } |
1360 | | |
1361 | | // Invalidate the destination (regular invalidation without pointer-escaping |
1362 | | // the address of the top-level region). |
1363 | | // FIXME: Even if we can't perfectly model the copy, we should see if we |
1364 | | // can use LazyCompoundVals to copy the source values into the destination. |
1365 | | // This would probably remove any existing bindings past the end of the |
1366 | | // copied region, but that's still an improvement over blank invalidation. |
1367 | 174 | state = invalidateDestinationBufferBySize( |
1368 | 174 | C, state, Dest.Expression, C.getSVal(Dest.Expression), sizeVal, |
1369 | 174 | Size.Expression->getType()); |
1370 | | |
1371 | | // Invalidate the source (const-invalidation without const-pointer-escaping |
1372 | | // the address of the top-level region). |
1373 | 174 | state = invalidateSourceBuffer(C, state, Source.Expression, |
1374 | 174 | C.getSVal(Source.Expression)); |
1375 | | |
1376 | 174 | C.addTransition(state); |
1377 | 174 | } |
1378 | 335 | } |
1379 | | |
1380 | | void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE, |
1381 | 189 | CharKind CK) const { |
1382 | | // void *memcpy(void *restrict dst, const void *restrict src, size_t n); |
1383 | | // The return value is the address of the destination buffer. |
1384 | 189 | DestinationArgExpr Dest = {{CE->getArg(0), 0}}; |
1385 | 189 | SourceArgExpr Src = {{CE->getArg(1), 1}}; |
1386 | 189 | SizeArgExpr Size = {{CE->getArg(2), 2}}; |
1387 | | |
1388 | 189 | ProgramStateRef State = C.getState(); |
1389 | | |
1390 | 189 | constexpr bool IsRestricted = true; |
1391 | 189 | constexpr bool IsMempcpy = false; |
1392 | 189 | evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK); |
1393 | 189 | } |
1394 | | |
1395 | | void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE, |
1396 | 133 | CharKind CK) const { |
1397 | | // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); |
1398 | | // The return value is a pointer to the byte following the last written byte. |
1399 | 133 | DestinationArgExpr Dest = {{CE->getArg(0), 0}}; |
1400 | 133 | SourceArgExpr Src = {{CE->getArg(1), 1}}; |
1401 | 133 | SizeArgExpr Size = {{CE->getArg(2), 2}}; |
1402 | | |
1403 | 133 | constexpr bool IsRestricted = true; |
1404 | 133 | constexpr bool IsMempcpy = true; |
1405 | 133 | evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, |
1406 | 133 | CK); |
1407 | 133 | } |
1408 | | |
1409 | | void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE, |
1410 | 25 | CharKind CK) const { |
1411 | | // void *memmove(void *dst, const void *src, size_t n); |
1412 | | // The return value is the address of the destination buffer. |
1413 | 25 | DestinationArgExpr Dest = {{CE->getArg(0), 0}}; |
1414 | 25 | SourceArgExpr Src = {{CE->getArg(1), 1}}; |
1415 | 25 | SizeArgExpr Size = {{CE->getArg(2), 2}}; |
1416 | | |
1417 | 25 | constexpr bool IsRestricted = false; |
1418 | 25 | constexpr bool IsMempcpy = false; |
1419 | 25 | evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, |
1420 | 25 | CK); |
1421 | 25 | } |
1422 | | |
1423 | 12 | void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { |
1424 | | // void bcopy(const void *src, void *dst, size_t n); |
1425 | 12 | SourceArgExpr Src{{CE->getArg(0), 0}}; |
1426 | 12 | DestinationArgExpr Dest = {{CE->getArg(1), 1}}; |
1427 | 12 | SizeArgExpr Size = {{CE->getArg(2), 2}}; |
1428 | | |
1429 | 12 | constexpr bool IsRestricted = false; |
1430 | 12 | constexpr bool IsMempcpy = false; |
1431 | 12 | evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, |
1432 | 12 | CharKind::Regular); |
1433 | 12 | } |
1434 | | |
1435 | | void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE, |
1436 | 80 | CharKind CK) const { |
1437 | | // int memcmp(const void *s1, const void *s2, size_t n); |
1438 | 80 | CurrentFunctionDescription = "memory comparison function"; |
1439 | | |
1440 | 80 | AnyArgExpr Left = {CE->getArg(0), 0}; |
1441 | 80 | AnyArgExpr Right = {CE->getArg(1), 1}; |
1442 | 80 | SizeArgExpr Size = {{CE->getArg(2), 2}}; |
1443 | | |
1444 | 80 | ProgramStateRef State = C.getState(); |
1445 | 80 | SValBuilder &Builder = C.getSValBuilder(); |
1446 | 80 | const LocationContext *LCtx = C.getLocationContext(); |
1447 | | |
1448 | | // See if the size argument is zero. |
1449 | 80 | SVal sizeVal = State->getSVal(Size.Expression, LCtx); |
1450 | 80 | QualType sizeTy = Size.Expression->getType(); |
1451 | | |
1452 | 80 | ProgramStateRef stateZeroSize, stateNonZeroSize; |
1453 | 80 | std::tie(stateZeroSize, stateNonZeroSize) = |
1454 | 80 | assumeZero(C, State, sizeVal, sizeTy); |
1455 | | |
1456 | | // If the size can be zero, the result will be 0 in that case, and we don't |
1457 | | // have to check either of the buffers. |
1458 | 80 | if (stateZeroSize) { |
1459 | 42 | State = stateZeroSize; |
1460 | 42 | State = State->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType())); |
1461 | 42 | C.addTransition(State); |
1462 | 42 | } |
1463 | | |
1464 | | // If the size can be nonzero, we have to check the other arguments. |
1465 | 80 | if (stateNonZeroSize) { |
1466 | 56 | State = stateNonZeroSize; |
1467 | | // If we know the two buffers are the same, we know the result is 0. |
1468 | | // First, get the two buffers' addresses. Another checker will have already |
1469 | | // made sure they're not undefined. |
1470 | 56 | DefinedOrUnknownSVal LV = |
1471 | 56 | State->getSVal(Left.Expression, LCtx).castAs<DefinedOrUnknownSVal>(); |
1472 | 56 | DefinedOrUnknownSVal RV = |
1473 | 56 | State->getSVal(Right.Expression, LCtx).castAs<DefinedOrUnknownSVal>(); |
1474 | | |
1475 | | // See if they are the same. |
1476 | 56 | ProgramStateRef SameBuffer, NotSameBuffer; |
1477 | 56 | std::tie(SameBuffer, NotSameBuffer) = |
1478 | 56 | State->assume(Builder.evalEQ(State, LV, RV)); |
1479 | | |
1480 | | // If the two arguments are the same buffer, we know the result is 0, |
1481 | | // and we only need to check one size. |
1482 | 56 | if (SameBuffer && !NotSameBuffer32 ) { |
1483 | 8 | State = SameBuffer; |
1484 | 8 | State = CheckBufferAccess(C, State, Left, Size, AccessKind::read); |
1485 | 8 | if (State) { |
1486 | 8 | State = |
1487 | 8 | SameBuffer->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType())); |
1488 | 8 | C.addTransition(State); |
1489 | 8 | } |
1490 | 8 | return; |
1491 | 8 | } |
1492 | | |
1493 | | // If the two arguments might be different buffers, we have to check |
1494 | | // the size of both of them. |
1495 | 48 | assert(NotSameBuffer); |
1496 | 48 | State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK); |
1497 | 48 | State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK); |
1498 | 48 | if (State) { |
1499 | | // The return value is the comparison result, which we don't know. |
1500 | 30 | SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); |
1501 | 30 | State = State->BindExpr(CE, LCtx, CmpV); |
1502 | 30 | C.addTransition(State); |
1503 | 30 | } |
1504 | 48 | } |
1505 | 80 | } |
1506 | | |
1507 | | void CStringChecker::evalstrLength(CheckerContext &C, |
1508 | 644 | const CallExpr *CE) const { |
1509 | | // size_t strlen(const char *s); |
1510 | 644 | evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); |
1511 | 644 | } |
1512 | | |
1513 | | void CStringChecker::evalstrnLength(CheckerContext &C, |
1514 | 144 | const CallExpr *CE) const { |
1515 | | // size_t strnlen(const char *s, size_t maxlen); |
1516 | 144 | evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); |
1517 | 144 | } |
1518 | | |
1519 | | void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, |
1520 | 788 | bool IsStrnlen) const { |
1521 | 788 | CurrentFunctionDescription = "string length function"; |
1522 | 788 | ProgramStateRef state = C.getState(); |
1523 | 788 | const LocationContext *LCtx = C.getLocationContext(); |
1524 | | |
1525 | 788 | if (IsStrnlen) { |
1526 | 144 | const Expr *maxlenExpr = CE->getArg(1); |
1527 | 144 | SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); |
1528 | | |
1529 | 144 | ProgramStateRef stateZeroSize, stateNonZeroSize; |
1530 | 144 | std::tie(stateZeroSize, stateNonZeroSize) = |
1531 | 144 | assumeZero(C, state, maxlenVal, maxlenExpr->getType()); |
1532 | | |
1533 | | // If the size can be zero, the result will be 0 in that case, and we don't |
1534 | | // have to check the string itself. |
1535 | 144 | if (stateZeroSize) { |
1536 | 28 | SVal zero = C.getSValBuilder().makeZeroVal(CE->getType()); |
1537 | 28 | stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero); |
1538 | 28 | C.addTransition(stateZeroSize); |
1539 | 28 | } |
1540 | | |
1541 | | // If the size is GUARANTEED to be zero, we're done! |
1542 | 144 | if (!stateNonZeroSize) |
1543 | 14 | return; |
1544 | | |
1545 | | // Otherwise, record the assumption that the size is nonzero. |
1546 | 130 | state = stateNonZeroSize; |
1547 | 130 | } |
1548 | | |
1549 | | // Check that the string argument is non-null. |
1550 | 774 | AnyArgExpr Arg = {CE->getArg(0), 0}; |
1551 | 774 | SVal ArgVal = state->getSVal(Arg.Expression, LCtx); |
1552 | 774 | state = checkNonNull(C, state, Arg, ArgVal); |
1553 | | |
1554 | 774 | if (!state) |
1555 | 14 | return; |
1556 | | |
1557 | 760 | SVal strLength = getCStringLength(C, state, Arg.Expression, ArgVal); |
1558 | | |
1559 | | // If the argument isn't a valid C string, there's no valid state to |
1560 | | // transition to. |
1561 | 760 | if (strLength.isUndef()) |
1562 | 29 | return; |
1563 | | |
1564 | 731 | DefinedOrUnknownSVal result = UnknownVal(); |
1565 | | |
1566 | | // If the check is for strnlen() then bind the return value to no more than |
1567 | | // the maxlen value. |
1568 | 731 | if (IsStrnlen) { |
1569 | 109 | QualType cmpTy = C.getSValBuilder().getConditionType(); |
1570 | | |
1571 | | // It's a little unfortunate to be getting this again, |
1572 | | // but it's not that expensive... |
1573 | 109 | const Expr *maxlenExpr = CE->getArg(1); |
1574 | 109 | SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); |
1575 | | |
1576 | 109 | std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); |
1577 | 109 | std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); |
1578 | | |
1579 | 109 | if (strLengthNL && maxlenValNL102 ) { |
1580 | 95 | ProgramStateRef stateStringTooLong, stateStringNotTooLong; |
1581 | | |
1582 | | // Check if the strLength is greater than the maxlen. |
1583 | 95 | std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume( |
1584 | 95 | C.getSValBuilder() |
1585 | 95 | .evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy) |
1586 | 95 | .castAs<DefinedOrUnknownSVal>()); |
1587 | | |
1588 | 95 | if (stateStringTooLong && !stateStringNotTooLong74 ) { |
1589 | | // If the string is longer than maxlen, return maxlen. |
1590 | 21 | result = *maxlenValNL; |
1591 | 74 | } else if (stateStringNotTooLong && !stateStringTooLong) { |
1592 | | // If the string is shorter than maxlen, return its length. |
1593 | 21 | result = *strLengthNL; |
1594 | 21 | } |
1595 | 95 | } |
1596 | | |
1597 | 109 | if (result.isUnknown()) { |
1598 | | // If we don't have enough information for a comparison, there's |
1599 | | // no guarantee the full string length will actually be returned. |
1600 | | // All we know is the return value is the min of the string length |
1601 | | // and the limit. This is better than nothing. |
1602 | 67 | result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, |
1603 | 67 | C.blockCount()); |
1604 | 67 | NonLoc resultNL = result.castAs<NonLoc>(); |
1605 | | |
1606 | 67 | if (strLengthNL) { |
1607 | 60 | state = state->assume(C.getSValBuilder().evalBinOpNN( |
1608 | 60 | state, BO_LE, resultNL, *strLengthNL, cmpTy) |
1609 | 60 | .castAs<DefinedOrUnknownSVal>(), true); |
1610 | 60 | } |
1611 | | |
1612 | 67 | if (maxlenValNL) { |
1613 | 60 | state = state->assume(C.getSValBuilder().evalBinOpNN( |
1614 | 60 | state, BO_LE, resultNL, *maxlenValNL, cmpTy) |
1615 | 60 | .castAs<DefinedOrUnknownSVal>(), true); |
1616 | 60 | } |
1617 | 67 | } |
1618 | | |
1619 | 622 | } else { |
1620 | | // This is a plain strlen(), not strnlen(). |
1621 | 622 | result = strLength.castAs<DefinedOrUnknownSVal>(); |
1622 | | |
1623 | | // If we don't know the length of the string, conjure a return |
1624 | | // value, so it can be used in constraints, at least. |
1625 | 622 | if (result.isUnknown()) { |
1626 | 2 | result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, |
1627 | 2 | C.blockCount()); |
1628 | 2 | } |
1629 | 622 | } |
1630 | | |
1631 | | // Bind the return value. |
1632 | 731 | assert(!result.isUnknown() && "Should have conjured a value by now"); |
1633 | 731 | state = state->BindExpr(CE, LCtx, result); |
1634 | 731 | C.addTransition(state); |
1635 | 731 | } |
1636 | | |
1637 | 67 | void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { |
1638 | | // char *strcpy(char *restrict dst, const char *restrict src); |
1639 | 67 | evalStrcpyCommon(C, CE, |
1640 | 67 | /* ReturnEnd = */ false, |
1641 | 67 | /* IsBounded = */ false, |
1642 | 67 | /* appendK = */ ConcatFnKind::none); |
1643 | 67 | } |
1644 | | |
1645 | 76 | void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { |
1646 | | // char *strncpy(char *restrict dst, const char *restrict src, size_t n); |
1647 | 76 | evalStrcpyCommon(C, CE, |
1648 | 76 | /* ReturnEnd = */ false, |
1649 | 76 | /* IsBounded = */ true, |
1650 | 76 | /* appendK = */ ConcatFnKind::none); |
1651 | 76 | } |
1652 | | |
1653 | 14 | void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { |
1654 | | // char *stpcpy(char *restrict dst, const char *restrict src); |
1655 | 14 | evalStrcpyCommon(C, CE, |
1656 | 14 | /* ReturnEnd = */ true, |
1657 | 14 | /* IsBounded = */ false, |
1658 | 14 | /* appendK = */ ConcatFnKind::none); |
1659 | 14 | } |
1660 | | |
1661 | 75 | void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const { |
1662 | | // size_t strlcpy(char *dest, const char *src, size_t size); |
1663 | 75 | evalStrcpyCommon(C, CE, |
1664 | 75 | /* ReturnEnd = */ true, |
1665 | 75 | /* IsBounded = */ true, |
1666 | 75 | /* appendK = */ ConcatFnKind::none, |
1667 | 75 | /* returnPtr = */ false); |
1668 | 75 | } |
1669 | | |
1670 | 57 | void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { |
1671 | | // char *strcat(char *restrict s1, const char *restrict s2); |
1672 | 57 | evalStrcpyCommon(C, CE, |
1673 | 57 | /* ReturnEnd = */ false, |
1674 | 57 | /* IsBounded = */ false, |
1675 | 57 | /* appendK = */ ConcatFnKind::strcat); |
1676 | 57 | } |
1677 | | |
1678 | 107 | void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { |
1679 | | // char *strncat(char *restrict s1, const char *restrict s2, size_t n); |
1680 | 107 | evalStrcpyCommon(C, CE, |
1681 | 107 | /* ReturnEnd = */ false, |
1682 | 107 | /* IsBounded = */ true, |
1683 | 107 | /* appendK = */ ConcatFnKind::strcat); |
1684 | 107 | } |
1685 | | |
1686 | 42 | void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const { |
1687 | | // size_t strlcat(char *dst, const char *src, size_t size); |
1688 | | // It will append at most size - strlen(dst) - 1 bytes, |
1689 | | // NULL-terminating the result. |
1690 | 42 | evalStrcpyCommon(C, CE, |
1691 | 42 | /* ReturnEnd = */ false, |
1692 | 42 | /* IsBounded = */ true, |
1693 | 42 | /* appendK = */ ConcatFnKind::strlcat, |
1694 | 42 | /* returnPtr = */ false); |
1695 | 42 | } |
1696 | | |
1697 | | void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, |
1698 | | bool ReturnEnd, bool IsBounded, |
1699 | | ConcatFnKind appendK, |
1700 | 438 | bool returnPtr) const { |
1701 | 438 | if (appendK == ConcatFnKind::none) |
1702 | 232 | CurrentFunctionDescription = "string copy function"; |
1703 | 206 | else |
1704 | 206 | CurrentFunctionDescription = "string concatenation function"; |
1705 | | |
1706 | 438 | ProgramStateRef state = C.getState(); |
1707 | 438 | const LocationContext *LCtx = C.getLocationContext(); |
1708 | | |
1709 | | // Check that the destination is non-null. |
1710 | 438 | DestinationArgExpr Dst = {{CE->getArg(0), 0}}; |
1711 | 438 | SVal DstVal = state->getSVal(Dst.Expression, LCtx); |
1712 | 438 | state = checkNonNull(C, state, Dst, DstVal); |
1713 | 438 | if (!state) |
1714 | 23 | return; |
1715 | | |
1716 | | // Check that the source is non-null. |
1717 | 415 | SourceArgExpr srcExpr = {{CE->getArg(1), 1}}; |
1718 | 415 | SVal srcVal = state->getSVal(srcExpr.Expression, LCtx); |
1719 | 415 | state = checkNonNull(C, state, srcExpr, srcVal); |
1720 | 415 | if (!state) |
1721 | 20 | return; |
1722 | | |
1723 | | // Get the string length of the source. |
1724 | 395 | SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal); |
1725 | 395 | std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); |
1726 | | |
1727 | | // Get the string length of the destination buffer. |
1728 | 395 | SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal); |
1729 | 395 | std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); |
1730 | | |
1731 | | // If the source isn't a valid C string, give up. |
1732 | 395 | if (strLength.isUndef()) |
1733 | 25 | return; |
1734 | | |
1735 | 370 | SValBuilder &svalBuilder = C.getSValBuilder(); |
1736 | 370 | QualType cmpTy = svalBuilder.getConditionType(); |
1737 | 370 | QualType sizeTy = svalBuilder.getContext().getSizeType(); |
1738 | | |
1739 | | // These two values allow checking two kinds of errors: |
1740 | | // - actual overflows caused by a source that doesn't fit in the destination |
1741 | | // - potential overflows caused by a bound that could exceed the destination |
1742 | 370 | SVal amountCopied = UnknownVal(); |
1743 | 370 | SVal maxLastElementIndex = UnknownVal(); |
1744 | 370 | const char *boundWarning = nullptr; |
1745 | | |
1746 | | // FIXME: Why do we choose the srcExpr if the access has no size? |
1747 | | // Note that the 3rd argument of the call would be the size parameter. |
1748 | 370 | SizeArgExpr SrcExprAsSizeDummy = { |
1749 | 370 | {srcExpr.Expression, srcExpr.ArgumentIndex}}; |
1750 | 370 | state = CheckOverlap( |
1751 | 370 | C, state, |
1752 | 370 | (IsBounded ? SizeArgExpr{{CE->getArg(2), 2}}268 : SrcExprAsSizeDummy102 ), Dst, |
1753 | 370 | srcExpr); |
1754 | | |
1755 | 370 | if (!state) |
1756 | 1 | return; |
1757 | | |
1758 | | // If the function is strncpy, strncat, etc... it is bounded. |
1759 | 369 | if (IsBounded) { |
1760 | | // Get the max number of characters to copy. |
1761 | 267 | SizeArgExpr lenExpr = {{CE->getArg(2), 2}}; |
1762 | 267 | SVal lenVal = state->getSVal(lenExpr.Expression, LCtx); |
1763 | | |
1764 | | // Protect against misdeclared strncpy(). |
1765 | 267 | lenVal = |
1766 | 267 | svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType()); |
1767 | | |
1768 | 267 | std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); |
1769 | | |
1770 | | // If we know both values, we might be able to figure out how much |
1771 | | // we're copying. |
1772 | 267 | if (strLengthNL && lenValNL257 ) { |
1773 | 244 | switch (appendK) { |
1774 | 130 | case ConcatFnKind::none: |
1775 | 209 | case ConcatFnKind::strcat: { |
1776 | 209 | ProgramStateRef stateSourceTooLong, stateSourceNotTooLong; |
1777 | | // Check if the max number to copy is less than the length of the src. |
1778 | | // If the bound is equal to the source length, strncpy won't null- |
1779 | | // terminate the result! |
1780 | 209 | std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume( |
1781 | 209 | svalBuilder |
1782 | 209 | .evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy) |
1783 | 209 | .castAs<DefinedOrUnknownSVal>()); |
1784 | | |
1785 | 209 | if (stateSourceTooLong && !stateSourceNotTooLong161 ) { |
1786 | | // Max number to copy is less than the length of the src, so the |
1787 | | // actual strLength copied is the max number arg. |
1788 | 83 | state = stateSourceTooLong; |
1789 | 83 | amountCopied = lenVal; |
1790 | | |
1791 | 126 | } else if (!stateSourceTooLong && stateSourceNotTooLong48 ) { |
1792 | | // The source buffer entirely fits in the bound. |
1793 | 48 | state = stateSourceNotTooLong; |
1794 | 48 | amountCopied = strLength; |
1795 | 48 | } |
1796 | 209 | break; |
1797 | 130 | } |
1798 | 35 | case ConcatFnKind::strlcat: |
1799 | 35 | if (!dstStrLengthNL) |
1800 | 4 | return; |
1801 | | |
1802 | | // amountCopied = min (size - dstLen - 1 , srcLen) |
1803 | 31 | SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, |
1804 | 31 | *dstStrLengthNL, sizeTy); |
1805 | 31 | if (!isa<NonLoc>(freeSpace)) |
1806 | 0 | return; |
1807 | 31 | freeSpace = |
1808 | 31 | svalBuilder.evalBinOp(state, BO_Sub, freeSpace, |
1809 | 31 | svalBuilder.makeIntVal(1, sizeTy), sizeTy); |
1810 | 31 | std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); |
1811 | | |
1812 | | // While unlikely, it is possible that the subtraction is |
1813 | | // too complex to compute, let's check whether it succeeded. |
1814 | 31 | if (!freeSpaceNL) |
1815 | 0 | return; |
1816 | 31 | SVal hasEnoughSpace = svalBuilder.evalBinOpNN( |
1817 | 31 | state, BO_LE, *strLengthNL, *freeSpaceNL, cmpTy); |
1818 | | |
1819 | 31 | ProgramStateRef TrueState, FalseState; |
1820 | 31 | std::tie(TrueState, FalseState) = |
1821 | 31 | state->assume(hasEnoughSpace.castAs<DefinedOrUnknownSVal>()); |
1822 | | |
1823 | | // srcStrLength <= size - dstStrLength -1 |
1824 | 31 | if (TrueState && !FalseState23 ) { |
1825 | 14 | amountCopied = strLength; |
1826 | 14 | } |
1827 | | |
1828 | | // srcStrLength > size - dstStrLength -1 |
1829 | 31 | if (!TrueState && FalseState8 ) { |
1830 | 8 | amountCopied = freeSpace; |
1831 | 8 | } |
1832 | | |
1833 | 31 | if (TrueState && FalseState23 ) |
1834 | 9 | amountCopied = UnknownVal(); |
1835 | 31 | break; |
1836 | 244 | } |
1837 | 244 | } |
1838 | | // We still want to know if the bound is known to be too large. |
1839 | 263 | if (lenValNL) { |
1840 | 250 | switch (appendK) { |
1841 | 87 | case ConcatFnKind::strcat: |
1842 | | // For strncat, the check is strlen(dst) + lenVal < sizeof(dst) |
1843 | | |
1844 | | // Get the string length of the destination. If the destination is |
1845 | | // memory that can't have a string length, we shouldn't be copying |
1846 | | // into it anyway. |
1847 | 87 | if (dstStrLength.isUndef()) |
1848 | 0 | return; |
1849 | | |
1850 | 87 | if (dstStrLengthNL) { |
1851 | 87 | maxLastElementIndex = svalBuilder.evalBinOpNN( |
1852 | 87 | state, BO_Add, *lenValNL, *dstStrLengthNL, sizeTy); |
1853 | | |
1854 | 87 | boundWarning = "Size argument is greater than the free space in the " |
1855 | 87 | "destination buffer"; |
1856 | 87 | } |
1857 | 87 | break; |
1858 | 130 | case ConcatFnKind::none: |
1859 | 163 | case ConcatFnKind::strlcat: |
1860 | | // For strncpy and strlcat, this is just checking |
1861 | | // that lenVal <= sizeof(dst). |
1862 | | // (Yes, strncpy and strncat differ in how they treat termination. |
1863 | | // strncat ALWAYS terminates, but strncpy doesn't.) |
1864 | | |
1865 | | // We need a special case for when the copy size is zero, in which |
1866 | | // case strncpy will do no work at all. Our bounds check uses n-1 |
1867 | | // as the last element accessed, so n == 0 is problematic. |
1868 | 163 | ProgramStateRef StateZeroSize, StateNonZeroSize; |
1869 | 163 | std::tie(StateZeroSize, StateNonZeroSize) = |
1870 | 163 | assumeZero(C, state, *lenValNL, sizeTy); |
1871 | | |
1872 | | // If the size is known to be zero, we're done. |
1873 | 163 | if (StateZeroSize && !StateNonZeroSize14 ) { |
1874 | 9 | if (returnPtr) { |
1875 | 5 | StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal); |
1876 | 5 | } else { |
1877 | 4 | if (appendK == ConcatFnKind::none) { |
1878 | | // strlcpy returns strlen(src) |
1879 | 2 | StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, strLength); |
1880 | 2 | } else { |
1881 | | // strlcat returns strlen(src) + strlen(dst) |
1882 | 2 | SVal retSize = svalBuilder.evalBinOp( |
1883 | 2 | state, BO_Add, strLength, dstStrLength, sizeTy); |
1884 | 2 | StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, retSize); |
1885 | 2 | } |
1886 | 4 | } |
1887 | 9 | C.addTransition(StateZeroSize); |
1888 | 9 | return; |
1889 | 9 | } |
1890 | | |
1891 | | // Otherwise, go ahead and figure out the last element we'll touch. |
1892 | | // We don't record the non-zero assumption here because we can't |
1893 | | // be sure. We won't warn on a possible zero. |
1894 | 154 | NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); |
1895 | 154 | maxLastElementIndex = |
1896 | 154 | svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy); |
1897 | 154 | boundWarning = "Size argument is greater than the length of the " |
1898 | 154 | "destination buffer"; |
1899 | 154 | break; |
1900 | 250 | } |
1901 | 250 | } |
1902 | 263 | } else { |
1903 | | // The function isn't bounded. The amount copied should match the length |
1904 | | // of the source buffer. |
1905 | 102 | amountCopied = strLength; |
1906 | 102 | } |
1907 | | |
1908 | 356 | assert(state); |
1909 | | |
1910 | | // This represents the number of characters copied into the destination |
1911 | | // buffer. (It may not actually be the strlen if the destination buffer |
1912 | | // is not terminated.) |
1913 | 356 | SVal finalStrLength = UnknownVal(); |
1914 | 356 | SVal strlRetVal = UnknownVal(); |
1915 | | |
1916 | 356 | if (appendK == ConcatFnKind::none && !returnPtr187 ) { |
1917 | | // strlcpy returns the sizeof(src) |
1918 | 71 | strlRetVal = strLength; |
1919 | 71 | } |
1920 | | |
1921 | | // If this is an appending function (strcat, strncat...) then set the |
1922 | | // string length to strlen(src) + strlen(dst) since the buffer will |
1923 | | // ultimately contain both. |
1924 | 356 | if (appendK != ConcatFnKind::none) { |
1925 | | // Get the string length of the destination. If the destination is memory |
1926 | | // that can't have a string length, we shouldn't be copying into it anyway. |
1927 | 169 | if (dstStrLength.isUndef()) |
1928 | 0 | return; |
1929 | | |
1930 | 169 | if (appendK == ConcatFnKind::strlcat && dstStrLengthNL35 && strLengthNL34 ) { |
1931 | 34 | strlRetVal = svalBuilder.evalBinOpNN(state, BO_Add, *strLengthNL, |
1932 | 34 | *dstStrLengthNL, sizeTy); |
1933 | 34 | } |
1934 | | |
1935 | 169 | std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); |
1936 | | |
1937 | | // If we know both string lengths, we might know the final string length. |
1938 | 169 | if (amountCopiedNL && dstStrLengthNL116 ) { |
1939 | | // Make sure the two lengths together don't overflow a size_t. |
1940 | 116 | state = checkAdditionOverflow(C, state, *amountCopiedNL, *dstStrLengthNL); |
1941 | 116 | if (!state) |
1942 | 0 | return; |
1943 | | |
1944 | 116 | finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *amountCopiedNL, |
1945 | 116 | *dstStrLengthNL, sizeTy); |
1946 | 116 | } |
1947 | | |
1948 | | // If we couldn't get a single value for the final string length, |
1949 | | // we can at least bound it by the individual lengths. |
1950 | 169 | if (finalStrLength.isUnknown()) { |
1951 | | // Try to get a "hypothetical" string length symbol, which we can later |
1952 | | // set as a real value if that turns out to be the case. |
1953 | 53 | finalStrLength = getCStringLength(C, state, CE, DstVal, true); |
1954 | 53 | assert(!finalStrLength.isUndef()); |
1955 | | |
1956 | 53 | if (std::optional<NonLoc> finalStrLengthNL = |
1957 | 53 | finalStrLength.getAs<NonLoc>()) { |
1958 | 52 | if (amountCopiedNL && appendK == ConcatFnKind::none0 ) { |
1959 | | // we overwrite dst string with the src |
1960 | | // finalStrLength >= srcStrLength |
1961 | 0 | SVal sourceInResult = svalBuilder.evalBinOpNN( |
1962 | 0 | state, BO_GE, *finalStrLengthNL, *amountCopiedNL, cmpTy); |
1963 | 0 | state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(), |
1964 | 0 | true); |
1965 | 0 | if (!state) |
1966 | 0 | return; |
1967 | 0 | } |
1968 | | |
1969 | 52 | if (dstStrLengthNL && appendK != ConcatFnKind::none) { |
1970 | | // we extend the dst string with the src |
1971 | | // finalStrLength >= dstStrLength |
1972 | 52 | SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE, |
1973 | 52 | *finalStrLengthNL, |
1974 | 52 | *dstStrLengthNL, |
1975 | 52 | cmpTy); |
1976 | 52 | state = |
1977 | 52 | state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true); |
1978 | 52 | if (!state) |
1979 | 0 | return; |
1980 | 52 | } |
1981 | 52 | } |
1982 | 53 | } |
1983 | | |
1984 | 187 | } else { |
1985 | | // Otherwise, this is a copy-over function (strcpy, strncpy, ...), and |
1986 | | // the final string length will match the input string length. |
1987 | 187 | finalStrLength = amountCopied; |
1988 | 187 | } |
1989 | | |
1990 | 356 | SVal Result; |
1991 | | |
1992 | 356 | if (returnPtr) { |
1993 | | // The final result of the function will either be a pointer past the last |
1994 | | // copied element, or a pointer to the start of the destination buffer. |
1995 | 250 | Result = (ReturnEnd ? UnknownVal()14 : DstVal236 ); |
1996 | 250 | } else { |
1997 | 106 | if (appendK == ConcatFnKind::strlcat || appendK == ConcatFnKind::none71 ) |
1998 | | //strlcpy, strlcat |
1999 | 106 | Result = strlRetVal; |
2000 | 0 | else |
2001 | 0 | Result = finalStrLength; |
2002 | 106 | } |
2003 | | |
2004 | 356 | assert(state); |
2005 | | |
2006 | | // If the destination is a MemRegion, try to check for a buffer overflow and |
2007 | | // record the new string length. |
2008 | 356 | if (std::optional<loc::MemRegionVal> dstRegVal = |
2009 | 356 | DstVal.getAs<loc::MemRegionVal>()) { |
2010 | 356 | QualType ptrTy = Dst.Expression->getType(); |
2011 | | |
2012 | | // If we have an exact value on a bounded copy, use that to check for |
2013 | | // overflows, rather than our estimate about how much is actually copied. |
2014 | 356 | if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { |
2015 | 239 | SVal maxLastElement = |
2016 | 239 | svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); |
2017 | | |
2018 | | // Check if the first byte of the destination is writable. |
2019 | 239 | state = CheckLocation(C, state, Dst, DstVal, AccessKind::write); |
2020 | 239 | if (!state) |
2021 | 8 | return; |
2022 | | // Check if the last byte of the destination is writable. |
2023 | 231 | state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write); |
2024 | 231 | if (!state) |
2025 | 68 | return; |
2026 | 231 | } |
2027 | | |
2028 | | // Then, if the final length is known... |
2029 | 280 | if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { |
2030 | 239 | SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, |
2031 | 239 | *knownStrLength, ptrTy); |
2032 | | |
2033 | | // ...and we haven't checked the bound, we'll check the actual copy. |
2034 | 239 | if (!boundWarning) { |
2035 | | // Check if the first byte of the destination is writable. |
2036 | 111 | state = CheckLocation(C, state, Dst, DstVal, AccessKind::write); |
2037 | 111 | if (!state) |
2038 | 8 | return; |
2039 | | // Check if the last byte of the destination is writable. |
2040 | 103 | state = CheckLocation(C, state, Dst, lastElement, AccessKind::write); |
2041 | 103 | if (!state) |
2042 | 25 | return; |
2043 | 103 | } |
2044 | | |
2045 | | // If this is a stpcpy-style copy, the last element is the return value. |
2046 | 206 | if (returnPtr && ReturnEnd154 ) |
2047 | 10 | Result = lastElement; |
2048 | 206 | } |
2049 | | |
2050 | | // Invalidate the destination (regular invalidation without pointer-escaping |
2051 | | // the address of the top-level region). This must happen before we set the |
2052 | | // C string length because invalidation will clear the length. |
2053 | | // FIXME: Even if we can't perfectly model the copy, we should see if we |
2054 | | // can use LazyCompoundVals to copy the source values into the destination. |
2055 | | // This would probably remove any existing bindings past the end of the |
2056 | | // string, but that's still an improvement over blank invalidation. |
2057 | 247 | state = invalidateDestinationBufferBySize(C, state, Dst.Expression, |
2058 | 247 | *dstRegVal, amountCopied, |
2059 | 247 | C.getASTContext().getSizeType()); |
2060 | | |
2061 | | // Invalidate the source (const-invalidation without const-pointer-escaping |
2062 | | // the address of the top-level region). |
2063 | 247 | state = invalidateSourceBuffer(C, state, srcExpr.Expression, srcVal); |
2064 | | |
2065 | | // Set the C string length of the destination, if we know it. |
2066 | 247 | if (IsBounded && (appendK == ConcatFnKind::none)178 ) { |
2067 | | // strncpy is annoying in that it doesn't guarantee to null-terminate |
2068 | | // the result string. If the original string didn't fit entirely inside |
2069 | | // the bound (including the null-terminator), we don't know how long the |
2070 | | // result is. |
2071 | 89 | if (amountCopied != strLength) |
2072 | 55 | finalStrLength = UnknownVal(); |
2073 | 89 | } |
2074 | 247 | state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength); |
2075 | 247 | } |
2076 | | |
2077 | 247 | assert(state); |
2078 | | |
2079 | 247 | if (returnPtr) { |
2080 | | // If this is a stpcpy-style copy, but we were unable to check for a buffer |
2081 | | // overflow, we still need a result. Conjure a return value. |
2082 | 164 | if (ReturnEnd && Result.isUnknown()10 ) { |
2083 | 0 | Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); |
2084 | 0 | } |
2085 | 164 | } |
2086 | | // Set the return value. |
2087 | 247 | state = state->BindExpr(CE, LCtx, Result); |
2088 | 247 | C.addTransition(state); |
2089 | 247 | } |
2090 | | |
2091 | 111 | void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { |
2092 | | //int strcmp(const char *s1, const char *s2); |
2093 | 111 | evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ false); |
2094 | 111 | } |
2095 | | |
2096 | 115 | void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { |
2097 | | //int strncmp(const char *s1, const char *s2, size_t n); |
2098 | 115 | evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ false); |
2099 | 115 | } |
2100 | | |
2101 | | void CStringChecker::evalStrcasecmp(CheckerContext &C, |
2102 | 100 | const CallExpr *CE) const { |
2103 | | //int strcasecmp(const char *s1, const char *s2); |
2104 | 100 | evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ true); |
2105 | 100 | } |
2106 | | |
2107 | | void CStringChecker::evalStrncasecmp(CheckerContext &C, |
2108 | 116 | const CallExpr *CE) const { |
2109 | | //int strncasecmp(const char *s1, const char *s2, size_t n); |
2110 | 116 | evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ true); |
2111 | 116 | } |
2112 | | |
2113 | | void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, |
2114 | 442 | bool IsBounded, bool IgnoreCase) const { |
2115 | 442 | CurrentFunctionDescription = "string comparison function"; |
2116 | 442 | ProgramStateRef state = C.getState(); |
2117 | 442 | const LocationContext *LCtx = C.getLocationContext(); |
2118 | | |
2119 | | // Check that the first string is non-null |
2120 | 442 | AnyArgExpr Left = {CE->getArg(0), 0}; |
2121 | 442 | SVal LeftVal = state->getSVal(Left.Expression, LCtx); |
2122 | 442 | state = checkNonNull(C, state, Left, LeftVal); |
2123 | 442 | if (!state) |
2124 | 20 | return; |
2125 | | |
2126 | | // Check that the second string is non-null. |
2127 | 422 | AnyArgExpr Right = {CE->getArg(1), 1}; |
2128 | 422 | SVal RightVal = state->getSVal(Right.Expression, LCtx); |
2129 | 422 | state = checkNonNull(C, state, Right, RightVal); |
2130 | 422 | if (!state) |
2131 | 41 | return; |
2132 | | |
2133 | | // Get the string length of the first string or give up. |
2134 | 381 | SVal LeftLength = getCStringLength(C, state, Left.Expression, LeftVal); |
2135 | 381 | if (LeftLength.isUndef()) |
2136 | 0 | return; |
2137 | | |
2138 | | // Get the string length of the second string or give up. |
2139 | 381 | SVal RightLength = getCStringLength(C, state, Right.Expression, RightVal); |
2140 | 381 | if (RightLength.isUndef()) |
2141 | 0 | return; |
2142 | | |
2143 | | // If we know the two buffers are the same, we know the result is 0. |
2144 | | // First, get the two buffers' addresses. Another checker will have already |
2145 | | // made sure they're not undefined. |
2146 | 381 | DefinedOrUnknownSVal LV = LeftVal.castAs<DefinedOrUnknownSVal>(); |
2147 | 381 | DefinedOrUnknownSVal RV = RightVal.castAs<DefinedOrUnknownSVal>(); |
2148 | | |
2149 | | // See if they are the same. |
2150 | 381 | SValBuilder &svalBuilder = C.getSValBuilder(); |
2151 | 381 | DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); |
2152 | 381 | ProgramStateRef StSameBuf, StNotSameBuf; |
2153 | 381 | std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); |
2154 | | |
2155 | | // If the two arguments might be the same buffer, we know the result is 0, |
2156 | | // and we only need to check one size. |
2157 | 381 | if (StSameBuf) { |
2158 | 11 | StSameBuf = StSameBuf->BindExpr(CE, LCtx, |
2159 | 11 | svalBuilder.makeZeroVal(CE->getType())); |
2160 | 11 | C.addTransition(StSameBuf); |
2161 | | |
2162 | | // If the two arguments are GUARANTEED to be the same, we're done! |
2163 | 11 | if (!StNotSameBuf) |
2164 | 5 | return; |
2165 | 11 | } |
2166 | | |
2167 | 376 | assert(StNotSameBuf); |
2168 | 376 | state = StNotSameBuf; |
2169 | | |
2170 | | // At this point we can go about comparing the two buffers. |
2171 | | // For now, we only do this if they're both known string literals. |
2172 | | |
2173 | | // Attempt to extract string literals from both expressions. |
2174 | 376 | const StringLiteral *LeftStrLiteral = |
2175 | 376 | getCStringLiteral(C, state, Left.Expression, LeftVal); |
2176 | 376 | const StringLiteral *RightStrLiteral = |
2177 | 376 | getCStringLiteral(C, state, Right.Expression, RightVal); |
2178 | 376 | bool canComputeResult = false; |
2179 | 376 | SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, |
2180 | 376 | C.blockCount()); |
2181 | | |
2182 | 376 | if (LeftStrLiteral && RightStrLiteral375 ) { |
2183 | 370 | StringRef LeftStrRef = LeftStrLiteral->getString(); |
2184 | 370 | StringRef RightStrRef = RightStrLiteral->getString(); |
2185 | | |
2186 | 370 | if (IsBounded) { |
2187 | | // Get the max number of characters to compare. |
2188 | 200 | const Expr *lenExpr = CE->getArg(2); |
2189 | 200 | SVal lenVal = state->getSVal(lenExpr, LCtx); |
2190 | | |
2191 | | // If the length is known, we can get the right substrings. |
2192 | 200 | if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) { |
2193 | | // Create substrings of each to compare the prefix. |
2194 | 200 | LeftStrRef = LeftStrRef.substr(0, (size_t)len->getZExtValue()); |
2195 | 200 | RightStrRef = RightStrRef.substr(0, (size_t)len->getZExtValue()); |
2196 | 200 | canComputeResult = true; |
2197 | 200 | } |
2198 | 200 | } else { |
2199 | | // This is a normal, unbounded strcmp. |
2200 | 170 | canComputeResult = true; |
2201 | 170 | } |
2202 | | |
2203 | 370 | if (canComputeResult) { |
2204 | | // Real strcmp stops at null characters. |
2205 | 370 | size_t s1Term = LeftStrRef.find('\0'); |
2206 | 370 | if (s1Term != StringRef::npos) |
2207 | 20 | LeftStrRef = LeftStrRef.substr(0, s1Term); |
2208 | | |
2209 | 370 | size_t s2Term = RightStrRef.find('\0'); |
2210 | 370 | if (s2Term != StringRef::npos) |
2211 | 20 | RightStrRef = RightStrRef.substr(0, s2Term); |
2212 | | |
2213 | | // Use StringRef's comparison methods to compute the actual result. |
2214 | 370 | int compareRes = IgnoreCase ? LeftStrRef.compare_insensitive(RightStrRef)185 |
2215 | 370 | : LeftStrRef.compare(RightStrRef)185 ; |
2216 | | |
2217 | | // The strcmp function returns an integer greater than, equal to, or less |
2218 | | // than zero, [c11, p7.24.4.2]. |
2219 | 370 | if (compareRes == 0) { |
2220 | 110 | resultVal = svalBuilder.makeIntVal(compareRes, CE->getType()); |
2221 | 110 | } |
2222 | 260 | else { |
2223 | 260 | DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType()); |
2224 | | // Constrain strcmp's result range based on the result of StringRef's |
2225 | | // comparison methods. |
2226 | 260 | BinaryOperatorKind op = (compareRes > 0) ? BO_GT110 : BO_LT150 ; |
2227 | 260 | SVal compareWithZero = |
2228 | 260 | svalBuilder.evalBinOp(state, op, resultVal, zeroVal, |
2229 | 260 | svalBuilder.getConditionType()); |
2230 | 260 | DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>(); |
2231 | 260 | state = state->assume(compareWithZeroVal, true); |
2232 | 260 | } |
2233 | 370 | } |
2234 | 370 | } |
2235 | | |
2236 | 376 | state = state->BindExpr(CE, LCtx, resultVal); |
2237 | | |
2238 | | // Record this as a possible path. |
2239 | 376 | C.addTransition(state); |
2240 | 376 | } |
2241 | | |
2242 | 31 | void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { |
2243 | | // char *strsep(char **stringp, const char *delim); |
2244 | | // Verify whether the search string parameter matches the return type. |
2245 | 31 | SourceArgExpr SearchStrPtr = {{CE->getArg(0), 0}}; |
2246 | | |
2247 | 31 | QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType(); |
2248 | 31 | if (CharPtrTy.isNull() || |
2249 | 31 | CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType()) |
2250 | 0 | return; |
2251 | | |
2252 | 31 | CurrentFunctionDescription = "strsep()"; |
2253 | 31 | ProgramStateRef State = C.getState(); |
2254 | 31 | const LocationContext *LCtx = C.getLocationContext(); |
2255 | | |
2256 | | // Check that the search string pointer is non-null (though it may point to |
2257 | | // a null string). |
2258 | 31 | SVal SearchStrVal = State->getSVal(SearchStrPtr.Expression, LCtx); |
2259 | 31 | State = checkNonNull(C, State, SearchStrPtr, SearchStrVal); |
2260 | 31 | if (!State) |
2261 | 5 | return; |
2262 | | |
2263 | | // Check that the delimiter string is non-null. |
2264 | 26 | AnyArgExpr DelimStr = {CE->getArg(1), 1}; |
2265 | 26 | SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx); |
2266 | 26 | State = checkNonNull(C, State, DelimStr, DelimStrVal); |
2267 | 26 | if (!State) |
2268 | 5 | return; |
2269 | | |
2270 | 21 | SValBuilder &SVB = C.getSValBuilder(); |
2271 | 21 | SVal Result; |
2272 | 21 | if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { |
2273 | | // Get the current value of the search string pointer, as a char*. |
2274 | 21 | Result = State->getSVal(*SearchStrLoc, CharPtrTy); |
2275 | | |
2276 | | // Invalidate the search string, representing the change of one delimiter |
2277 | | // character to NUL. |
2278 | | // As the replacement never overflows, do not invalidate its super region. |
2279 | 21 | State = invalidateDestinationBufferNeverOverflows( |
2280 | 21 | C, State, SearchStrPtr.Expression, Result); |
2281 | | |
2282 | | // Overwrite the search string pointer. The new value is either an address |
2283 | | // further along in the same string, or NULL if there are no more tokens. |
2284 | 21 | State = State->bindLoc(*SearchStrLoc, |
2285 | 21 | SVB.conjureSymbolVal(getTag(), |
2286 | 21 | CE, |
2287 | 21 | LCtx, |
2288 | 21 | CharPtrTy, |
2289 | 21 | C.blockCount()), |
2290 | 21 | LCtx); |
2291 | 21 | } else { |
2292 | 0 | assert(SearchStrVal.isUnknown()); |
2293 | | // Conjure a symbolic value. It's the best we can do. |
2294 | 0 | Result = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); |
2295 | 0 | } |
2296 | | |
2297 | | // Set the return value, and finish. |
2298 | 21 | State = State->BindExpr(CE, LCtx, Result); |
2299 | 21 | C.addTransition(State); |
2300 | 21 | } |
2301 | | |
2302 | | // These should probably be moved into a C++ standard library checker. |
2303 | 8 | void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const { |
2304 | 8 | evalStdCopyCommon(C, CE); |
2305 | 8 | } |
2306 | | |
2307 | | void CStringChecker::evalStdCopyBackward(CheckerContext &C, |
2308 | 5 | const CallExpr *CE) const { |
2309 | 5 | evalStdCopyCommon(C, CE); |
2310 | 5 | } |
2311 | | |
2312 | | void CStringChecker::evalStdCopyCommon(CheckerContext &C, |
2313 | 13 | const CallExpr *CE) const { |
2314 | 13 | if (!CE->getArg(2)->getType()->isPointerType()) |
2315 | 2 | return; |
2316 | | |
2317 | 11 | ProgramStateRef State = C.getState(); |
2318 | | |
2319 | 11 | const LocationContext *LCtx = C.getLocationContext(); |
2320 | | |
2321 | | // template <class _InputIterator, class _OutputIterator> |
2322 | | // _OutputIterator |
2323 | | // copy(_InputIterator __first, _InputIterator __last, |
2324 | | // _OutputIterator __result) |
2325 | | |
2326 | | // Invalidate the destination buffer |
2327 | 11 | const Expr *Dst = CE->getArg(2); |
2328 | 11 | SVal DstVal = State->getSVal(Dst, LCtx); |
2329 | | // FIXME: As we do not know how many items are copied, we also invalidate the |
2330 | | // super region containing the target location. |
2331 | 11 | State = |
2332 | 11 | invalidateDestinationBufferAlwaysEscapeSuperRegion(C, State, Dst, DstVal); |
2333 | | |
2334 | 11 | SValBuilder &SVB = C.getSValBuilder(); |
2335 | | |
2336 | 11 | SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); |
2337 | 11 | State = State->BindExpr(CE, LCtx, ResultVal); |
2338 | | |
2339 | 11 | C.addTransition(State); |
2340 | 11 | } |
2341 | | |
2342 | 184 | void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { |
2343 | | // void *memset(void *s, int c, size_t n); |
2344 | 184 | CurrentFunctionDescription = "memory set function"; |
2345 | | |
2346 | 184 | DestinationArgExpr Buffer = {{CE->getArg(0), 0}}; |
2347 | 184 | AnyArgExpr CharE = {CE->getArg(1), 1}; |
2348 | 184 | SizeArgExpr Size = {{CE->getArg(2), 2}}; |
2349 | | |
2350 | 184 | ProgramStateRef State = C.getState(); |
2351 | | |
2352 | | // See if the size argument is zero. |
2353 | 184 | const LocationContext *LCtx = C.getLocationContext(); |
2354 | 184 | SVal SizeVal = C.getSVal(Size.Expression); |
2355 | 184 | QualType SizeTy = Size.Expression->getType(); |
2356 | | |
2357 | 184 | ProgramStateRef ZeroSize, NonZeroSize; |
2358 | 184 | std::tie(ZeroSize, NonZeroSize) = assumeZero(C, State, SizeVal, SizeTy); |
2359 | | |
2360 | | // Get the value of the memory area. |
2361 | 184 | SVal BufferPtrVal = C.getSVal(Buffer.Expression); |
2362 | | |
2363 | | // If the size is zero, there won't be any actual memory access, so |
2364 | | // just bind the return value to the buffer and return. |
2365 | 184 | if (ZeroSize && !NonZeroSize0 ) { |
2366 | 0 | ZeroSize = ZeroSize->BindExpr(CE, LCtx, BufferPtrVal); |
2367 | 0 | C.addTransition(ZeroSize); |
2368 | 0 | return; |
2369 | 0 | } |
2370 | | |
2371 | | // Ensure the memory area is not null. |
2372 | | // If it is NULL there will be a NULL pointer dereference. |
2373 | 184 | State = checkNonNull(C, NonZeroSize, Buffer, BufferPtrVal); |
2374 | 184 | if (!State) |
2375 | 1 | return; |
2376 | | |
2377 | 183 | State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write); |
2378 | 183 | if (!State) |
2379 | 2 | return; |
2380 | | |
2381 | | // According to the values of the arguments, bind the value of the second |
2382 | | // argument to the destination buffer and set string length, or just |
2383 | | // invalidate the destination buffer. |
2384 | 181 | if (!memsetAux(Buffer.Expression, C.getSVal(CharE.Expression), |
2385 | 181 | Size.Expression, C, State)) |
2386 | 0 | return; |
2387 | | |
2388 | 181 | State = State->BindExpr(CE, LCtx, BufferPtrVal); |
2389 | 181 | C.addTransition(State); |
2390 | 181 | } |
2391 | | |
2392 | 30 | void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { |
2393 | 30 | CurrentFunctionDescription = "memory clearance function"; |
2394 | | |
2395 | 30 | DestinationArgExpr Buffer = {{CE->getArg(0), 0}}; |
2396 | 30 | SizeArgExpr Size = {{CE->getArg(1), 1}}; |
2397 | 30 | SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy); |
2398 | | |
2399 | 30 | ProgramStateRef State = C.getState(); |
2400 | | |
2401 | | // See if the size argument is zero. |
2402 | 30 | SVal SizeVal = C.getSVal(Size.Expression); |
2403 | 30 | QualType SizeTy = Size.Expression->getType(); |
2404 | | |
2405 | 30 | ProgramStateRef StateZeroSize, StateNonZeroSize; |
2406 | 30 | std::tie(StateZeroSize, StateNonZeroSize) = |
2407 | 30 | assumeZero(C, State, SizeVal, SizeTy); |
2408 | | |
2409 | | // If the size is zero, there won't be any actual memory access, |
2410 | | // In this case we just return. |
2411 | 30 | if (StateZeroSize && !StateNonZeroSize0 ) { |
2412 | 0 | C.addTransition(StateZeroSize); |
2413 | 0 | return; |
2414 | 0 | } |
2415 | | |
2416 | | // Get the value of the memory area. |
2417 | 30 | SVal MemVal = C.getSVal(Buffer.Expression); |
2418 | | |
2419 | | // Ensure the memory area is not null. |
2420 | | // If it is NULL there will be a NULL pointer dereference. |
2421 | 30 | State = checkNonNull(C, StateNonZeroSize, Buffer, MemVal); |
2422 | 30 | if (!State) |
2423 | 10 | return; |
2424 | | |
2425 | 20 | State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write); |
2426 | 20 | if (!State) |
2427 | 4 | return; |
2428 | | |
2429 | 16 | if (!memsetAux(Buffer.Expression, Zero, Size.Expression, C, State)) |
2430 | 0 | return; |
2431 | | |
2432 | 16 | C.addTransition(State); |
2433 | 16 | } |
2434 | | |
2435 | 20 | void CStringChecker::evalSprintf(CheckerContext &C, const CallExpr *CE) const { |
2436 | 20 | CurrentFunctionDescription = "'sprintf'"; |
2437 | 20 | bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___sprintf_chk; |
2438 | 20 | evalSprintfCommon(C, CE, /* IsBounded */ false, IsBI); |
2439 | 20 | } |
2440 | | |
2441 | 24 | void CStringChecker::evalSnprintf(CheckerContext &C, const CallExpr *CE) const { |
2442 | 24 | CurrentFunctionDescription = "'snprintf'"; |
2443 | 24 | bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___snprintf_chk; |
2444 | 24 | evalSprintfCommon(C, CE, /* IsBounded */ true, IsBI); |
2445 | 24 | } |
2446 | | |
2447 | | void CStringChecker::evalSprintfCommon(CheckerContext &C, const CallExpr *CE, |
2448 | 44 | bool IsBounded, bool IsBuiltin) const { |
2449 | 44 | ProgramStateRef State = C.getState(); |
2450 | 44 | DestinationArgExpr Dest = {{CE->getArg(0), 0}}; |
2451 | | |
2452 | 44 | const auto NumParams = CE->getCalleeDecl()->getAsFunction()->getNumParams(); |
2453 | 44 | assert(CE->getNumArgs() >= NumParams); |
2454 | | |
2455 | 44 | const auto AllArguments = |
2456 | 44 | llvm::make_range(CE->getArgs(), CE->getArgs() + CE->getNumArgs()); |
2457 | 44 | const auto VariadicArguments = drop_begin(enumerate(AllArguments), NumParams); |
2458 | | |
2459 | 56 | for (const auto &[ArgIdx, ArgExpr] : VariadicArguments) { |
2460 | | // We consider only string buffers |
2461 | 56 | if (const QualType type = ArgExpr->getType(); |
2462 | 56 | !type->isAnyPointerType() || |
2463 | 56 | !type->getPointeeType()->isAnyCharacterType()36 ) |
2464 | 20 | continue; |
2465 | 36 | SourceArgExpr Source = {{ArgExpr, unsigned(ArgIdx)}}; |
2466 | | |
2467 | | // Ensure the buffers do not overlap. |
2468 | 36 | SizeArgExpr SrcExprAsSizeDummy = { |
2469 | 36 | {Source.Expression, Source.ArgumentIndex}}; |
2470 | 36 | State = CheckOverlap( |
2471 | 36 | C, State, |
2472 | 36 | (IsBounded ? SizeArgExpr{{CE->getArg(1), 1}}20 : SrcExprAsSizeDummy16 ), |
2473 | 36 | Dest, Source); |
2474 | 36 | if (!State) |
2475 | 28 | return; |
2476 | 36 | } |
2477 | | |
2478 | 16 | C.addTransition(State); |
2479 | 16 | } |
2480 | | |
2481 | | //===----------------------------------------------------------------------===// |
2482 | | // The driver method, and other Checker callbacks. |
2483 | | //===----------------------------------------------------------------------===// |
2484 | | |
2485 | | CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call, |
2486 | 35.7k | CheckerContext &C) const { |
2487 | 35.7k | const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); |
2488 | 35.7k | if (!CE) |
2489 | 3.46k | return nullptr; |
2490 | | |
2491 | 32.3k | const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); |
2492 | 32.3k | if (!FD) |
2493 | 23 | return nullptr; |
2494 | | |
2495 | 32.2k | if (StdCopy.matches(Call)) |
2496 | 8 | return &CStringChecker::evalStdCopy; |
2497 | 32.2k | if (StdCopyBackward.matches(Call)) |
2498 | 5 | return &CStringChecker::evalStdCopyBackward; |
2499 | | |
2500 | | // Pro-actively check that argument types are safe to do arithmetic upon. |
2501 | | // We do not want to crash if someone accidentally passes a structure |
2502 | | // into, say, a C++ overload of any of these functions. We could not check |
2503 | | // that for std::copy because they may have arguments of other types. |
2504 | 32.2k | for (auto I : CE->arguments()) { |
2505 | 31.2k | QualType T = I->getType(); |
2506 | 31.2k | if (!T->isIntegralOrEnumerationType() && !T->isPointerType()20.7k ) |
2507 | 11.6k | return nullptr; |
2508 | 31.2k | } |
2509 | | |
2510 | 20.6k | const FnCheck *Callback = Callbacks.lookup(Call); |
2511 | 20.6k | if (Callback) |
2512 | 2.39k | return *Callback; |
2513 | | |
2514 | 18.2k | return nullptr; |
2515 | 20.6k | } |
2516 | | |
2517 | 35.7k | bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { |
2518 | 35.7k | FnCheck Callback = identifyCall(Call, C); |
2519 | | |
2520 | | // If the callee isn't a string function, let another checker handle it. |
2521 | 35.7k | if (!Callback) |
2522 | 33.3k | return false; |
2523 | | |
2524 | | // Check and evaluate the call. |
2525 | 2.40k | const auto *CE = cast<CallExpr>(Call.getOriginExpr()); |
2526 | 2.40k | Callback(this, C, CE); |
2527 | | |
2528 | | // If the evaluate call resulted in no change, chain to the next eval call |
2529 | | // handler. |
2530 | | // Note, the custom CString evaluation calls assume that basic safety |
2531 | | // properties are held. However, if the user chooses to turn off some of these |
2532 | | // checks, we ignore the issues and leave the call evaluation to a generic |
2533 | | // handler. |
2534 | 2.40k | return C.isDifferent(); |
2535 | 35.7k | } |
2536 | | |
2537 | 12.6k | void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { |
2538 | | // Record string length for char a[] = "abc"; |
2539 | 12.6k | ProgramStateRef state = C.getState(); |
2540 | | |
2541 | 12.6k | for (const auto *I : DS->decls()) { |
2542 | 12.6k | const VarDecl *D = dyn_cast<VarDecl>(I); |
2543 | 12.6k | if (!D) |
2544 | 0 | continue; |
2545 | | |
2546 | | // FIXME: Handle array fields of structs. |
2547 | 12.6k | if (!D->getType()->isArrayType()) |
2548 | 11.6k | continue; |
2549 | | |
2550 | 942 | const Expr *Init = D->getInit(); |
2551 | 942 | if (!Init) |
2552 | 282 | continue; |
2553 | 660 | if (!isa<StringLiteral>(Init)) |
2554 | 464 | continue; |
2555 | | |
2556 | 196 | Loc VarLoc = state->getLValue(D, C.getLocationContext()); |
2557 | 196 | const MemRegion *MR = VarLoc.getAsRegion(); |
2558 | 196 | if (!MR) |
2559 | 0 | continue; |
2560 | | |
2561 | 196 | SVal StrVal = C.getSVal(Init); |
2562 | 196 | assert(StrVal.isValid() && "Initializer string is unknown or undefined"); |
2563 | 196 | DefinedOrUnknownSVal strLength = |
2564 | 196 | getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>(); |
2565 | | |
2566 | 196 | state = state->set<CStringLength>(MR, strLength); |
2567 | 196 | } |
2568 | | |
2569 | 12.6k | C.addTransition(state); |
2570 | 12.6k | } |
2571 | | |
2572 | | ProgramStateRef |
2573 | | CStringChecker::checkRegionChanges(ProgramStateRef state, |
2574 | | const InvalidatedSymbols *, |
2575 | | ArrayRef<const MemRegion *> ExplicitRegions, |
2576 | | ArrayRef<const MemRegion *> Regions, |
2577 | | const LocationContext *LCtx, |
2578 | 56.0k | const CallEvent *Call) const { |
2579 | 56.0k | CStringLengthTy Entries = state->get<CStringLength>(); |
2580 | 56.0k | if (Entries.isEmpty()) |
2581 | 54.8k | return state; |
2582 | | |
2583 | 1.16k | llvm::SmallPtrSet<const MemRegion *, 8> Invalidated; |
2584 | 1.16k | llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions; |
2585 | | |
2586 | | // First build sets for the changed regions and their super-regions. |
2587 | 1.45k | for (const MemRegion *MR : Regions) { |
2588 | 1.45k | Invalidated.insert(MR); |
2589 | | |
2590 | 1.45k | SuperRegions.insert(MR); |
2591 | 2.57k | while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) { |
2592 | 1.11k | MR = SR->getSuperRegion(); |
2593 | 1.11k | SuperRegions.insert(MR); |
2594 | 1.11k | } |
2595 | 1.45k | } |
2596 | | |
2597 | 1.16k | CStringLengthTy::Factory &F = state->get_context<CStringLength>(); |
2598 | | |
2599 | | // Then loop over the entries in the current state. |
2600 | 1.35k | for (const MemRegion *MR : llvm::make_first_range(Entries)) { |
2601 | | // Is this entry for a super-region of a changed region? |
2602 | 1.35k | if (SuperRegions.count(MR)) { |
2603 | 665 | Entries = F.remove(Entries, MR); |
2604 | 665 | continue; |
2605 | 665 | } |
2606 | | |
2607 | | // Is this entry for a sub-region of a changed region? |
2608 | 691 | const MemRegion *Super = MR; |
2609 | 1.39k | while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) { |
2610 | 739 | Super = SR->getSuperRegion(); |
2611 | 739 | if (Invalidated.count(Super)) { |
2612 | 34 | Entries = F.remove(Entries, MR); |
2613 | 34 | break; |
2614 | 34 | } |
2615 | 739 | } |
2616 | 691 | } |
2617 | | |
2618 | 1.16k | return state->set<CStringLength>(Entries); |
2619 | 56.0k | } |
2620 | | |
2621 | | void CStringChecker::checkLiveSymbols(ProgramStateRef state, |
2622 | 137k | SymbolReaper &SR) const { |
2623 | | // Mark all symbols in our string length map as valid. |
2624 | 137k | CStringLengthTy Entries = state->get<CStringLength>(); |
2625 | | |
2626 | 137k | for (SVal Len : llvm::make_second_range(Entries)) { |
2627 | 3.04k | for (SymbolRef Sym : Len.symbols()) |
2628 | 1.89k | SR.markInUse(Sym); |
2629 | 3.04k | } |
2630 | 137k | } |
2631 | | |
2632 | | void CStringChecker::checkDeadSymbols(SymbolReaper &SR, |
2633 | 137k | CheckerContext &C) const { |
2634 | 137k | ProgramStateRef state = C.getState(); |
2635 | 137k | CStringLengthTy Entries = state->get<CStringLength>(); |
2636 | 137k | if (Entries.isEmpty()) |
2637 | 134k | return; |
2638 | | |
2639 | 2.57k | CStringLengthTy::Factory &F = state->get_context<CStringLength>(); |
2640 | 3.04k | for (auto [Reg, Len] : Entries) { |
2641 | 3.04k | if (SymbolRef Sym = Len.getAsSymbol()) { |
2642 | 1.84k | if (SR.isDead(Sym)) |
2643 | 479 | Entries = F.remove(Entries, Reg); |
2644 | 1.84k | } |
2645 | 3.04k | } |
2646 | | |
2647 | 2.57k | state = state->set<CStringLength>(Entries); |
2648 | 2.57k | C.addTransition(state); |
2649 | 2.57k | } |
2650 | | |
2651 | 273 | void ento::registerCStringModeling(CheckerManager &Mgr) { |
2652 | 273 | Mgr.registerChecker<CStringChecker>(); |
2653 | 273 | } |
2654 | | |
2655 | 2.01k | bool ento::shouldRegisterCStringModeling(const CheckerManager &mgr) { |
2656 | 2.01k | return true; |
2657 | 2.01k | } |
2658 | | |
2659 | | #define REGISTER_CHECKER(name) \ |
2660 | 181 | void ento::register##name(CheckerManager &mgr) { \ |
2661 | 181 | CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ |
2662 | 181 | checker->Filter.Check##name = true; \ |
2663 | 181 | checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ |
2664 | 181 | } \ clang::ento::registerCStringNullArg(clang::ento::CheckerManager&) Line | Count | Source | 2660 | 93 | void ento::register##name(CheckerManager &mgr) { \ | 2661 | 93 | CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ | 2662 | 93 | checker->Filter.Check##name = true; \ | 2663 | 93 | checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ | 2664 | 93 | } \ |
clang::ento::registerCStringOutOfBounds(clang::ento::CheckerManager&) Line | Count | Source | 2660 | 22 | void ento::register##name(CheckerManager &mgr) { \ | 2661 | 22 | CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ | 2662 | 22 | checker->Filter.Check##name = true; \ | 2663 | 22 | checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ | 2664 | 22 | } \ |
clang::ento::registerCStringBufferOverlap(clang::ento::CheckerManager&) Line | Count | Source | 2660 | 27 | void ento::register##name(CheckerManager &mgr) { \ | 2661 | 27 | CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ | 2662 | 27 | checker->Filter.Check##name = true; \ | 2663 | 27 | checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ | 2664 | 27 | } \ |
clang::ento::registerCStringNotNullTerm(clang::ento::CheckerManager&) Line | Count | Source | 2660 | 23 | void ento::register##name(CheckerManager &mgr) { \ | 2661 | 23 | CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ | 2662 | 23 | checker->Filter.Check##name = true; \ | 2663 | 23 | checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ | 2664 | 23 | } \ |
clang::ento::registerCStringUninitializedRead(clang::ento::CheckerManager&) Line | Count | Source | 2660 | 16 | void ento::register##name(CheckerManager &mgr) { \ | 2661 | 16 | CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ | 2662 | 16 | checker->Filter.Check##name = true; \ | 2663 | 16 | checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ | 2664 | 16 | } \ |
|
2665 | | \ |
2666 | 366 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } clang::ento::shouldRegisterCStringNullArg(clang::ento::CheckerManager const&) Line | Count | Source | 2666 | 190 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterCStringOutOfBounds(clang::ento::CheckerManager const&) Line | Count | Source | 2666 | 44 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterCStringBufferOverlap(clang::ento::CheckerManager const&) Line | Count | Source | 2666 | 54 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterCStringNotNullTerm(clang::ento::CheckerManager const&) Line | Count | Source | 2666 | 46 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterCStringUninitializedRead(clang::ento::CheckerManager const&) Line | Count | Source | 2666 | 32 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
|
2667 | | |
2668 | | REGISTER_CHECKER(CStringNullArg) |
2669 | | REGISTER_CHECKER(CStringOutOfBounds) |
2670 | | REGISTER_CHECKER(CStringBufferOverlap) |
2671 | | REGISTER_CHECKER(CStringNotNullTerm) |
2672 | | REGISTER_CHECKER(CStringUninitializedRead) |