10b57cec5SDimitry Andric //== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines ArrayBoundCheckerV2, which is a path-sensitive check 100b57cec5SDimitry Andric // which looks for an out-of-bound array element access. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "clang/AST/CharUnits.h" 155ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 1681ad6265SDimitry Andric #include "clang/StaticAnalyzer/Checkers/Taint.h" 170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" 210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" 230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 240b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 250b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 26bdd1243dSDimitry Andric #include <optional> 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric using namespace clang; 290b57cec5SDimitry Andric using namespace ento; 300b57cec5SDimitry Andric using namespace taint; 310b57cec5SDimitry Andric 320b57cec5SDimitry Andric namespace { 330b57cec5SDimitry Andric class ArrayBoundCheckerV2 : 340b57cec5SDimitry Andric public Checker<check::Location> { 350b57cec5SDimitry Andric mutable std::unique_ptr<BuiltinBug> BT; 36*06c3fb27SDimitry Andric mutable std::unique_ptr<BugType> TaintBT; 370b57cec5SDimitry Andric 38*06c3fb27SDimitry Andric enum OOB_Kind { OOB_Precedes, OOB_Excedes }; 390b57cec5SDimitry Andric 40*06c3fb27SDimitry Andric void reportOOB(CheckerContext &C, ProgramStateRef errorState, 41*06c3fb27SDimitry Andric OOB_Kind kind) const; 42*06c3fb27SDimitry Andric void reportTaintOOB(CheckerContext &C, ProgramStateRef errorState, 43*06c3fb27SDimitry Andric SVal TaintedSVal) const; 44*06c3fb27SDimitry Andric 45*06c3fb27SDimitry Andric static bool isFromCtypeMacro(const Stmt *S, ASTContext &AC); 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric public: 480b57cec5SDimitry Andric void checkLocation(SVal l, bool isLoad, const Stmt *S, 490b57cec5SDimitry Andric CheckerContext &C) const; 500b57cec5SDimitry Andric }; 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric // FIXME: Eventually replace RegionRawOffset with this class. 530b57cec5SDimitry Andric class RegionRawOffsetV2 { 540b57cec5SDimitry Andric private: 550b57cec5SDimitry Andric const SubRegion *baseRegion; 56*06c3fb27SDimitry Andric NonLoc byteOffset; 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric public: 59*06c3fb27SDimitry Andric RegionRawOffsetV2(const SubRegion *base, NonLoc offset) 60*06c3fb27SDimitry Andric : baseRegion(base), byteOffset(offset) { assert(base); } 610b57cec5SDimitry Andric 62*06c3fb27SDimitry Andric NonLoc getByteOffset() const { return byteOffset; } 630b57cec5SDimitry Andric const SubRegion *getRegion() const { return baseRegion; } 640b57cec5SDimitry Andric 65*06c3fb27SDimitry Andric static std::optional<RegionRawOffsetV2> 66*06c3fb27SDimitry Andric computeOffset(ProgramStateRef State, SValBuilder &SVB, SVal Location); 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric void dump() const; 690b57cec5SDimitry Andric void dumpToStream(raw_ostream &os) const; 700b57cec5SDimitry Andric }; 710b57cec5SDimitry Andric } 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric // TODO: once the constraint manager is smart enough to handle non simplified 740b57cec5SDimitry Andric // symbolic expressions remove this function. Note that this can not be used in 750b57cec5SDimitry Andric // the constraint manager as is, since this does not handle overflows. It is 760b57cec5SDimitry Andric // safe to assume, however, that memory offsets will not overflow. 77*06c3fb27SDimitry Andric // NOTE: callers of this function need to be aware of the effects of overflows 78*06c3fb27SDimitry Andric // and signed<->unsigned conversions! 790b57cec5SDimitry Andric static std::pair<NonLoc, nonloc::ConcreteInt> 800b57cec5SDimitry Andric getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, 810b57cec5SDimitry Andric SValBuilder &svalBuilder) { 82bdd1243dSDimitry Andric std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); 830b57cec5SDimitry Andric if (SymVal && SymVal->isExpression()) { 840b57cec5SDimitry Andric if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) { 850b57cec5SDimitry Andric llvm::APSInt constant = 860b57cec5SDimitry Andric APSIntType(extent.getValue()).convert(SIE->getRHS()); 870b57cec5SDimitry Andric switch (SIE->getOpcode()) { 880b57cec5SDimitry Andric case BO_Mul: 890b57cec5SDimitry Andric // The constant should never be 0 here, since it the result of scaling 900b57cec5SDimitry Andric // based on the size of a type which is never 0. 910b57cec5SDimitry Andric if ((extent.getValue() % constant) != 0) 920b57cec5SDimitry Andric return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent); 930b57cec5SDimitry Andric else 940b57cec5SDimitry Andric return getSimplifiedOffsets( 950b57cec5SDimitry Andric nonloc::SymbolVal(SIE->getLHS()), 960b57cec5SDimitry Andric svalBuilder.makeIntVal(extent.getValue() / constant), 970b57cec5SDimitry Andric svalBuilder); 980b57cec5SDimitry Andric case BO_Add: 990b57cec5SDimitry Andric return getSimplifiedOffsets( 1000b57cec5SDimitry Andric nonloc::SymbolVal(SIE->getLHS()), 1010b57cec5SDimitry Andric svalBuilder.makeIntVal(extent.getValue() - constant), svalBuilder); 1020b57cec5SDimitry Andric default: 1030b57cec5SDimitry Andric break; 1040b57cec5SDimitry Andric } 1050b57cec5SDimitry Andric } 1060b57cec5SDimitry Andric } 1070b57cec5SDimitry Andric 1080b57cec5SDimitry Andric return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent); 1090b57cec5SDimitry Andric } 1100b57cec5SDimitry Andric 111*06c3fb27SDimitry Andric // Evaluate the comparison Value < Threshold with the help of the custom 112*06c3fb27SDimitry Andric // simplification algorithm defined for this checker. Return a pair of states, 113*06c3fb27SDimitry Andric // where the first one corresponds to "value below threshold" and the second 114*06c3fb27SDimitry Andric // corresponds to "value at or above threshold". Returns {nullptr, nullptr} in 115*06c3fb27SDimitry Andric // the case when the evaluation fails. 116*06c3fb27SDimitry Andric static std::pair<ProgramStateRef, ProgramStateRef> 117*06c3fb27SDimitry Andric compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, 118*06c3fb27SDimitry Andric SValBuilder &SVB) { 119*06c3fb27SDimitry Andric if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) { 120*06c3fb27SDimitry Andric std::tie(Value, Threshold) = getSimplifiedOffsets(Value, *ConcreteThreshold, SVB); 121*06c3fb27SDimitry Andric } 122*06c3fb27SDimitry Andric if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) { 123*06c3fb27SDimitry Andric QualType T = Value.getType(SVB.getContext()); 124*06c3fb27SDimitry Andric if (T->isUnsignedIntegerType() && ConcreteThreshold->getValue().isNegative()) { 125*06c3fb27SDimitry Andric // In this case we reduced the bound check to a comparison of the form 126*06c3fb27SDimitry Andric // (symbol or value with unsigned type) < (negative number) 127*06c3fb27SDimitry Andric // which is always false. We are handling these cases separately because 128*06c3fb27SDimitry Andric // evalBinOpNN can perform a signed->unsigned conversion that turns the 129*06c3fb27SDimitry Andric // negative number into a huge positive value and leads to wildly 130*06c3fb27SDimitry Andric // inaccurate conclusions. 131*06c3fb27SDimitry Andric return {nullptr, State}; 132*06c3fb27SDimitry Andric } 133*06c3fb27SDimitry Andric } 134*06c3fb27SDimitry Andric auto BelowThreshold = 135*06c3fb27SDimitry Andric SVB.evalBinOpNN(State, BO_LT, Value, Threshold, SVB.getConditionType()).getAs<NonLoc>(); 136*06c3fb27SDimitry Andric 137*06c3fb27SDimitry Andric if (BelowThreshold) 138*06c3fb27SDimitry Andric return State->assume(*BelowThreshold); 139*06c3fb27SDimitry Andric 140*06c3fb27SDimitry Andric return {nullptr, nullptr}; 141*06c3fb27SDimitry Andric } 142*06c3fb27SDimitry Andric 1430b57cec5SDimitry Andric void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, 1440b57cec5SDimitry Andric const Stmt* LoadS, 1450b57cec5SDimitry Andric CheckerContext &checkerContext) const { 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping 1480b57cec5SDimitry Andric // some new logic here that reasons directly about memory region extents. 1490b57cec5SDimitry Andric // Once that logic is more mature, we can bring it back to assumeInBound() 1500b57cec5SDimitry Andric // for all clients to use. 1510b57cec5SDimitry Andric // 1520b57cec5SDimitry Andric // The algorithm we are using here for bounds checking is to see if the 1530b57cec5SDimitry Andric // memory access is within the extent of the base region. Since we 1540b57cec5SDimitry Andric // have some flexibility in defining the base region, we can achieve 1550b57cec5SDimitry Andric // various levels of conservatism in our buffer overflow checking. 156*06c3fb27SDimitry Andric 157*06c3fb27SDimitry Andric // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as 158*06c3fb27SDimitry Andric // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX) 159*06c3fb27SDimitry Andric // and incomplete analysis of these leads to false positives. As even 160*06c3fb27SDimitry Andric // accurate reports would be confusing for the users, just disable reports 161*06c3fb27SDimitry Andric // from these macros: 162*06c3fb27SDimitry Andric if (isFromCtypeMacro(LoadS, checkerContext.getASTContext())) 163*06c3fb27SDimitry Andric return; 164*06c3fb27SDimitry Andric 1650b57cec5SDimitry Andric ProgramStateRef state = checkerContext.getState(); 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric SValBuilder &svalBuilder = checkerContext.getSValBuilder(); 168*06c3fb27SDimitry Andric const std::optional<RegionRawOffsetV2> &RawOffset = 1690b57cec5SDimitry Andric RegionRawOffsetV2::computeOffset(state, svalBuilder, location); 1700b57cec5SDimitry Andric 171*06c3fb27SDimitry Andric if (!RawOffset) 1720b57cec5SDimitry Andric return; 1730b57cec5SDimitry Andric 174*06c3fb27SDimitry Andric NonLoc ByteOffset = RawOffset->getByteOffset(); 1750b57cec5SDimitry Andric 176*06c3fb27SDimitry Andric // CHECK LOWER BOUND 177*06c3fb27SDimitry Andric const MemSpaceRegion *SR = RawOffset->getRegion()->getMemorySpace(); 178*06c3fb27SDimitry Andric if (!llvm::isa<UnknownSpaceRegion>(SR)) { 179*06c3fb27SDimitry Andric // A pointer to UnknownSpaceRegion may point to the middle of 180*06c3fb27SDimitry Andric // an allocated region. 1810b57cec5SDimitry Andric 182*06c3fb27SDimitry Andric auto [state_precedesLowerBound, state_withinLowerBound] = 183*06c3fb27SDimitry Andric compareValueToThreshold(state, ByteOffset, 184*06c3fb27SDimitry Andric svalBuilder.makeZeroArrayIndex(), svalBuilder); 1850b57cec5SDimitry Andric 1860b57cec5SDimitry Andric if (state_precedesLowerBound && !state_withinLowerBound) { 187*06c3fb27SDimitry Andric // We know that the index definitely precedes the lower bound. 1880b57cec5SDimitry Andric reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes); 1890b57cec5SDimitry Andric return; 1900b57cec5SDimitry Andric } 1910b57cec5SDimitry Andric 192*06c3fb27SDimitry Andric if (state_withinLowerBound) 1930b57cec5SDimitry Andric state = state_withinLowerBound; 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric 196*06c3fb27SDimitry Andric // CHECK UPPER BOUND 197*06c3fb27SDimitry Andric DefinedOrUnknownSVal Size = 198*06c3fb27SDimitry Andric getDynamicExtent(state, RawOffset->getRegion(), svalBuilder); 199*06c3fb27SDimitry Andric if (auto KnownSize = Size.getAs<NonLoc>()) { 200*06c3fb27SDimitry Andric auto [state_withinUpperBound, state_exceedsUpperBound] = 201*06c3fb27SDimitry Andric compareValueToThreshold(state, ByteOffset, *KnownSize, svalBuilder); 2020b57cec5SDimitry Andric 203*06c3fb27SDimitry Andric if (state_exceedsUpperBound) { 204*06c3fb27SDimitry Andric if (!state_withinUpperBound) { 205*06c3fb27SDimitry Andric // We know that the index definitely exceeds the upper bound. 2060b57cec5SDimitry Andric reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); 2070b57cec5SDimitry Andric return; 2080b57cec5SDimitry Andric } 209*06c3fb27SDimitry Andric if (isTainted(state, ByteOffset)) { 210*06c3fb27SDimitry Andric // Both cases are possible, but the index is tainted, so report. 211*06c3fb27SDimitry Andric reportTaintOOB(checkerContext, state_exceedsUpperBound, ByteOffset); 212*06c3fb27SDimitry Andric return; 213*06c3fb27SDimitry Andric } 214*06c3fb27SDimitry Andric } 2150b57cec5SDimitry Andric 216*06c3fb27SDimitry Andric if (state_withinUpperBound) 2170b57cec5SDimitry Andric state = state_withinUpperBound; 2180b57cec5SDimitry Andric } 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric checkerContext.addTransition(state); 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric 223*06c3fb27SDimitry Andric void ArrayBoundCheckerV2::reportTaintOOB(CheckerContext &checkerContext, 224*06c3fb27SDimitry Andric ProgramStateRef errorState, 225*06c3fb27SDimitry Andric SVal TaintedSVal) const { 226*06c3fb27SDimitry Andric ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); 227*06c3fb27SDimitry Andric if (!errorNode) 228*06c3fb27SDimitry Andric return; 229*06c3fb27SDimitry Andric 230*06c3fb27SDimitry Andric if (!TaintBT) 231*06c3fb27SDimitry Andric TaintBT.reset( 232*06c3fb27SDimitry Andric new BugType(this, "Out-of-bound access", categories::TaintedData)); 233*06c3fb27SDimitry Andric 234*06c3fb27SDimitry Andric SmallString<256> buf; 235*06c3fb27SDimitry Andric llvm::raw_svector_ostream os(buf); 236*06c3fb27SDimitry Andric os << "Out of bound memory access (index is tainted)"; 237*06c3fb27SDimitry Andric auto BR = 238*06c3fb27SDimitry Andric std::make_unique<PathSensitiveBugReport>(*TaintBT, os.str(), errorNode); 239*06c3fb27SDimitry Andric 240*06c3fb27SDimitry Andric // Track back the propagation of taintedness. 241*06c3fb27SDimitry Andric for (SymbolRef Sym : getTaintedSymbols(errorState, TaintedSVal)) { 242*06c3fb27SDimitry Andric BR->markInteresting(Sym); 243*06c3fb27SDimitry Andric } 244*06c3fb27SDimitry Andric 245*06c3fb27SDimitry Andric checkerContext.emitReport(std::move(BR)); 246*06c3fb27SDimitry Andric } 247*06c3fb27SDimitry Andric 248*06c3fb27SDimitry Andric void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, 249*06c3fb27SDimitry Andric ProgramStateRef errorState, 250*06c3fb27SDimitry Andric OOB_Kind kind) const { 2510b57cec5SDimitry Andric 2520b57cec5SDimitry Andric ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); 2530b57cec5SDimitry Andric if (!errorNode) 2540b57cec5SDimitry Andric return; 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric if (!BT) 2570b57cec5SDimitry Andric BT.reset(new BuiltinBug(this, "Out-of-bound access")); 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric // FIXME: This diagnostics are preliminary. We should get far better 2600b57cec5SDimitry Andric // diagnostics for explaining buffer overruns. 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric SmallString<256> buf; 2630b57cec5SDimitry Andric llvm::raw_svector_ostream os(buf); 2640b57cec5SDimitry Andric os << "Out of bound memory access "; 2650b57cec5SDimitry Andric switch (kind) { 2660b57cec5SDimitry Andric case OOB_Precedes: 2670b57cec5SDimitry Andric os << "(accessed memory precedes memory block)"; 2680b57cec5SDimitry Andric break; 2690b57cec5SDimitry Andric case OOB_Excedes: 2700b57cec5SDimitry Andric os << "(access exceeds upper limit of memory block)"; 2710b57cec5SDimitry Andric break; 272*06c3fb27SDimitry Andric } 273*06c3fb27SDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode); 274*06c3fb27SDimitry Andric checkerContext.emitReport(std::move(BR)); 2750b57cec5SDimitry Andric } 2760b57cec5SDimitry Andric 277*06c3fb27SDimitry Andric bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) { 278*06c3fb27SDimitry Andric SourceLocation Loc = S->getBeginLoc(); 279*06c3fb27SDimitry Andric if (!Loc.isMacroID()) 280*06c3fb27SDimitry Andric return false; 281*06c3fb27SDimitry Andric 282*06c3fb27SDimitry Andric StringRef MacroName = Lexer::getImmediateMacroName( 283*06c3fb27SDimitry Andric Loc, ACtx.getSourceManager(), ACtx.getLangOpts()); 284*06c3fb27SDimitry Andric 285*06c3fb27SDimitry Andric if (MacroName.size() < 7 || MacroName[0] != 'i' || MacroName[1] != 's') 286*06c3fb27SDimitry Andric return false; 287*06c3fb27SDimitry Andric 288*06c3fb27SDimitry Andric return ((MacroName == "isalnum") || (MacroName == "isalpha") || 289*06c3fb27SDimitry Andric (MacroName == "isblank") || (MacroName == "isdigit") || 290*06c3fb27SDimitry Andric (MacroName == "isgraph") || (MacroName == "islower") || 291*06c3fb27SDimitry Andric (MacroName == "isnctrl") || (MacroName == "isprint") || 292*06c3fb27SDimitry Andric (MacroName == "ispunct") || (MacroName == "isspace") || 293*06c3fb27SDimitry Andric (MacroName == "isupper") || (MacroName == "isxdigit")); 2940b57cec5SDimitry Andric } 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric #ifndef NDEBUG 2970b57cec5SDimitry Andric LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const { 2980b57cec5SDimitry Andric dumpToStream(llvm::errs()); 2990b57cec5SDimitry Andric } 3000b57cec5SDimitry Andric 3010b57cec5SDimitry Andric void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const { 3020b57cec5SDimitry Andric os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; 3030b57cec5SDimitry Andric } 3040b57cec5SDimitry Andric #endif 3050b57cec5SDimitry Andric 306*06c3fb27SDimitry Andric /// For a given Location that can be represented as a symbolic expression 307*06c3fb27SDimitry Andric /// Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block 308*06c3fb27SDimitry Andric /// Arr and the distance of Location from the beginning of Arr (expressed in a 309*06c3fb27SDimitry Andric /// NonLoc that specifies the number of CharUnits). Returns nullopt when these 310*06c3fb27SDimitry Andric /// cannot be determined. 311*06c3fb27SDimitry Andric std::optional<RegionRawOffsetV2> 312*06c3fb27SDimitry Andric RegionRawOffsetV2::computeOffset(ProgramStateRef State, SValBuilder &SVB, 313*06c3fb27SDimitry Andric SVal Location) { 314*06c3fb27SDimitry Andric QualType T = SVB.getArrayIndexType(); 315*06c3fb27SDimitry Andric auto Calc = [&SVB, State, T](BinaryOperatorKind Op, NonLoc LHS, NonLoc RHS) { 316*06c3fb27SDimitry Andric // We will use this utility to add and multiply values. 317*06c3fb27SDimitry Andric return SVB.evalBinOpNN(State, Op, LHS, RHS, T).getAs<NonLoc>(); 318*06c3fb27SDimitry Andric }; 3190b57cec5SDimitry Andric 320*06c3fb27SDimitry Andric const MemRegion *Region = Location.getAsRegion(); 321*06c3fb27SDimitry Andric NonLoc Offset = SVB.makeZeroArrayIndex(); 3220b57cec5SDimitry Andric 323*06c3fb27SDimitry Andric while (Region) { 324*06c3fb27SDimitry Andric if (const auto *ERegion = dyn_cast<ElementRegion>(Region)) { 325*06c3fb27SDimitry Andric if (const auto Index = ERegion->getIndex().getAs<NonLoc>()) { 326*06c3fb27SDimitry Andric QualType ElemType = ERegion->getElementType(); 3270b57cec5SDimitry Andric // If the element is an incomplete type, go no further. 328*06c3fb27SDimitry Andric if (ElemType->isIncompleteType()) 329*06c3fb27SDimitry Andric return std::nullopt; 3300b57cec5SDimitry Andric 331*06c3fb27SDimitry Andric // Perform Offset += Index * sizeof(ElemType); then continue the offset 332*06c3fb27SDimitry Andric // calculations with SuperRegion: 333*06c3fb27SDimitry Andric NonLoc Size = SVB.makeArrayIndex( 334*06c3fb27SDimitry Andric SVB.getContext().getTypeSizeInChars(ElemType).getQuantity()); 335*06c3fb27SDimitry Andric if (auto Delta = Calc(BO_Mul, *Index, Size)) { 336*06c3fb27SDimitry Andric if (auto NewOffset = Calc(BO_Add, Offset, *Delta)) { 337*06c3fb27SDimitry Andric Offset = *NewOffset; 338*06c3fb27SDimitry Andric Region = ERegion->getSuperRegion(); 3390b57cec5SDimitry Andric continue; 3400b57cec5SDimitry Andric } 3410b57cec5SDimitry Andric } 3420b57cec5SDimitry Andric } 343*06c3fb27SDimitry Andric } else if (const auto *SRegion = dyn_cast<SubRegion>(Region)) { 344*06c3fb27SDimitry Andric // NOTE: The dyn_cast<>() is expected to succeed, it'd be very surprising 345*06c3fb27SDimitry Andric // to see a MemSpaceRegion at this point. 346*06c3fb27SDimitry Andric // FIXME: We may return with {<Region>, 0} even if we didn't handle any 347*06c3fb27SDimitry Andric // ElementRegion layers. I think that this behavior was introduced 348*06c3fb27SDimitry Andric // accidentally by 8a4c760c204546aba566e302f299f7ed2e00e287 in 2011, so 349*06c3fb27SDimitry Andric // it may be useful to review it in the future. 350*06c3fb27SDimitry Andric return RegionRawOffsetV2(SRegion, Offset); 351*06c3fb27SDimitry Andric } 352*06c3fb27SDimitry Andric return std::nullopt; 353*06c3fb27SDimitry Andric } 354*06c3fb27SDimitry Andric return std::nullopt; 3550b57cec5SDimitry Andric } 3560b57cec5SDimitry Andric 3570b57cec5SDimitry Andric void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { 3580b57cec5SDimitry Andric mgr.registerChecker<ArrayBoundCheckerV2>(); 3590b57cec5SDimitry Andric } 3600b57cec5SDimitry Andric 3615ffd83dbSDimitry Andric bool ento::shouldRegisterArrayBoundCheckerV2(const CheckerManager &mgr) { 3620b57cec5SDimitry Andric return true; 3630b57cec5SDimitry Andric } 364