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