clang 24.0.0git
ArrayBoundChecker.cpp
Go to the documentation of this file.
1//== ArrayBoundChecker.cpp -------------------------------------------------==//
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 file defines security.ArrayBound, which is a path-sensitive checker
10// that looks for out of bounds access of memory regions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/CharUnits.h"
26#include "llvm/ADT/APSInt.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/raw_ostream.h"
29#include <optional>
30
31using namespace clang;
32using namespace ento;
33using namespace taint;
34using llvm::formatv;
35
36namespace {
37/// If `E` is an array subscript expression with a base that is "clean" (= not
38/// modified by pointer arithmetic = the beginning of a memory region), return
39/// it as a pointer to ArraySubscriptExpr; otherwise return nullptr.
40/// This helper function is used by two separate heuristics that are only valid
41/// in these "clean" cases.
42static const ArraySubscriptExpr *
43getAsCleanArraySubscriptExpr(const Expr *E, const CheckerContext &C) {
44 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
45 if (!ASE)
46 return nullptr;
47
48 const MemRegion *SubscriptBaseReg = C.getSVal(ASE->getBase()).getAsRegion();
49 if (!SubscriptBaseReg)
50 return nullptr;
51
52 // The base of the subscript expression is affected by pointer arithmetics,
53 // so we want to report byte offsets instead of indices and we don't want to
54 // activate the "index is unsigned -> cannot be negative" shortcut.
55 if (isa<ElementRegion>(SubscriptBaseReg->StripCasts()))
56 return nullptr;
57
58 return ASE;
59}
60
61class SizeUnit {
62 QualType AsType;
63 int64_t AsCharUnits;
64
65 SizeUnit() : AsType(), AsCharUnits(1) {}
66
67public:
68 SizeUnit(QualType T, const ASTContext &ACtx)
69 : AsType(T), AsCharUnits(ACtx.getTypeSizeInChars(T).getQuantity()) {
70 assert(!T.isNull());
71 }
72
73 static SizeUnit bytes() { return SizeUnit(); }
74
75 bool isBytes() const { return AsType.isNull(); }
76
77 /// Return the element type that is "natural" for reporting out-of-bounds
78 /// memory access to \p ER.
79 static SizeUnit forElementRegion(const ElementRegion *ER,
80 const ASTContext &ACtx) {
81 return SizeUnit(ER->getElementType(), ACtx);
82 }
83
84 /// If `E` is a "clean" array subscript expression, return the type of the
85 /// accessed element; otherwise return 'Bytes' because that's the best (or
86 /// least bad) option for the assumption messages that use this.
87 /// FIXME: It is unfortunate that this heuristic differs from the heuristic
88 /// used for reporting assumption; but this difference is currently needed
89 /// due to the unfortunate phrasing of the assumption messages.
90 /// Get rid of this when the assumption note is rephrased and improved.
91 static SizeUnit forExpr(const Expr *E, const CheckerContext &C) {
92 const auto *ASE = getAsCleanArraySubscriptExpr(E, C);
93 if (!ASE)
94 return bytes();
95
96 return SizeUnit(ASE->getType(), C.getASTContext());
97 }
98
99 int64_t asCharUnits() const { return AsCharUnits; }
100
101 bool canExpress(std::optional<int64_t> Val) const {
102 return asCharUnits() && (!Val || !(*Val % asCharUnits()));
103 }
104
105 std::string asExtentDesc() const {
106 if (isBytes())
107 return "the extent of";
108 return formatv("the number of '{0}' elements in", AsType.getAsString());
109 }
110
111 std::string asElementName() const {
112 if (isBytes())
113 return "byte";
114 return formatv("'{0}' element", AsType.getAsString());
115 }
116};
117
118/// Strings that will be passed to the parameters 'desc' and 'fullDesc' of the
119/// constructor of 'PathSensitiveBugReport'.
120struct BugDescription {
121 std::string Short;
122 std::string Full;
123};
124
125// NOTE: The `ArraySubscriptExpr` and `UnaryOperator` callbacks are `PostStmt`
126// instead of `PreStmt` because the current implementation passes the whole
127// expression to `CheckerContext::getSVal()` which only works after the
128// symbolic evaluation of the expression. (To turn them into `PreStmt`
129// callbacks, we'd need to duplicate the logic that evaluates these
130// expressions.) The `MemberExpr` callback would work as `PreStmt` but it's
131// defined as `PostStmt` for the sake of consistency with the other callbacks.
132class ArrayBoundChecker : public Checker<check::PostStmt<ArraySubscriptExpr>,
133 check::PostStmt<UnaryOperator>,
134 check::PostStmt<MemberExpr>> {
135 BugType BT{this, "Out-of-bound access"};
136 BugType TaintBT{this, "Out-of-bound access", categories::TaintedData};
137
138 void handleAccessExpr(const Expr *E, CheckerContext &C) const;
139
140 void reportOOB(CheckerContext &C, ProgramStateRef ErrorState,
141 BugDescription Desc, NonLoc Offset,
142 std::optional<NonLoc> Extent, bool IsTaintBug = false) const;
143
144 static void markPartsInteresting(PathSensitiveBugReport &BR,
145 ProgramStateRef ErrorState, NonLoc Val,
146 bool MarkTaint);
147
148 static bool isFromCtypeMacro(const Expr *E, ASTContext &AC);
149
150 static bool isOffsetObviouslyNonnegative(const Expr *E, CheckerContext &C);
151
152 static bool isInAddressOf(const Stmt *S, ASTContext &AC);
153
154public:
155 void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const {
156 handleAccessExpr(E, C);
157 }
158 void checkPostStmt(const UnaryOperator *E, CheckerContext &C) const {
159 if (E->getOpcode() == UO_Deref)
160 handleAccessExpr(E, C);
161 }
162 void checkPostStmt(const MemberExpr *E, CheckerContext &C) const {
163 if (E->isArrow())
164 handleAccessExpr(E->getBase(), C);
165 }
166};
167
168} // anonymous namespace
169
170/// Return true if information about the value of \p SV can put constraints
171/// on some symbol which is interesting within the bug report \p BR.
172/// In particular, this returns true when \p SV is interesting within \p BR;
173/// but it also returns true if \p SV is an expression that contains integer
174/// constants and a single symbolic operand which is interesting (in \p BR).
175/// We need to use this instead of plain `BR.isInteresting()` because if we
176/// are analyzing code like
177/// int array[10];
178/// int f(int arg) {
179/// return array[arg] && array[arg + 10];
180/// }
181/// then the byte offsets are `arg * 4` and `(arg + 10) * 4`, which are not
182/// sub-expressions of each other (but `getSimplifiedOffsets` is smart enough
183/// to detect this out of bounds access).
186 SymbolRef Sym = SV.getAsSymbol();
187 if (!Sym)
188 return false;
189 for (SymbolRef PartSym : Sym->symbols()) {
190 // The interestingess mark may appear on any layer as we're stripping off
191 // the SymIntExpr, UnarySymExpr etc. layers...
192 if (BR.isInteresting(PartSym))
193 return true;
194 // ...but if both sides of the expression are symbolic, then there is no
195 // practical algorithm to produce separate constraints for the two
196 // operands (from the single combined result).
197 if (isa<SymSymExpr>(PartSym))
198 return false;
199 }
200 return false;
201}
202
203/// For a given \p CurRegion that can be represented as a symbolic expression
204/// Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block
205/// Arr and the distance of Location from the beginning of Arr (expressed in a
206/// NonLoc that specifies the number of CharUnits). Returns nullopt when these
207/// cannot be determined.
208static std::optional<std::pair<const SubRegion *, NonLoc>>
210 const ElementRegion *CurRegion) {
212 auto EvalBinOp = [&SVB, State, T](BinaryOperatorKind Op, NonLoc L, NonLoc R) {
213 // We will use this utility to add and multiply values.
214 return SVB.evalBinOpNN(State, Op, L, R, T).getAs<NonLoc>();
215 };
216
217 const SubRegion *OwnerRegion = nullptr;
218 std::optional<NonLoc> Offset = SVB.makeZeroArrayIndex();
219
220 while (CurRegion) {
221 const auto Index = CurRegion->getIndex().getAs<NonLoc>();
222 if (!Index)
223 return std::nullopt;
224
225 QualType ElemType = CurRegion->getElementType();
226
227 // FIXME: The following early return was presumably added to safeguard the
228 // getTypeSizeInChars() call (which doesn't accept an incomplete type), but
229 // it seems that `ElemType` cannot be incomplete at this point.
230 if (ElemType->isIncompleteType())
231 return std::nullopt;
232
233 // Calculate Delta = Index * sizeof(ElemType).
234 NonLoc Size = SVB.makeArrayIndex(
235 SVB.getContext().getTypeSizeInChars(ElemType).getQuantity());
236 auto Delta = EvalBinOp(BO_Mul, *Index, Size);
237 if (!Delta)
238 return std::nullopt;
239
240 // Perform Offset += Delta.
241 Offset = EvalBinOp(BO_Add, *Offset, *Delta);
242 if (!Offset)
243 return std::nullopt;
244
245 OwnerRegion = CurRegion->getSuperRegion()->getAs<SubRegion>();
246 // When this is just another ElementRegion layer, we need to continue the
247 // offset calculations:
248 CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion);
249 }
250
251 if (OwnerRegion)
252 return std::make_pair(OwnerRegion, *Offset);
253
254 return std::nullopt;
255}
256
257static std::optional<int64_t> getConcreteValue(NonLoc SV) {
258 if (auto ConcreteVal = SV.getAs<nonloc::ConcreteInt>()) {
259 return ConcreteVal->getValue()->tryExtValue();
260 }
261 return std::nullopt;
262}
263
264static std::optional<int64_t> getConcreteValue(std::optional<NonLoc> SV) {
265 return SV ? getConcreteValue(*SV) : std::nullopt;
266}
267
268static StringRef getAdjective(const bounds::CheckResult &R) {
269 return (R.mayUnderflow()
270 ? (R.mayOverflow() ? "a negative or overflowing" : "a negative")
271 : (R.mayOverflow() ? "an overflowing" : "a valid"));
272}
273
274static StringRef getPreposition(const bounds::CheckResult &R) {
275 return (R.mayUnderflow() ? (R.mayOverflow() ? "around" : "preceding")
276 : (R.mayOverflow() ? "after the end of" : "within"));
277}
278
280 StringRef RegName, SizeUnit SU) {
281 std::optional<int64_t> OffsetN = getConcreteValue(Res.getOffset());
282 std::optional<int64_t> ExtentN =
284
285 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
286 if (OffsetN)
287 *OffsetN /= SU.asCharUnits();
288 if (ExtentN)
289 *ExtentN /= SU.asCharUnits();
290 } else {
291 // Fall back to reporting the offsets in bytes.
292 SU = SizeUnit::bytes();
293 }
294
295 StringRef OffsetOrIndex = SU.isBytes() ? "byte offset" : "index";
296
298 llvm::raw_svector_ostream Out(Buf);
299 Out << "Access of ";
300 if (OffsetN && !ExtentN && !SU.isBytes()) {
301 // If the offset is reported as an index, then the report must mention the
302 // element type (because it is not always clear from the code). It's more
303 // natural to mention the element type later where the extent is described,
304 // but if the extent is unknown/irrelevant, then the element type can be
305 // inserted into the message at this point.
306 Out << SU.asElementName() << " in ";
307 }
308 Out << RegName << " at ";
309 if (OffsetN) {
310 if (Res.mayUnderflow() && !Res.mayOverflow())
311 Out << "negative ";
312 Out << OffsetOrIndex << " " << *OffsetN;
313 } else {
314 Out << getAdjective(Res) << " " << OffsetOrIndex;
315 }
316 if (ExtentN) {
317 Out << ", while it holds only ";
318 if (*ExtentN != 1)
319 Out << *ExtentN;
320 else
321 Out << "a single";
322
323 Out << ' ' << SU.asElementName();
324
325 if (*ExtentN > 1)
326 Out << "s";
327 }
328
329 return {formatv("Out of bound access to memory {0} {1}", getPreposition(Res),
330 RegName),
331 std::string(Buf)};
332}
333
334static BugDescription describeTaintBug(StringRef RegName, StringRef OffsetName,
335 bool AlsoMentionUnderflow) {
336 return {formatv("Potential out of bound access to {0} with tainted {1}",
337 RegName, OffsetName),
338 formatv("Access of {0} with a tainted {1} that may be {2}too large",
339 RegName, OffsetName,
340 AlsoMentionUnderflow ? "negative or " : "")};
341}
342
343/// When the access was ambiguous (that is, mayBeInBounds() && mayBeInvalid()),
344/// returns the note "assuming in bounds" note that is relevant for the bug
345/// report \p BR. When the access wasn't ambiguous or the the assumption is
346/// irrelevant for \p BR, this returns the empty string (which signifies "do
347/// not emit a note tag" when returned by a note tag callback).
350 StringRef RegName, SizeUnit SU) {
351 bool ShouldReportNonNegative = Res.mayUnderflow();
353 std::optional<NonLoc> E = Res.getExtentIfMayOverflow();
354 if (E && isDeterminedByInterestingSymbol(*E, BR)) {
355 // Even if the byte offset isn't interesting (e.g. it's a constant value),
356 // the assumption can still be interesting if it provides information
357 // about an interesting symbolic upper bound.
358 ShouldReportNonNegative = false;
359 } else {
360 // We don't have anything interesting, don't report the assumption.
361 return "";
362 }
363 }
364
365 std::optional<int64_t> OffsetN = getConcreteValue(Res.getOffset());
366 std::optional<int64_t> ExtentN =
368
369 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
370 if (OffsetN)
371 *OffsetN /= SU.asCharUnits();
372 if (ExtentN)
373 *ExtentN /= SU.asCharUnits();
374 } else {
375 // Fall back to reporting the offsets in bytes.
376 SU = SizeUnit::bytes();
377 }
378
380 llvm::raw_svector_ostream Out(Buf);
381 Out << "Assuming ";
382 if (!SU.isBytes()) {
383 Out << "index ";
384 if (OffsetN)
385 Out << "'" << OffsetN << "' ";
386 } else if (Res.mayOverflow()) {
387 Out << "byte offset ";
388 if (OffsetN)
389 Out << "'" << OffsetN << "' ";
390 } else {
391 Out << "offset ";
392 }
393
394 Out << "is";
395 if (ShouldReportNonNegative) {
396 Out << " non-negative";
397 }
398 if (Res.mayOverflow()) {
399 if (ShouldReportNonNegative)
400 Out << " and";
401 Out << " less than ";
402 if (ExtentN)
403 Out << *ExtentN << ", ";
404 Out << SU.asExtentDesc() << ' ' << RegName;
405 }
406 return std::string(Out.str());
407}
408
409void ArrayBoundChecker::handleAccessExpr(const Expr *E,
410 CheckerContext &C) const {
411 ASTContext &ACtx = C.getASTContext();
412 const ElementRegion *AccessedER =
413 dyn_cast_or_null<ElementRegion>(C.getSVal(E).getAsRegion());
414 if (!AccessedER)
415 return;
416
417 // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as
418 // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX)
419 // and incomplete analysis of these leads to false positives. As even
420 // accurate reports would be confusing for the users, just disable reports
421 // from these macros:
422 if (isFromCtypeMacro(E, ACtx))
423 return;
424
425 ProgramStateRef State = C.getState();
426 SValBuilder &SVB = C.getSValBuilder();
427
428 const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset =
429 computeOffset(State, SVB, AccessedER);
430
431 if (!RawOffset)
432 return;
433
434 auto [Reg, ByteOffset] = *RawOffset;
435
436 const MemSpaceRegion *Space = Reg->getMemorySpace(State);
437 auto Extent = getDynamicExtent(State, Reg, SVB).getAs<NonLoc>();
438
439 // A symbolic region in unknown space represents an unknown pointer that
440 // may point into the middle of an array, so we don't look for underflows.
441 // Both conditions are significant because we want to check underflows in
442 // symbolic regions on the heap (which may be introduced by checkers like
443 // MallocChecker that call SValBuilder::getConjuredHeapSymbolVal()) and
444 // non-symbolic regions (e.g. a field subregion of a symbolic region) in
445 // unknown space.
446
447 bounds::CheckFlags Flags = {
448 /*CheckUnderflow=*/!(isa<SymbolicRegion>(Reg) &&
450 /*OffsetObviouslyNonnegative=*/isOffsetObviouslyNonnegative(E, C)};
451
452 bounds::CheckResult Res = checkBounds(State, SVB, ByteOffset, Extent, Flags);
453
454 if (Res.isCorruptedState()) {
455 C.addSink();
456 return;
457 }
458
459 std::string RegName =
460 Reg->getDescriptiveName(/*UseQuotes=*/true, /*AllowFallback=*/true);
461
462 const NoteTag *T = nullptr;
463 if (Res.mayBeInvalid()) {
464 if (!Res.mayBeInBounds()) {
465 if (isa<ArraySubscriptExpr>(E) && isInAddressOf(E, ACtx) && Extent) {
466 // Recognize and accept the idiomatic `&array[size]` expression that
467 // forms the past-the-end pointer without actually dereferencing it.
468 auto [EqualsToThreshold, NotEqualToThreshold] =
469 bounds::compareValueToThreshold(State, SVB, ByteOffset, *Extent,
470 /*CheckEquality=*/true);
471 if (EqualsToThreshold && !NotEqualToThreshold) {
472 C.addTransition(EqualsToThreshold);
473 return;
474 }
475 }
476
477 SizeUnit SU = SizeUnit::forElementRegion(AccessedER, ACtx);
478 BugDescription Desc = describeInvalidAccess(Res, RegName, SU);
479 reportOOB(C, State, Desc, ByteOffset, Res.getExtentIfMayOverflow());
480 return;
481 }
482
483 // FIXME: Remove `Res.mayOverflow()` and provide diagnostics for the case
484 // when the tainted access operation cannot overflow but can underflow.
485 // (This is an NFC commit, so I cannot include this improvement.)
486 if (Res.mayOverflow() && isTainted(State, ByteOffset)) {
487 // Diagnostic detail: saying "tainted offset" is always correct, but
488 // the common case is that 'idx' is tainted in 'arr[idx]' and then it's
489 // nicer to say "tainted index".
490 StringRef OffsetName = "offset";
491 if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
492 if (isTainted(State, ASE->getIdx(), C.getStackFrame()))
493 OffsetName = "index";
494
495 BugDescription Desc =
496 describeTaintBug(RegName, OffsetName, Res.mayUnderflow());
497 reportOOB(C, State, Desc, ByteOffset, Extent, /*IsTaintBug=*/true);
498 return;
499 }
500
501 SizeUnit SU = SizeUnit::forExpr(E, C);
502 T = C.getNoteTag(
503 [Res, RegName, SU](PathSensitiveBugReport &BR) -> std::string {
504 return getAssumptionNote(Res, BR, RegName, SU);
505 });
506 }
507
508 C.addTransition(Res.getInBoundsState(), T);
509}
510
511void ArrayBoundChecker::markPartsInteresting(PathSensitiveBugReport &BR,
512 ProgramStateRef ErrorState,
513 NonLoc Val, bool MarkTaint) {
514 if (SymbolRef Sym = Val.getAsSymbol()) {
515 // If the offset is a symbolic value, iterate over its "parts" with
516 // `SymExpr::symbols()` and mark each of them as interesting.
517 // For example, if the offset is `x*4 + y` then we put interestingness onto
518 // the SymSymExpr `x*4 + y`, the SymIntExpr `x*4` and the two data symbols
519 // `x` and `y`.
520 for (SymbolRef PartSym : Sym->symbols())
521 BR.markInteresting(PartSym);
522 }
523
524 if (MarkTaint) {
525 // If the issue that we're reporting depends on the taintedness of the
526 // offset, then put interestingness onto symbols that could be the origin
527 // of the taint. Note that this may find symbols that did not appear in
528 // `Sym->symbols()` (because they're only loosely connected to `Val`).
529 for (SymbolRef Sym : getTaintedSymbols(ErrorState, Val))
530 BR.markInteresting(Sym);
531 }
532}
533
534void ArrayBoundChecker::reportOOB(CheckerContext &C, ProgramStateRef ErrorState,
535 BugDescription Desc, NonLoc Offset,
536 std::optional<NonLoc> Extent,
537 bool IsTaintBug /*=false*/) const {
538
539 ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState);
540 if (!ErrorNode)
541 return;
542
543 auto BR = std::make_unique<PathSensitiveBugReport>(
544 IsTaintBug ? TaintBT : BT, Desc.Short, Desc.Full, ErrorNode);
545
546 // FIXME: ideally we would just call trackExpressionValue() and that would
547 // "do the right thing": mark the relevant symbols as interesting, track the
548 // control dependencies and statements storing the relevant values and add
549 // helpful diagnostic pieces. However, right now trackExpressionValue() is
550 // a heap of unreliable heuristics, so it would cause several issues:
551 // - Interestingness is not applied consistently, e.g. if `array[x+10]`
552 // causes an overflow, then `x` is not marked as interesting.
553 // - We get irrelevant diagnostic pieces, e.g. in the code
554 // `int *p = (int*)malloc(2*sizeof(int)); p[3] = 0;`
555 // it places a "Storing uninitialized value" note on the `malloc` call
556 // (which is technically true, but irrelevant).
557 // If trackExpressionValue() becomes reliable, it should be applied instead
558 // of this custom markPartsInteresting().
559 markPartsInteresting(*BR, ErrorState, Offset, IsTaintBug);
560 if (Extent)
561 markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
562
563 C.emitReport(std::move(BR));
564}
565
566bool ArrayBoundChecker::isFromCtypeMacro(const Expr *E, ASTContext &ACtx) {
567 SourceLocation Loc = E->getBeginLoc();
568 if (!Loc.isMacroID())
569 return false;
570
571 StringRef MacroName = Lexer::getImmediateMacroName(
572 Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
573
574 if (MacroName.size() < 7 || MacroName[0] != 'i' || MacroName[1] != 's')
575 return false;
576
577 return ((MacroName == "isalnum") || (MacroName == "isalpha") ||
578 (MacroName == "isblank") || (MacroName == "isdigit") ||
579 (MacroName == "isgraph") || (MacroName == "islower") ||
580 (MacroName == "isnctrl") || (MacroName == "isprint") ||
581 (MacroName == "ispunct") || (MacroName == "isspace") ||
582 (MacroName == "isupper") || (MacroName == "isxdigit"));
583}
584
585bool ArrayBoundChecker::isOffsetObviouslyNonnegative(const Expr *E,
586 CheckerContext &C) {
587 const ArraySubscriptExpr *ASE = getAsCleanArraySubscriptExpr(E, C);
588 if (!ASE)
589 return false;
591}
592
593bool ArrayBoundChecker::isInAddressOf(const Stmt *S, ASTContext &ACtx) {
594 ParentMapContext &ParentCtx = ACtx.getParentMapContext();
595 do {
596 const DynTypedNodeList Parents = ParentCtx.getParents(*S);
597 if (Parents.empty())
598 return false;
599 S = Parents[0].get<Stmt>();
600 } while (isa_and_nonnull<ParenExpr, ImplicitCastExpr>(S));
601 const auto *UnaryOp = dyn_cast_or_null<UnaryOperator>(S);
602 return UnaryOp && UnaryOp->getOpcode() == UO_AddrOf;
603}
604
605void ento::registerArrayBoundChecker(CheckerManager &mgr) {
606 mgr.registerChecker<ArrayBoundChecker>();
607}
608
609bool ento::shouldRegisterArrayBoundChecker(const CheckerManager &mgr) {
610 return true;
611}
static StringRef bytes(const std::vector< T, Allocator > &v)
static std::optional< std::pair< const SubRegion *, NonLoc > > computeOffset(ProgramStateRef State, SValBuilder &SVB, const ElementRegion *CurRegion)
For a given CurRegion that can be represented as a symbolic expression Arr[Idx] (or perhaps Arr[Idx1]...
static bool isDeterminedByInterestingSymbol(SVal SV, PathSensitiveBugReport &BR)
Return true if information about the value of SV can put constraints on some symbol which is interest...
static std::string getAssumptionNote(bounds::CheckResult Res, PathSensitiveBugReport &BR, StringRef RegName, SizeUnit SU)
When the access was ambiguous (that is, mayBeInBounds() && mayBeInvalid()), returns the note "assumin...
static BugDescription describeInvalidAccess(bounds::CheckResult Res, StringRef RegName, SizeUnit SU)
static BugDescription describeTaintBug(StringRef RegName, StringRef OffsetName, bool AlsoMentionUnderflow)
static StringRef getAdjective(const bounds::CheckResult &R)
static std::optional< int64_t > getConcreteValue(NonLoc SV)
static StringRef getPreposition(const bounds::CheckResult &R)
SourceManager & getSourceManager()
Definition ASTContext.h:884
ParentMapContext & getParentMapContext()
Returns the dynamic AST node parent map context.
const LangOptions & getLangOpts() const
Definition ASTContext.h:980
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition Expr.h:2732
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
static StringRef getImmediateMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Retrieve the name of the immediate macro expansion.
Definition Lexer.cpp:1111
Expr * getBase() const
Definition Expr.h:3452
bool isArrow() const
Definition Expr.h:3559
DynTypedNodeList getParents(const NodeT &Node)
Returns the parents of the given node (within the traversal scope).
A (possibly-)qualified type.
Definition TypeBase.h:938
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:355
bool isUnsignedIntegerOrEnumerationType() const
Determines whether this is an integer type that is unsigned or an enumeration types whose underlying ...
Definition Type.cpp:2385
bool isIncompleteType(NamedDecl **Def=nullptr) const
Types are partitioned into 3 broad categories (C99 6.2.5p1): object types, function types,...
Definition Type.cpp:2557
Opcode getOpcode() const
Definition Expr.h:2291
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
ElementRegion is used to represent both array elements and casts.
Definition MemRegion.h:1237
QualType getElementType() const
Definition MemRegion.h:1261
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * StripCasts(bool StripBaseAndDerivedCasts=true) const
const RegionTy * getAs() const
Definition MemRegion.h:1426
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
bool isInteresting(SymbolRef sym) const
NonLoc makeArrayIndex(uint64_t idx)
ASTContext & getContext()
QualType getArrayIndexType() const
virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy)=0
Create a new value which represents a binary expression with two non- location operands.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:57
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition SVals.cpp:103
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:88
SubRegion - A region that subsets another larger region.
Definition MemRegion.h:480
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getSuperRegion() const
Definition MemRegion.h:493
llvm::iterator_range< symbol_iterator > symbols() const
Definition SymExpr.h:107
bool mayBeInBounds() const
When true, the checked offset may be in bounds.
bool mayBeInvalid() const
When true, the checked offset may be out of bounds.
bool mayUnderflow() const
When true, the checked offset may be negative.
NonLoc getOffset() const
Returns the offset of the accessed location from the beginning of the accessd region.
ProgramStateRef getInBoundsState() const
Returns the program state that should be used for continuing the analysis after this bounds check.
bool mayOverflow() const
When true, the checked offset may be >= the extent of the region.
bool isCorruptedState() const
When true, the bounds check noticed that the value of an unsigned expression is constrained to negati...
std::optional< NonLoc > getExtentIfMayOverflow() const
Returns the extent of the accessed region if it is relevant (because the offset may overflow it),...
Value representing integer constant.
Definition SVals.h:306
std::pair< ProgramStateRef, ProgramStateRef > compareValueToThreshold(ProgramStateRef State, SValBuilder &SVB, NonLoc Value, NonLoc Threshold, bool CheckEquality=false)
CheckResult checkBounds(ProgramStateRef State, SValBuilder &SVB, NonLoc Offset, std::optional< NonLoc > Extent, CheckFlags Flags)
Checks the validity of accessing a memory region with extent Extent at offset Offset.
bool isTainted(ProgramStateRef State, const Expr *E, const StackFrame *SF, TaintTagType Kind=TaintTagGeneric)
Check if the expression has a tainted value in the given state.
Definition Taint.cpp:147
std::vector< SymbolRef > getTaintedSymbols(ProgramStateRef State, const Expr *E, const StackFrame *SF, TaintTagType Kind=TaintTagGeneric)
Returns the tainted Symbols for a given expression and state.
Definition Taint.cpp:169
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB)
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
const FunctionProtoType * T
long int64_t