diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h @@ -53,6 +53,11 @@ /// (bufptr) // extent is unknown SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV); +/// \returns The stored element count of the region represented by a symbolic +/// value \p BufV. +DefinedOrUnknownSVal getDynamicElementCountWithOffset(ProgramStateRef State, + SVal BufV, QualType Ty); + } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -325,12 +325,12 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const { - const MemRegion *MR = getArgRegion(CE, C); - if (!MR) + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; ProgramStateRef State = C.getState(); - DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder()); + SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); State = State->BindExpr(CE, C.getLocationContext(), Size); C.addTransition(State); @@ -338,12 +338,12 @@ void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const { - const MemRegion *MR = getArgRegion(CE, C); - if (!MR) + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - DefinedOrUnknownSVal Size = - getDynamicExtent(C.getState(), MR, C.getSValBuilder()); + ProgramStateRef State = C.getState(); + SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); printAndReport(C, Size); } @@ -362,8 +362,8 @@ assert(!ElementTy->isPointerType()); - DefinedOrUnknownSVal ElementCount = - getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); + DefinedOrUnknownSVal ElementCount = getDynamicElementCountWithOffset( + C.getState(), C.getSVal(getArgExpr(CE, C)), ElementTy); printAndReport(C, ElementCount); } diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -161,20 +161,41 @@ return; } - DefinedOrUnknownSVal ElementCount = getDynamicElementCount( - Ctx.getState(), SuperRegion, Ctx.getSValBuilder(), - CE.getArgExpr(1)->getType()->getPointeeType()); - const llvm::APSInt &ArrSize = - ElementCount.castAs().getValue(); + QualType ElemType = CE.getArgExpr(1)->getType()->getPointeeType(); + ProgramStateRef State = Ctx.getState(); + SValBuilder &SVB = Ctx.getSValBuilder(); + ASTContext &ASTCtx = Ctx.getASTContext(); + + auto ElementCount = + getDynamicElementCountWithOffset(State, CE.getArgSVal(1), ElemType) + .getAs(); + if (!ElementCount) + return; + const llvm::APSInt &ArrSize = ElementCount->getValue(); + + CharUnits ElemSizeInChars = ASTCtx.getTypeSizeInChars(ElemType); + int64_t ElemSizeInBits = + (ElemSizeInChars.isZero() ? 1 : ElemSizeInChars.getQuantity()) * + ASTCtx.getCharWidth(); + const NonLoc MROffset = + SVB.makeArrayIndex(MR->getAsOffset().getOffset() / ElemSizeInBits); + SVal Count = CE.getArgSVal(0); for (size_t i = 0; i < ArrSize; ++i) { const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); - - const ElementRegion *const ER = RegionManager.getElementRegion( - CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, - Ctx.getASTContext()); - - ReqRegions.push_back(ER->getAs()); + auto CountReached = SVB.evalBinOp(State, BO_GE, Idx, Count, ASTCtx.BoolTy) + .getAs(); + if (CountReached && State->assume(*CountReached, true)) + break; + + auto ElementRegionIndex = + SVB.evalBinOp(State, BO_Add, Idx, MROffset, SVB.getArrayIndexType()) + .getAs(); + if (ElementRegionIndex) { + const ElementRegion *const ER = RegionManager.getElementRegion( + ElemType, *ElementRegionIndex, SuperRegion, Ctx.getASTContext()); + ReqRegions.push_back(ER); + } } } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { ReqRegions.push_back(MR); 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 @@ -30,7 +30,9 @@ MR = MR->StripCasts(); if (const DefinedOrUnknownSVal *Size = State->get(MR)) - return *Size; + if (auto SSize = + SVB.convertToArrayIndex(*Size).getAs()) + return *SSize; return MR->getMemRegionManager().getStaticSize(MR, SVB); } @@ -40,6 +42,39 @@ SVB.getArrayIndexType()); } +static DefinedOrUnknownSVal getConstantArrayElementCount(SValBuilder &SVB, + const MemRegion *MR) { + if (!MR) + return UnknownVal(); + MR = MR->StripCasts(); + + const auto *TVR = MR->getAs(); + if (!TVR) + return UnknownVal(); + + QualType Ty = TVR->getValueType(); + if (const auto *ConstArrayTy = + dyn_cast_or_null(Ty->getAsArrayTypeUnsafe())) + return SVB.makeIntVal(ConstArrayTy->getSize(), false); + + return UnknownVal(); +} + +static DefinedOrUnknownSVal +getDynamicElementCount(ProgramStateRef State, SVal Size, + DefinedOrUnknownSVal ElementSize) { + assert(!ElementSize.isZeroConstant() && "Non-zero element size expected"); + if (Size.isUndef()) + return UnknownVal(); + + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + + auto ElementCount = + SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType()) + .getAs(); + return ElementCount ? *ElementCount : UnknownVal(); +} + DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, @@ -47,13 +82,12 @@ assert(MR != nullptr && "Not-null region expected"); MR = MR->StripCasts(); - DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB); - SVal ElementSize = getElementExtent(ElementTy, SVB); + DefinedOrUnknownSVal ElementSize = getElementExtent(ElementTy, SVB); + if (ElementSize.isZeroConstant()) + return getConstantArrayElementCount(SVB, MR); - SVal ElementCount = - SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType()); - - return ElementCount.castAs(); + return getDynamicElementCount(State, getDynamicExtent(State, MR, SVB), + ElementSize); } SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV) { @@ -79,6 +113,18 @@ SvalBuilder.getArrayIndexType()); } +DefinedOrUnknownSVal getDynamicElementCountWithOffset(ProgramStateRef State, + SVal BufV, + QualType ElementTy) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + DefinedOrUnknownSVal ElementSize = getElementExtent(ElementTy, SVB); + if (ElementSize.isZeroConstant()) + return getConstantArrayElementCount(SVB, BufV.getAsRegion()); + + return getDynamicElementCount(State, getDynamicExtentWithOffset(State, BufV), + ElementSize); +} + ProgramStateRef setDynamicExtent(ProgramStateRef State, const MemRegion *MR, DefinedOrUnknownSVal Size, SValBuilder &SVB) { MR = MR->StripCasts(); diff --git a/clang/test/Analysis/array-bound-v2-constraint-check.c b/clang/test/Analysis/array-bound-v2-constraint-check.c --- a/clang/test/Analysis/array-bound-v2-constraint-check.c +++ b/clang/test/Analysis/array-bound-v2-constraint-check.c @@ -1,10 +1,10 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.security.ArrayBoundV2,debug.ExprInspection \ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,alpha.security.ArrayBoundV2,debug.ExprInspection \ // RUN: -analyzer-config eagerly-assume=false -verify %s void clang_analyzer_eval(int); void clang_analyzer_printState(void); -typedef unsigned long long size_t; +typedef typeof(sizeof(int)) size_t; const char a[] = "abcd"; // extent: 5 bytes void symbolic_size_t_and_int0(size_t len) { @@ -83,6 +83,18 @@ clang_analyzer_eval(len <= 2); // expected-warning {{UNKNOWN}} } +void *malloc(size_t); +void free(void *); +void symbolic_longlong_and_int0_dynamic_extent(long long len) { + char *b = malloc(5); + (void)b[len + 1]; // no-warning + // len: [-1,3] + clang_analyzer_eval(-1 <= len && len <= 3); // expected-warning {{TRUE}} + clang_analyzer_eval(0 <= len); // expected-warning {{UNKNOWN}} + clang_analyzer_eval(len <= 2); // expected-warning {{UNKNOWN}} + free(b); +} + void symbolic_longlong_and_int1(long long len) { (void)a[len]; // no-warning // len: [0,4] 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 S64b}} 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 {{4 S64b}} + // expected-warning@-2 {{0 S64b}} 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 {{4 S64b}} + // expected-warning@-2 {{0 S64b}} + 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 S64b}} + // expected-warning@-2 {{8 S64b}} 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 S64b}} + // expected-warning@-2 {{8 S64b}} + 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 {{-6 S64b}} } void test_zero_length_array_fam(void) { @@ -70,19 +102,19 @@ 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 S64b}} 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 {{4 S64b}} + // expected-warning@-2 {{0 S64b}} 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 {{4 S64b}} + // expected-warning@-2 {{0 S64b}} free(q); } @@ -97,19 +129,19 @@ clang_analyzer_dump(clang_analyzer_getExtent(&likely_fam)); clang_analyzer_dump(clang_analyzer_getExtent(likely_fam.data)); // expected-warning@-2 {{8 S64b}} - // expected-warning@-2 {{Unknown}} + // expected-warning@-2 {{4 S64b}} FAM *p = (FAM *)alloca(sizeof(FAM)); clang_analyzer_dump(clang_analyzer_getExtent(p)); clang_analyzer_dump(clang_analyzer_getExtent(p->data)); - // expected-warning@-2 {{8 U64b}} - // expected-warning@-2 {{Unknown}} + // expected-warning@-2 {{8 S64b}} + // expected-warning@-2 {{4 S64b}} FAM *q = (FAM *)malloc(sizeof(FAM)); clang_analyzer_dump(clang_analyzer_getExtent(q)); clang_analyzer_dump(clang_analyzer_getExtent(q->data)); - // expected-warning@-2 {{8 U64b}} - // expected-warning@-2 {{Unknown}} + // expected-warning@-2 {{8 S64b}} + // expected-warning@-2 {{4 S64b}} free(q); #else FAM likely_fam; @@ -121,13 +153,13 @@ FAM *p = (FAM *)alloca(sizeof(FAM)); clang_analyzer_dump(clang_analyzer_getExtent(p)); clang_analyzer_dump(clang_analyzer_getExtent(p->data)); - // expected-warning@-2 {{8 U64b}} + // expected-warning@-2 {{8 S64b}} // expected-warning@-2 {{4 S64b}} FAM *q = (FAM *)malloc(sizeof(FAM)); clang_analyzer_dump(clang_analyzer_getExtent(q)); clang_analyzer_dump(clang_analyzer_getExtent(q->data)); - // expected-warning@-2 {{8 U64b}} + // expected-warning@-2 {{8 S64b}} // expected-warning@-2 {{4 S64b}} free(q); #endif diff --git a/clang/test/Analysis/memory-model.cpp b/clang/test/Analysis/memory-model.cpp --- a/clang/test/Analysis/memory-model.cpp +++ b/clang/test/Analysis/memory-model.cpp @@ -66,8 +66,8 @@ void field_ptr(S *a) { clang_analyzer_dump(&a->f); // expected-warning {{Element{SymRegion{reg_$0},0 S64b,struct S}.f}} - clang_analyzer_dumpExtent(&a->f); // expected-warning {{4 S64b}} - clang_analyzer_dumpElementCount(&a->f); // expected-warning {{1 S64b}} + clang_analyzer_dumpExtent(&a->f); // expected-warning {{extent_$1{SymRegion{reg_$0}}}} + clang_analyzer_dumpElementCount(&a->f); // expected-warning {{(extent_$1{SymRegion{reg_$0}}) / 4U}} } void symbolic_array() { @@ -90,7 +90,7 @@ void symbolic_malloc() { int *a = (int *)malloc(12); clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} - clang_analyzer_dumpExtent(a); // expected-warning {{12 U64b}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} free(a); } @@ -98,22 +98,22 @@ void symbolic_alloca() { int *a = (int *)alloca(12); clang_analyzer_dump(a); // expected-warning {{Element{HeapSymRegion{conj}} - clang_analyzer_dumpExtent(a); // expected-warning {{12 U64b}} + clang_analyzer_dumpExtent(a); // expected-warning {{12 S64b}} clang_analyzer_dumpElementCount(a); // expected-warning {{3 S64b}} } void symbolic_complex() { int *a = (int *)malloc(4); - clang_analyzer_dumpExtent(a); // expected-warning {{4 U64b}} + clang_analyzer_dumpExtent(a); // expected-warning {{4 S64b}} clang_analyzer_dumpElementCount(a); // expected-warning {{1 S64b}} int *b = (int *)realloc(a, sizeof(int) * 2); - clang_analyzer_dumpExtent(b); // expected-warning {{8 U64b}} + clang_analyzer_dumpExtent(b); // expected-warning {{8 S64b}} clang_analyzer_dumpElementCount(b); // expected-warning {{2 S64b}} free(b); int *c = (int *)calloc(3, 4); - clang_analyzer_dumpExtent(c); // expected-warning {{12 U64b}} + clang_analyzer_dumpExtent(c); // expected-warning {{12 S64b}} clang_analyzer_dumpElementCount(c); // expected-warning {{3 S64b}} free(c); } @@ -123,7 +123,7 @@ char *b = (char *)malloc(13); clang_analyzer_dump(clang_analyzer_getExtent(a)); // expected-warning {{13 S64b}} - clang_analyzer_dump(clang_analyzer_getExtent(b)); // expected-warning {{13 U64b}} + clang_analyzer_dump(clang_analyzer_getExtent(b)); // expected-warning {{13 S64b}} clang_analyzer_eval(clang_analyzer_getExtent(a) == clang_analyzer_getExtent(b)); // expected-warning@-2 {{TRUE}} @@ -155,3 +155,16 @@ operator delete[](a, std::align_val_t(32)); } + +int a[5][0]; +struct T { + int x[]; +}; + +T t[5]; +void zero_sized_element() { + clang_analyzer_dumpExtent(a); // expected-warning {{0 S64b}} + clang_analyzer_dumpElementCount(a); // expected-warning {{5 S64b}} + clang_analyzer_dumpExtent(t); // expected-warning {{0 S64b}} + clang_analyzer_dumpElementCount(t); // expected-warning {{5 S64b}} +} diff --git a/clang/test/Analysis/mpichecker.cpp b/clang/test/Analysis/mpichecker.cpp --- a/clang/test/Analysis/mpichecker.cpp +++ b/clang/test/Analysis/mpichecker.cpp @@ -272,6 +272,49 @@ MPI_Wait(&rs.req2, MPI_STATUS_IGNORE); } // no error +void nestedRequestWithCount() { + typedef struct { + MPI_Request req[3]; + MPI_Request req2; + } ReqStruct; + + ReqStruct rs; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + for (int i = 0; i < 3; ++i) + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req[i]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req2); + MPI_Waitall(2, rs.req, MPI_STATUSES_IGNORE); + MPI_Waitall(1, rs.req + 2, MPI_STATUSES_IGNORE); + MPI_Wait(&rs.req2, MPI_STATUS_IGNORE); +} // no error + +void nestedRequestWithCountMissingNonBlockingWait() { + typedef struct { + MPI_Request req[3]; + MPI_Request req2; + } ReqStruct; + + ReqStruct rs; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + for (int i = 0; i < 3; ++i) + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req[i]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req2); + MPI_Waitall(1, rs.req, MPI_STATUSES_IGNORE); + // MPI_Waitall(1, rs.req + 1, MPI_STATUSES_IGNORE); + MPI_Waitall(1, rs.req + 2, MPI_STATUSES_IGNORE); + MPI_Wait(&rs.req2, MPI_STATUS_IGNORE); +} // expected-warning{{Request 'rs.req[1]' has no matching wait.}} + void singleRequestInWaitall() { MPI_Request r; int rank = 0;