diff --git a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp --- a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp +++ b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp @@ -25,6 +25,51 @@ namespace clang { namespace ento { +DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, + const MemRegion *MR, SValBuilder &SVB); + +static bool isFlexibleArrayMember(const FieldDecl *FD) { + const auto *RD = FD->getParent(); + if (!RD->hasFlexibleArrayMember()) + return false; + + const FieldDecl *LastFD = nullptr; + for (const FieldDecl *It : RD->fields()) + LastFD = It; + + return FD == LastFD; +} + +static std::optional +getFlexibleArrayExtent(ProgramStateRef State, const MemRegion *MR, + SValBuilder &SVB) { + const auto *FieldMR = dyn_cast(MR); + if (nullptr == FieldMR) + return std::nullopt; + + const FieldDecl *FD = FieldMR->getDecl(); + if (!isFlexibleArrayMember(FD)) + return std::nullopt; + + const RecordDecl *RD = FD->getParent(); + const MemRegion *BaseMR = FieldMR->getBaseRegion(); + auto BaseSize = getDynamicExtent(State, BaseMR, SVB); + + auto &C = SVB.getContext(); + uint64_t RecordSize = C.getTypeSizeInChars(C.getRecordType(RD)).getQuantity(); + SVal RecordSizeVal = SVB.makeIntVal(RecordSize, C.getSizeType()); + + SVal BaseTooSmall = + SVB.evalBinOp(State, BO_LT, BaseSize, RecordSizeVal, C.BoolTy); + if (!BaseTooSmall.isUndef() && + State->assume(*BaseTooSmall.getAs(), true)) + return std::nullopt; + + SVal FlexibleArrayExtent = + SVB.evalBinOp(State, BO_Sub, BaseSize, RecordSizeVal, C.getSizeType()); + return FlexibleArrayExtent.getAs(); +} + DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB) { MR = MR->StripCasts(); @@ -32,6 +77,9 @@ if (const DefinedOrUnknownSVal *Size = State->get(MR)) return *Size; + if (auto FlexibleArrayExtent = getFlexibleArrayExtent(State, MR, SVB)) + return *FlexibleArrayExtent; + return MR->getMemRegionManager().getStaticSize(MR, SVB); } diff --git a/clang/test/Analysis/flexible-array-members.c b/clang/test/Analysis/flexible-array-members.c --- a/clang/test/Analysis/flexible-array-members.c +++ b/clang/test/Analysis/flexible-array-members.c @@ -44,20 +44,52 @@ clang_analyzer_dump(clang_analyzer_getExtent(&fam)); clang_analyzer_dump(clang_analyzer_getExtent(fam.data)); // expected-warning@-2 {{4 S64b}} - // expected-warning@-2 {{Unknown}} + // expected-warning@-2 {{0 U64b}} FAM *p = (FAM *)alloca(sizeof(FAM)); clang_analyzer_dump(clang_analyzer_getExtent(p)); clang_analyzer_dump(clang_analyzer_getExtent(p->data)); // expected-warning@-2 {{4 U64b}} - // expected-warning@-2 {{Unknown}} + // expected-warning@-2 {{0 U64b}} FAM *q = (FAM *)malloc(sizeof(FAM)); clang_analyzer_dump(clang_analyzer_getExtent(q)); clang_analyzer_dump(clang_analyzer_getExtent(q->data)); // expected-warning@-2 {{4 U64b}} - // expected-warning@-2 {{Unknown}} + // expected-warning@-2 {{0 U64b}} free(q); + + q = (FAM *)malloc(sizeof(FAM) + sizeof(int) * 2); + clang_analyzer_dump(clang_analyzer_getExtent(q)); + clang_analyzer_dump(clang_analyzer_getExtent(q->data)); + // expected-warning@-2 {{12 U64b}} + // expected-warning@-2 {{8 U64b}} + free(q); + + typedef struct __attribute__((packed)) { + char c; + int data[]; + } PackedFAM; + + PackedFAM *t = (PackedFAM *)malloc(sizeof(PackedFAM) + sizeof(int) * 2); + clang_analyzer_dump(clang_analyzer_getExtent(t)); + clang_analyzer_dump(clang_analyzer_getExtent(t->data)); + // expected-warning@-2 {{9 U64b}} + // expected-warning@-2 {{8 U64b}} + free(t); +} + +void test_too_small_base(void) { + typedef struct FAM { + long c; + int data[]; + } FAM; + short s = 0; + FAM *p = (FAM *) &s; + clang_analyzer_dump(clang_analyzer_getExtent(p)); + clang_analyzer_dump(clang_analyzer_getExtent(p->data)); + // expected-warning@-2 {{2 S64b}} + // expected-warning@-2 {{Unknown}} } void test_zero_length_array_fam(void) {