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
203static int64_t getElementSize(const ElementRegion *ER, SValBuilder &SVB) {
204 QualType ElemType = ER->getElementType();
205
206 assert(!ElemType->isIncompleteType() && "ElemType cannot be incomplete");
207
208 return SVB.getContext().getTypeSizeInChars(ElemType).getQuantity();
209}
210
211/// For a given \p CurRegion that can be represented as a symbolic expression
212/// Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block
213/// Arr and the distance of Location from the beginning of Arr (expressed in a
214/// NonLoc that specifies the number of CharUnits). Returns nullopt when these
215/// cannot be determined.
216static std::optional<std::pair<const SubRegion *, NonLoc>>
218 const ElementRegion *CurRegion) {
220 auto EvalBinOp = [&SVB, State, T](BinaryOperatorKind Op, NonLoc L, NonLoc R) {
221 // We will use this utility to add and multiply values.
222 return SVB.evalBinOpNN(State, Op, L, R, T).getAs<NonLoc>();
223 };
224
225 const SubRegion *OwnerRegion = nullptr;
226 std::optional<NonLoc> Offset = SVB.makeZeroArrayIndex();
227
228 while (CurRegion) {
229 const auto Index = CurRegion->getIndex().getAs<NonLoc>();
230 if (!Index)
231 return std::nullopt;
232
233 // Calculate Delta = Index * sizeof(ElemType).
234 NonLoc Size = SVB.makeArrayIndex(getElementSize(CurRegion, SVB));
235 auto Delta = EvalBinOp(BO_Mul, *Index, Size);
236 if (!Delta)
237 return std::nullopt;
238
239 // Perform Offset += Delta.
240 Offset = EvalBinOp(BO_Add, *Offset, *Delta);
241 if (!Offset)
242 return std::nullopt;
243
244 OwnerRegion = CurRegion->getSuperRegion()->getAs<SubRegion>();
245 // When this is just another ElementRegion layer, we need to continue the
246 // offset calculations:
247 CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion);
248 }
249
250 if (OwnerRegion)
251 return std::make_pair(OwnerRegion, *Offset);
252
253 return std::nullopt;
254}
255
256static std::optional<int64_t> getConcreteValue(NonLoc SV) {
257 if (auto ConcreteVal = SV.getAs<nonloc::ConcreteInt>()) {
258 return ConcreteVal->getValue()->tryExtValue();
259 }
260 return std::nullopt;
261}
262
263static std::optional<int64_t> getConcreteValue(std::optional<NonLoc> SV) {
264 return SV ? getConcreteValue(*SV) : std::nullopt;
265}
266
267static StringRef getAdjective(const bounds::CheckResult &R) {
268 return (R.mayUnderflow()
269 ? (R.mayOverflow() ? "a negative or overflowing" : "a negative")
270 : (R.mayOverflow() ? "an overflowing" : "a valid"));
271}
272
273static StringRef getPreposition(const bounds::CheckResult &R) {
274 return (R.mayUnderflow() ? (R.mayOverflow() ? "around" : "preceding")
275 : (R.mayOverflow() ? "after the end of" : "within"));
276}
277
279 StringRef RegName, SizeUnit SU) {
280 assert(Res.mayBeInvalid());
281
282 std::optional<int64_t> OffsetN = getConcreteValue(Res.getOffset());
283 std::optional<int64_t> ExtentN =
285
286 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
287 if (OffsetN)
288 *OffsetN /= SU.asCharUnits();
289 if (ExtentN)
290 *ExtentN /= SU.asCharUnits();
291 } else {
292 // Fall back to reporting the offsets in bytes.
293 SU = SizeUnit::bytes();
294 }
295
296 StringRef OffsetOrIndex = SU.isBytes() ? "byte offset" : "index";
297
299 llvm::raw_svector_ostream Out(Buf);
300 Out << "Access of ";
301 if (OffsetN && !ExtentN && !SU.isBytes()) {
302 // If the offset is reported as an index, then the report must mention the
303 // element type (because it is not always clear from the code). It's more
304 // natural to mention the element type later where the extent is described,
305 // but if the extent is unknown/irrelevant, then the element type can be
306 // inserted into the message at this point.
307 Out << SU.asElementName() << " in ";
308 }
309 Out << RegName << " at ";
310 if (OffsetN) {
311 if (Res.mayUnderflow() && !Res.mayOverflow())
312 Out << "negative ";
313 Out << OffsetOrIndex << " " << *OffsetN;
314 } else {
315 Out << getAdjective(Res) << " " << OffsetOrIndex;
316 }
317 if (ExtentN) {
318 Out << ", while it holds only ";
319 if (*ExtentN != 1)
320 Out << *ExtentN;
321 else
322 Out << "a single";
323
324 Out << ' ' << SU.asElementName();
325
326 if (*ExtentN != 1)
327 Out << "s";
328 }
329
330 return {formatv("Out of bound access to memory {0} {1}", getPreposition(Res),
331 RegName),
332 std::string(Buf)};
333}
334
335static BugDescription describeTaintBug(bounds::CheckResult Res,
336 StringRef RegName,
337 StringRef OffsetName) {
338 assert(Res.mayBeInvalid());
339 return {formatv("Potential out of bound access to {0} with tainted {1}",
340 RegName, OffsetName),
341 formatv("Access of {0} with a tainted {1} that may be{2}{3}{4}",
342 RegName, OffsetName, Res.mayUnderflow() ? " negative" : "",
343 (Res.mayUnderflow() && Res.mayOverflow()) ? " or" : "",
344 Res.mayOverflow() ? " too large" : "")};
345}
346
347/// When the access was ambiguous (that is, mayBeInBounds() && mayBeInvalid()),
348/// returns the note "assuming in bounds" note that is relevant for the bug
349/// report \p BR. When the access wasn't ambiguous or the the assumption is
350/// irrelevant for \p BR, this returns the empty string (which signifies "do
351/// not emit a note tag" when returned by a note tag callback).
354 StringRef RegName, SizeUnit SU) {
355 bool ShouldReportNonNegative = Res.mayUnderflow();
357 std::optional<NonLoc> E = Res.getExtentIfMayOverflow();
358 if (E && isDeterminedByInterestingSymbol(*E, BR)) {
359 // Even if the byte offset isn't interesting (e.g. it's a constant value),
360 // the assumption can still be interesting if it provides information
361 // about an interesting symbolic upper bound.
362 ShouldReportNonNegative = false;
363 } else {
364 // We don't have anything interesting, don't report the assumption.
365 return "";
366 }
367 }
368
369 std::optional<int64_t> OffsetN = getConcreteValue(Res.getOffset());
370 std::optional<int64_t> ExtentN =
372
373 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
374 if (OffsetN)
375 *OffsetN /= SU.asCharUnits();
376 if (ExtentN)
377 *ExtentN /= SU.asCharUnits();
378 } else {
379 // Fall back to reporting the offsets in bytes.
380 SU = SizeUnit::bytes();
381 }
382
384 llvm::raw_svector_ostream Out(Buf);
385 Out << "Assuming ";
386 if (!SU.isBytes()) {
387 Out << "index ";
388 if (OffsetN)
389 Out << "'" << OffsetN << "' ";
390 } else if (Res.mayOverflow()) {
391 Out << "byte offset ";
392 if (OffsetN)
393 Out << "'" << OffsetN << "' ";
394 } else {
395 Out << "offset ";
396 }
397
398 Out << "is";
399 if (ShouldReportNonNegative) {
400 Out << " non-negative";
401 }
402 if (Res.mayOverflow()) {
403 if (ShouldReportNonNegative)
404 Out << " and";
405 Out << " less than ";
406 if (ExtentN)
407 Out << *ExtentN << ", ";
408 Out << SU.asExtentDesc() << ' ' << RegName;
409 }
410 return std::string(Out.str());
411}
412
413void ArrayBoundChecker::handleAccessExpr(const Expr *E,
414 CheckerContext &C) const {
415 ASTContext &ACtx = C.getASTContext();
416 const ElementRegion *AccessedER =
417 dyn_cast_or_null<ElementRegion>(C.getSVal(E).getAsRegion());
418 if (!AccessedER)
419 return;
420
421 // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as
422 // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX)
423 // and incomplete analysis of these leads to false positives. As even
424 // accurate reports would be confusing for the users, just disable reports
425 // from these macros:
426 if (isFromCtypeMacro(E, ACtx))
427 return;
428
429 ProgramStateRef State = C.getState();
430 SValBuilder &SVB = C.getSValBuilder();
431
432 const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset =
433 computeOffset(State, SVB, AccessedER);
434
435 if (!RawOffset)
436 return;
437
438 auto [Reg, ByteOffset] = *RawOffset;
439
440 const MemSpaceRegion *Space = Reg->getMemorySpace(State);
441 auto Extent = getDynamicExtent(State, Reg, SVB).getAs<NonLoc>();
442
443 // A symbolic region in unknown space represents an unknown pointer that
444 // may point into the middle of an array, so we don't look for underflows.
445 // Both conditions are significant because we want to check underflows in
446 // symbolic regions on the heap (which may be introduced by checkers like
447 // MallocChecker that call SValBuilder::getConjuredHeapSymbolVal()) and
448 // non-symbolic regions (e.g. a field subregion of a symbolic region) in
449 // unknown space.
450
451 bounds::CheckFlags Flags = {
452 /*CheckUnderflow=*/!(isa<SymbolicRegion>(Reg) &&
454 /*OffsetObviouslyNonnegative=*/isOffsetObviouslyNonnegative(E, C),
455 /*AlsoAcceptEquality=*/(getElementSize(AccessedER, SVB) == 0)};
456
457 bounds::CheckResult Res = checkBounds(State, SVB, ByteOffset, Extent, Flags);
458
459 if (Res.isCorruptedState()) {
460 C.addSink();
461 return;
462 }
463
464 std::string RegName =
465 Reg->getDescriptiveName(/*UseQuotes=*/true, /*AllowFallback=*/true);
466
467 const NoteTag *T = nullptr;
468 if (Res.mayBeInvalid()) {
469 if (!Res.mayBeInBounds()) {
470 if (isa<ArraySubscriptExpr>(E) && isInAddressOf(E, ACtx) && Extent) {
471 // Recognize and accept the idiomatic `&array[size]` expression that
472 // forms the past-the-end pointer without actually dereferencing it.
473 auto [EqualsToThreshold, NotEqualToThreshold] =
474 bounds::compareValueToThreshold(State, SVB, ByteOffset, *Extent,
475 bounds::Comparison::EQ);
476 if (EqualsToThreshold && !NotEqualToThreshold) {
477 C.addTransition(EqualsToThreshold);
478 return;
479 }
480 }
481
482 SizeUnit SU = SizeUnit::forElementRegion(AccessedER, ACtx);
483 BugDescription Desc = describeInvalidAccess(Res, RegName, SU);
484 reportOOB(C, State, Desc, ByteOffset, Res.getExtentIfMayOverflow());
485 return;
486 }
487
488 if (isTainted(State, ByteOffset)) {
489 // Diagnostic detail: saying "tainted offset" is always correct, but
490 // the common case is that 'idx' is tainted in 'arr[idx]' and then it's
491 // nicer to say "tainted index".
492 StringRef OffsetName = "offset";
493 if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
494 if (isTainted(State, ASE->getIdx(), C.getStackFrame()))
495 OffsetName = "index";
496
497 BugDescription Desc = describeTaintBug(Res, RegName, OffsetName);
498 reportOOB(C, State, Desc, ByteOffset, Res.getExtentIfMayOverflow(),
499 /*IsTaintBug=*/true);
500 return;
501 }
502
503 SizeUnit SU = SizeUnit::forExpr(E, C);
504 T = C.getNoteTag(
505 [Res, RegName, SU](PathSensitiveBugReport &BR) -> std::string {
506 return getAssumptionNote(Res, BR, RegName, SU);
507 });
508 }
509
510 C.addTransition(Res.getInBoundsState(), T);
511}
512
513void ArrayBoundChecker::markPartsInteresting(PathSensitiveBugReport &BR,
514 ProgramStateRef ErrorState,
515 NonLoc Val, bool MarkTaint) {
516 if (SymbolRef Sym = Val.getAsSymbol()) {
517 // If the offset is a symbolic value, iterate over its "parts" with
518 // `SymExpr::symbols()` and mark each of them as interesting.
519 // For example, if the offset is `x*4 + y` then we put interestingness onto
520 // the SymSymExpr `x*4 + y`, the SymIntExpr `x*4` and the two data symbols
521 // `x` and `y`.
522 for (SymbolRef PartSym : Sym->symbols())
523 BR.markInteresting(PartSym);
524 }
525
526 if (MarkTaint) {
527 // If the issue that we're reporting depends on the taintedness of the
528 // offset, then put interestingness onto symbols that could be the origin
529 // of the taint. Note that this may find symbols that did not appear in
530 // `Sym->symbols()` (because they're only loosely connected to `Val`).
531 for (SymbolRef Sym : getTaintedSymbols(ErrorState, Val))
532 BR.markInteresting(Sym);
533 }
534}
535
536void ArrayBoundChecker::reportOOB(CheckerContext &C, ProgramStateRef ErrorState,
537 BugDescription Desc, NonLoc Offset,
538 std::optional<NonLoc> Extent,
539 bool IsTaintBug /*=false*/) const {
540
541 ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState);
542 if (!ErrorNode)
543 return;
544
545 auto BR = std::make_unique<PathSensitiveBugReport>(
546 IsTaintBug ? TaintBT : BT, Desc.Short, Desc.Full, ErrorNode);
547
548 // FIXME: ideally we would just call trackExpressionValue() and that would
549 // "do the right thing": mark the relevant symbols as interesting, track the
550 // control dependencies and statements storing the relevant values and add
551 // helpful diagnostic pieces. However, right now trackExpressionValue() is
552 // a heap of unreliable heuristics, so it would cause several issues:
553 // - Interestingness is not applied consistently, e.g. if `array[x+10]`
554 // causes an overflow, then `x` is not marked as interesting.
555 // - We get irrelevant diagnostic pieces, e.g. in the code
556 // `int *p = (int*)malloc(2*sizeof(int)); p[3] = 0;`
557 // it places a "Storing uninitialized value" note on the `malloc` call
558 // (which is technically true, but irrelevant).
559 // If trackExpressionValue() becomes reliable, it should be applied instead
560 // of this custom markPartsInteresting().
561 markPartsInteresting(*BR, ErrorState, Offset, IsTaintBug);
562 if (Extent)
563 markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
564
565 C.emitReport(std::move(BR));
566}
567
568bool ArrayBoundChecker::isFromCtypeMacro(const Expr *E, ASTContext &ACtx) {
569 SourceLocation Loc = E->getBeginLoc();
570 if (!Loc.isMacroID())
571 return false;
572
573 StringRef MacroName = Lexer::getImmediateMacroName(
574 Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
575
576 if (MacroName.size() < 7 || MacroName[0] != 'i' || MacroName[1] != 's')
577 return false;
578
579 return ((MacroName == "isalnum") || (MacroName == "isalpha") ||
580 (MacroName == "isblank") || (MacroName == "isdigit") ||
581 (MacroName == "isgraph") || (MacroName == "islower") ||
582 (MacroName == "isnctrl") || (MacroName == "isprint") ||
583 (MacroName == "ispunct") || (MacroName == "isspace") ||
584 (MacroName == "isupper") || (MacroName == "isxdigit"));
585}
586
587bool ArrayBoundChecker::isOffsetObviouslyNonnegative(const Expr *E,
588 CheckerContext &C) {
589 const ArraySubscriptExpr *ASE = getAsCleanArraySubscriptExpr(E, C);
590 if (!ASE)
591 return false;
593}
594
595bool ArrayBoundChecker::isInAddressOf(const Stmt *S, ASTContext &ACtx) {
596 ParentMapContext &ParentCtx = ACtx.getParentMapContext();
597 do {
598 const DynTypedNodeList Parents = ParentCtx.getParents(*S);
599 if (Parents.empty())
600 return false;
601 S = Parents[0].get<Stmt>();
602 } while (isa_and_nonnull<ParenExpr, ImplicitCastExpr>(S));
603 const auto *UnaryOp = dyn_cast_or_null<UnaryOperator>(S);
604 return UnaryOp && UnaryOp->getOpcode() == UO_AddrOf;
605}
606
607void ento::registerArrayBoundChecker(CheckerManager &mgr) {
608 mgr.registerChecker<ArrayBoundChecker>();
609}
610
611bool ento::shouldRegisterArrayBoundChecker(const CheckerManager &mgr) {
612 return true;
613}
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 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:907
ParentMapContext & getParentMapContext()
Returns the dynamic AST node parent map context.
const LangOptions & getLangOpts() const
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:2765
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
This represents one expression.
Definition Expr.h:113
QualType getType() const
Definition Expr.h:145
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:3485
bool isArrow() const
Definition Expr.h:3592
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:2387
bool isIncompleteType(NamedDecl **Def=nullptr) const
Types are partitioned into 3 broad categories (C99 6.2.5p1): object types, function types,...
Definition Type.cpp:2559
Opcode getOpcode() const
Definition Expr.h:2324
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
long int64_t