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 {
208static std::optional<std::pair<const SubRegion *, NonLoc>>
236 auto Delta = EvalBinOp(BO_Mul, *Index, Size);
241 Offset = EvalBinOp(BO_Add, *Offset, *Delta);
248 CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion);
252 return std::make_pair(OwnerRegion, *Offset);
259 return ConcreteVal->getValue()->tryExtValue();
269 return (R.mayUnderflow()
270 ? (R.mayOverflow() ?
"a negative or overflowing" :
"a negative")
271 : (R.mayOverflow() ?
"an overflowing" :
"a valid"));
275 return (R.mayUnderflow() ? (R.mayOverflow() ?
"around" :
"preceding")
276 : (R.mayOverflow() ?
"after the end of" :
"within"));
280 StringRef RegName, SizeUnit SU) {
282 std::optional<int64_t> ExtentN =
285 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
287 *OffsetN /= SU.asCharUnits();
289 *ExtentN /= SU.asCharUnits();
292 SU = SizeUnit::bytes();
295 StringRef OffsetOrIndex = SU.isBytes() ?
"byte offset" :
"index";
298 llvm::raw_svector_ostream Out(Buf);
300 if (OffsetN && !ExtentN && !SU.isBytes()) {
306 Out << SU.asElementName() <<
" in ";
308 Out << RegName <<
" at ";
312 Out << OffsetOrIndex <<
" " << *OffsetN;
317 Out <<
", while it holds only ";
323 Out <<
' ' << SU.asElementName();
329 return {formatv(
"Out of bound access to memory {0} {1}",
getPreposition(Res),
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",
340 AlsoMentionUnderflow ?
"negative or " :
"")};
350 StringRef RegName, SizeUnit SU) {
358 ShouldReportNonNegative =
false;
366 std::optional<int64_t> ExtentN =
369 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
371 *OffsetN /= SU.asCharUnits();
373 *ExtentN /= SU.asCharUnits();
376 SU = SizeUnit::bytes();
380 llvm::raw_svector_ostream Out(Buf);
385 Out <<
"'" << OffsetN <<
"' ";
387 Out <<
"byte offset ";
389 Out <<
"'" << OffsetN <<
"' ";
395 if (ShouldReportNonNegative) {
396 Out <<
" non-negative";
399 if (ShouldReportNonNegative)
401 Out <<
" less than ";
403 Out << *ExtentN <<
", ";
404 Out << SU.asExtentDesc() <<
' ' << RegName;
406 return std::string(Out.str());
409void ArrayBoundChecker::handleAccessExpr(
const Expr *E,
411 ASTContext &ACtx =
C.getASTContext();
412 const ElementRegion *AccessedER =
413 dyn_cast_or_null<ElementRegion>(
C.getSVal(E).getAsRegion());
422 if (isFromCtypeMacro(E, ACtx))
426 SValBuilder &SVB =
C.getSValBuilder();
428 const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset =
434 auto [Reg, ByteOffset] = *RawOffset;
436 const MemSpaceRegion *Space = Reg->getMemorySpace(State);
447 bounds::CheckFlags Flags = {
450 isOffsetObviouslyNonnegative(E,
C)};
452 bounds::CheckResult Res =
checkBounds(State, SVB, ByteOffset, Extent, Flags);
459 std::string RegName =
460 Reg->getDescriptiveName(
true,
true);
462 const NoteTag *
T =
nullptr;
468 auto [EqualsToThreshold, NotEqualToThreshold] =
471 if (EqualsToThreshold && !NotEqualToThreshold) {
472 C.addTransition(EqualsToThreshold);
477 SizeUnit SU = SizeUnit::forElementRegion(AccessedER, ACtx);
490 StringRef OffsetName =
"offset";
491 if (
const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
492 if (
isTainted(State, ASE->getIdx(),
C.getStackFrame()))
493 OffsetName =
"index";
495 BugDescription Desc =
497 reportOOB(
C, State, Desc, ByteOffset, Extent,
true);
501 SizeUnit SU = SizeUnit::forExpr(E,
C);
503 [Res, RegName, SU](PathSensitiveBugReport &BR) -> std::string {
511void ArrayBoundChecker::markPartsInteresting(PathSensitiveBugReport &BR,
513 NonLoc Val,
bool MarkTaint) {
534void ArrayBoundChecker::reportOOB(CheckerContext &
C,
ProgramStateRef ErrorState,
535 BugDescription Desc, NonLoc Offset,
536 std::optional<NonLoc> Extent,
537 bool IsTaintBug )
const {
539 ExplodedNode *ErrorNode =
C.generateErrorNode(ErrorState);
543 auto BR = std::make_unique<PathSensitiveBugReport>(
544 IsTaintBug ? TaintBT : BT, Desc.Short, Desc.Full, ErrorNode);
559 markPartsInteresting(*BR, ErrorState, Offset, IsTaintBug);
561 markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
563 C.emitReport(std::move(BR));
566bool ArrayBoundChecker::isFromCtypeMacro(
const Expr *E, ASTContext &ACtx) {
574 if (MacroName.size() < 7 || MacroName[0] !=
'i' || MacroName[1] !=
's')
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"));
585bool ArrayBoundChecker::isOffsetObviouslyNonnegative(
const Expr *E,
587 const ArraySubscriptExpr *ASE = getAsCleanArraySubscriptExpr(E,
C);
593bool ArrayBoundChecker::isInAddressOf(
const Stmt *S, ASTContext &ACtx) {
596 const DynTypedNodeList Parents = ParentCtx.
getParents(*S);
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;
605void ento::registerArrayBoundChecker(CheckerManager &mgr) {
609bool 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 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()
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, 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.
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