26#include "llvm/ADT/APSInt.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/raw_ostream.h"
44 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
48 const MemRegion *SubscriptBaseReg =
C.getSVal(ASE->getBase()).getAsRegion();
49 if (!SubscriptBaseReg)
65 SizeUnit() : AsType(), AsCharUnits(1) {}
68 SizeUnit(QualType
T,
const ASTContext &ACtx)
69 : AsType(
T), AsCharUnits(ACtx.getTypeSizeInChars(
T).getQuantity()) {
73 static SizeUnit
bytes() {
return SizeUnit(); }
75 bool isBytes()
const {
return AsType.isNull(); }
79 static SizeUnit forElementRegion(
const ElementRegion *ER,
80 const ASTContext &ACtx) {
91 static SizeUnit forExpr(
const Expr *E,
const CheckerContext &
C) {
92 const auto *ASE = getAsCleanArraySubscriptExpr(E,
C);
96 return SizeUnit(ASE->getType(),
C.getASTContext());
99 int64_t asCharUnits()
const {
return AsCharUnits; }
101 bool canExpress(std::optional<int64_t> Val)
const {
102 return asCharUnits() && (!Val || !(*Val % asCharUnits()));
105 std::string asExtentDesc()
const {
107 return "the extent of";
108 return formatv(
"the number of '{0}' elements in", AsType.getAsString());
111 std::string asElementName()
const {
114 return formatv(
"'{0}' element", AsType.getAsString());
120struct BugDescription {
132class ArrayBoundChecker :
public Checker<check::PostStmt<ArraySubscriptExpr>,
133 check::PostStmt<UnaryOperator>,
134 check::PostStmt<MemberExpr>> {
135 BugType BT{
this,
"Out-of-bound access"};
138 void handleAccessExpr(
const Expr *E, CheckerContext &
C)
const;
141 BugDescription Desc, NonLoc Offset,
142 std::optional<NonLoc> Extent,
bool IsTaintBug =
false)
const;
144 static void markPartsInteresting(PathSensitiveBugReport &BR,
148 static bool isFromCtypeMacro(
const Expr *E, ASTContext &AC);
150 static bool isOffsetObviouslyNonnegative(
const Expr *E, CheckerContext &
C);
152 static bool isInAddressOf(
const Stmt *S, ASTContext &AC);
155 void checkPostStmt(
const ArraySubscriptExpr *E, CheckerContext &
C)
const {
156 handleAccessExpr(E,
C);
158 void checkPostStmt(
const UnaryOperator *E, CheckerContext &
C)
const {
160 handleAccessExpr(E,
C);
162 void checkPostStmt(
const MemberExpr *E, CheckerContext &
C)
const {
216static std::optional<std::pair<const SubRegion *, NonLoc>>
235 auto Delta = EvalBinOp(BO_Mul, *Index, Size);
240 Offset = EvalBinOp(BO_Add, *Offset, *Delta);
247 CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion);
251 return std::make_pair(OwnerRegion, *Offset);
258 return ConcreteVal->getValue()->tryExtValue();
268 return (R.mayUnderflow()
269 ? (R.mayOverflow() ?
"a negative or overflowing" :
"a negative")
270 : (R.mayOverflow() ?
"an overflowing" :
"a valid"));
274 return (R.mayUnderflow() ? (R.mayOverflow() ?
"around" :
"preceding")
275 : (R.mayOverflow() ?
"after the end of" :
"within"));
279 StringRef RegName, SizeUnit SU) {
283 std::optional<int64_t> ExtentN =
286 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
288 *OffsetN /= SU.asCharUnits();
290 *ExtentN /= SU.asCharUnits();
293 SU = SizeUnit::bytes();
296 StringRef OffsetOrIndex = SU.isBytes() ?
"byte offset" :
"index";
299 llvm::raw_svector_ostream Out(Buf);
301 if (OffsetN && !ExtentN && !SU.isBytes()) {
307 Out << SU.asElementName() <<
" in ";
309 Out << RegName <<
" at ";
313 Out << OffsetOrIndex <<
" " << *OffsetN;
318 Out <<
", while it holds only ";
324 Out <<
' ' << SU.asElementName();
330 return {formatv(
"Out of bound access to memory {0} {1}",
getPreposition(Res),
337 StringRef OffsetName) {
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" :
"",
354 StringRef RegName, SizeUnit SU) {
362 ShouldReportNonNegative =
false;
370 std::optional<int64_t> ExtentN =
373 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
375 *OffsetN /= SU.asCharUnits();
377 *ExtentN /= SU.asCharUnits();
380 SU = SizeUnit::bytes();
384 llvm::raw_svector_ostream Out(Buf);
389 Out <<
"'" << OffsetN <<
"' ";
391 Out <<
"byte offset ";
393 Out <<
"'" << OffsetN <<
"' ";
399 if (ShouldReportNonNegative) {
400 Out <<
" non-negative";
403 if (ShouldReportNonNegative)
405 Out <<
" less than ";
407 Out << *ExtentN <<
", ";
408 Out << SU.asExtentDesc() <<
' ' << RegName;
410 return std::string(Out.str());
413void ArrayBoundChecker::handleAccessExpr(
const Expr *E,
415 ASTContext &ACtx =
C.getASTContext();
416 const ElementRegion *AccessedER =
417 dyn_cast_or_null<ElementRegion>(
C.getSVal(E).getAsRegion());
426 if (isFromCtypeMacro(E, ACtx))
430 SValBuilder &SVB =
C.getSValBuilder();
432 const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset =
438 auto [Reg, ByteOffset] = *RawOffset;
440 const MemSpaceRegion *Space = Reg->getMemorySpace(State);
451 bounds::CheckFlags Flags = {
454 isOffsetObviouslyNonnegative(E,
C),
457 bounds::CheckResult Res =
checkBounds(State, SVB, ByteOffset, Extent, Flags);
464 std::string RegName =
465 Reg->getDescriptiveName(
true,
true);
467 const NoteTag *
T =
nullptr;
473 auto [EqualsToThreshold, NotEqualToThreshold] =
475 bounds::Comparison::EQ);
476 if (EqualsToThreshold && !NotEqualToThreshold) {
477 C.addTransition(EqualsToThreshold);
482 SizeUnit SU = SizeUnit::forElementRegion(AccessedER, ACtx);
492 StringRef OffsetName =
"offset";
493 if (
const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
494 if (
isTainted(State, ASE->getIdx(),
C.getStackFrame()))
495 OffsetName =
"index";
503 SizeUnit SU = SizeUnit::forExpr(E,
C);
505 [Res, RegName, SU](PathSensitiveBugReport &BR) -> std::string {
513void ArrayBoundChecker::markPartsInteresting(PathSensitiveBugReport &BR,
515 NonLoc Val,
bool MarkTaint) {
536void ArrayBoundChecker::reportOOB(CheckerContext &
C,
ProgramStateRef ErrorState,
537 BugDescription Desc, NonLoc Offset,
538 std::optional<NonLoc> Extent,
539 bool IsTaintBug )
const {
541 ExplodedNode *ErrorNode =
C.generateErrorNode(ErrorState);
545 auto BR = std::make_unique<PathSensitiveBugReport>(
546 IsTaintBug ? TaintBT : BT, Desc.Short, Desc.Full, ErrorNode);
561 markPartsInteresting(*BR, ErrorState, Offset, IsTaintBug);
563 markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
565 C.emitReport(std::move(BR));
568bool ArrayBoundChecker::isFromCtypeMacro(
const Expr *E, ASTContext &ACtx) {
576 if (MacroName.size() < 7 || MacroName[0] !=
'i' || MacroName[1] !=
's')
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"));
587bool ArrayBoundChecker::isOffsetObviouslyNonnegative(
const Expr *E,
589 const ArraySubscriptExpr *ASE = getAsCleanArraySubscriptExpr(E,
C);
595bool ArrayBoundChecker::isInAddressOf(
const Stmt *S, ASTContext &ACtx) {
598 const DynTypedNodeList Parents = ParentCtx.
getParents(*S);
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;
607void ento::registerArrayBoundChecker(CheckerManager &mgr) {
611bool ento::shouldRegisterArrayBoundChecker(
const CheckerManager &mgr) {
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()
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.
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
This represents one expression.
static StringRef getImmediateMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Retrieve the name of the immediate macro expansion.
DynTypedNodeList getParents(const NodeT &Node)
Returns the parents of the given node (within the traversal scope).
A (possibly-)qualified type.
SourceLocation getBeginLoc() const LLVM_READONLY
bool isUnsignedIntegerOrEnumerationType() const
Determines whether this is an integer type that is unsigned or an enumeration types whose underlying ...
bool isIncompleteType(NamedDecl **Def=nullptr) const
Types are partitioned into 3 broad categories (C99 6.2.5p1): object types, function types,...
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.
ElementRegion is used to represent both array elements and casts.
QualType getElementType() const
MemRegion - The root abstract class for all memory regions.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * StripCasts(bool StripBaseAndDerivedCasts=true) const
const RegionTy * getAs() const
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.
NonLoc makeZeroArrayIndex()
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
SubRegion - A region that subsets another larger region.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getSuperRegion() const
llvm::iterator_range< symbol_iterator > symbols() const
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.
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.
const char *const TaintedData
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.
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.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB)
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
const FunctionProtoType * T