diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -172,13 +172,18 @@ return; NonLoc ByteOffset = RawOffset->getByteOffset(); + const SubRegion *Reg = RawOffset->getRegion(); // CHECK LOWER BOUND - const MemSpaceRegion *SR = RawOffset->getRegion()->getMemorySpace(); - if (!llvm::isa(SR)) { - // A pointer to UnknownSpaceRegion may point to the middle of - // an allocated region. - + const MemSpaceRegion *Space = Reg->getMemorySpace(); + if (!(isa(Reg) && isa(Space))) { + // A symbolic region in unknown space represents an unknown pointer that + // may point into the middle of an array, so we don't look for underflows. + // Both conditions are significant because we want to check underflows in + // symbolic regions on the heap (which may be introduced by checkers like + // MallocChecker that call SValBuilder::getConjuredHeapSymbolVal()) and + // non-symbolic regions (e.g. a field subregion of a symbolic region) in + // unknown space. auto [state_precedesLowerBound, state_withinLowerBound] = compareValueToThreshold(state, ByteOffset, svalBuilder.makeZeroArrayIndex(), svalBuilder); @@ -195,7 +200,7 @@ // CHECK UPPER BOUND DefinedOrUnknownSVal Size = - getDynamicExtent(state, RawOffset->getRegion(), svalBuilder); + getDynamicExtent(state, Reg, svalBuilder); if (auto KnownSize = Size.getAs()) { auto [state_withinUpperBound, state_exceedsUpperBound] = compareValueToThreshold(state, ByteOffset, *KnownSize, svalBuilder); diff --git a/clang/test/Analysis/out-of-bounds.c b/clang/test/Analysis/out-of-bounds.c --- a/clang/test/Analysis/out-of-bounds.c +++ b/clang/test/Analysis/out-of-bounds.c @@ -151,11 +151,23 @@ // Don't warn when indexing below the start of a symbolic region's whose // base extent we don't know. int *get_symbolic(void); -void test_index_below_symboloc(void) { +void test_underflow_symbolic(void) { int *buf = get_symbolic(); buf[-1] = 0; // no-warning; } +// But warn if we understand the internal memory layout of a symbolic region. +typedef struct { + int id; + char name[256]; +} user_t; + +user_t *get_symbolic_user(void); +char test_underflow_symbolic_2() { + user_t *user = get_symbolic_user(); + return user->name[-1]; // expected-warning{{Out of bound memory access}} +} + void test_incomplete_struct(void) { extern struct incomplete incomplete; int *p = (int *)&incomplete;