27#include "llvm/ADT/APSInt.h"
28#include "llvm/Support/FormatVariadic.h"
29#include "llvm/Support/raw_ostream.h"
45 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
49 const MemRegion *SubscriptBaseReg =
C.getSVal(ASE->getBase()).getAsRegion();
50 if (!SubscriptBaseReg)
66 SizeUnit() : AsType(), AsCharUnits(1) {}
69 SizeUnit(QualType
T,
const ASTContext &ACtx)
70 : AsType(
T), AsCharUnits(ACtx.getTypeSizeInChars(
T).getQuantity()) {
74 static SizeUnit
bytes() {
return SizeUnit(); }
76 bool isBytes()
const {
return AsType.isNull(); }
80 static SizeUnit forElementRegion(
const ElementRegion *ER,
81 const ASTContext &ACtx) {
92 static SizeUnit forExpr(
const Expr *E,
const CheckerContext &
C) {
93 const auto *ASE = getAsCleanArraySubscriptExpr(E,
C);
97 return SizeUnit(ASE->getType(),
C.getASTContext());
100 int64_t asCharUnits()
const {
return AsCharUnits; }
102 bool canExpress(std::optional<int64_t> Val)
const {
103 return asCharUnits() && (!Val || !(*Val % asCharUnits()));
106 std::string asExtentDesc()
const {
108 return "the extent of";
109 return formatv(
"the number of '{0}' elements in", AsType.getAsString());
112 std::string asElementName()
const {
115 return formatv(
"'{0}' element", AsType.getAsString());
121struct BugDescription {
133class ArrayBoundChecker :
public Checker<check::PostStmt<ArraySubscriptExpr>,
134 check::PostStmt<UnaryOperator>,
135 check::PostStmt<MemberExpr>> {
136 BugType BT{
this,
"Out-of-bound access"};
139 void handleAccessExpr(
const Expr *E, CheckerContext &
C)
const;
142 BugDescription Desc, NonLoc Offset,
143 std::optional<NonLoc> Extent,
bool IsTaintBug =
false)
const;
145 static void markPartsInteresting(PathSensitiveBugReport &BR,
149 static bool isFromCtypeMacro(
const Expr *E, ASTContext &AC);
151 static bool isOffsetObviouslyNonnegative(
const Expr *E, CheckerContext &
C);
153 static bool isInAddressOf(
const Stmt *S, ASTContext &AC);
156 void checkPostStmt(
const ArraySubscriptExpr *E, CheckerContext &
C)
const {
157 handleAccessExpr(E,
C);
159 void checkPostStmt(
const UnaryOperator *E, CheckerContext &
C)
const {
161 handleAccessExpr(E,
C);
163 void checkPostStmt(
const MemberExpr *E, CheckerContext &
C)
const {
219static std::optional<std::pair<const SubRegion *, NonLoc>>
243 auto Delta = EvalBinOp(BO_Mul, *Index, SVB.
makeArrayIndex(*ElemSize));
248 Offset = EvalBinOp(BO_Add, *Offset, *Delta);
255 CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion);
259 return std::make_pair(OwnerRegion, *Offset);
266 return ConcreteVal->getValue()->tryExtValue();
276 return (R.mayUnderflow()
277 ? (R.mayOverflow() ?
"a negative or overflowing" :
"a negative")
278 : (R.mayOverflow() ?
"an overflowing" :
"a valid"));
282 return (R.mayUnderflow() ? (R.mayOverflow() ?
"around" :
"preceding")
283 : (R.mayOverflow() ?
"after the end of" :
"within"));
287 StringRef RegName, SizeUnit SU) {
291 std::optional<int64_t> ExtentN =
294 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
296 *OffsetN /= SU.asCharUnits();
298 *ExtentN /= SU.asCharUnits();
301 SU = SizeUnit::bytes();
304 StringRef OffsetOrIndex = SU.isBytes() ?
"byte offset" :
"index";
307 llvm::raw_svector_ostream Out(Buf);
309 if (OffsetN && !ExtentN && !SU.isBytes()) {
315 Out << SU.asElementName() <<
" in ";
317 Out << RegName <<
" at ";
321 Out << OffsetOrIndex <<
" " << *OffsetN;
326 Out <<
", while it holds only ";
332 Out <<
' ' << SU.asElementName();
338 return {formatv(
"Out of bound access to memory {0} {1}",
getPreposition(Res),
345 StringRef OffsetName) {
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" :
"",
362 StringRef RegName, SizeUnit SU) {
370 ShouldReportNonNegative =
false;
378 std::optional<int64_t> ExtentN =
381 if (SU.canExpress(OffsetN) && SU.canExpress(ExtentN)) {
383 *OffsetN /= SU.asCharUnits();
385 *ExtentN /= SU.asCharUnits();
388 SU = SizeUnit::bytes();
392 llvm::raw_svector_ostream Out(Buf);
397 Out <<
"'" << OffsetN <<
"' ";
399 Out <<
"byte offset ";
401 Out <<
"'" << OffsetN <<
"' ";
407 if (ShouldReportNonNegative) {
408 Out <<
" non-negative";
411 if (ShouldReportNonNegative)
413 Out <<
" less than ";
415 Out << *ExtentN <<
", ";
416 Out << SU.asExtentDesc() <<
' ' << RegName;
418 return std::string(Out.str());
421void ArrayBoundChecker::handleAccessExpr(
const Expr *E,
423 ASTContext &ACtx =
C.getASTContext();
424 const ElementRegion *AccessedER =
425 dyn_cast_or_null<ElementRegion>(
C.getSVal(E).getAsRegion());
434 if (isFromCtypeMacro(E, ACtx))
438 SValBuilder &SVB =
C.getSValBuilder();
440 const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset =
446 auto [Reg, ByteOffset] = *RawOffset;
448 const MemSpaceRegion *Space = Reg->getMemorySpace(State);
459 bounds::CheckFlags Flags = {
462 isOffsetObviouslyNonnegative(E,
C),
465 bounds::CheckResult Res =
checkBounds(State, SVB, ByteOffset, Extent, Flags);
472 std::string RegName =
473 Reg->getDescriptiveName(
true,
true);
475 const NoteTag *
T =
nullptr;
481 auto [EqualsToThreshold, NotEqualToThreshold] =
483 bounds::Comparison::EQ);
484 if (EqualsToThreshold && !NotEqualToThreshold) {
485 C.addTransition(EqualsToThreshold);
490 SizeUnit SU = SizeUnit::forElementRegion(AccessedER, ACtx);
500 StringRef OffsetName =
"offset";
501 if (
const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
502 if (
isTainted(State, ASE->getIdx(),
C.getStackFrame()))
503 OffsetName =
"index";
511 SizeUnit SU = SizeUnit::forExpr(E,
C);
513 [Res, RegName, SU](PathSensitiveBugReport &BR) -> std::string {
521void ArrayBoundChecker::markPartsInteresting(PathSensitiveBugReport &BR,
523 NonLoc Val,
bool MarkTaint) {
544void ArrayBoundChecker::reportOOB(CheckerContext &
C,
ProgramStateRef ErrorState,
545 BugDescription Desc, NonLoc Offset,
546 std::optional<NonLoc> Extent,
547 bool IsTaintBug )
const {
549 ExplodedNode *ErrorNode =
C.addSink(ErrorState);
553 auto BR = std::make_unique<PathSensitiveBugReport>(
554 IsTaintBug ? TaintBT : BT, Desc.Short, Desc.Full, ErrorNode);
569 markPartsInteresting(*BR, ErrorState, Offset, IsTaintBug);
571 markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
573 C.emitReport(std::move(BR));
576bool ArrayBoundChecker::isFromCtypeMacro(
const Expr *E, ASTContext &ACtx) {
584 if (MacroName.size() < 7 || MacroName[0] !=
'i' || MacroName[1] !=
's')
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"));
595bool ArrayBoundChecker::isOffsetObviouslyNonnegative(
const Expr *E,
597 const ArraySubscriptExpr *ASE = getAsCleanArraySubscriptExpr(E,
C);
603bool ArrayBoundChecker::isInAddressOf(
const Stmt *S, ASTContext &ACtx) {
606 const DynTypedNodeList Parents = ParentCtx.
getParents(*S);
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;
615void ento::registerArrayBoundChecker(CheckerManager &mgr) {
619bool 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::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()
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.
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
QuantityType getQuantity() const
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
__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