Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -160,10 +160,11 @@ { public: MallocChecker() - : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr), - II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr), - II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr), - II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {} + : II_alloca(nullptr), II_alloca_builtin(nullptr), II_malloc(nullptr), + II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), + II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), + II_strdup(nullptr), II_kmalloc(nullptr), II_if_nameindex(nullptr), + II_if_freenameindex(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -218,9 +219,11 @@ mutable std::unique_ptr BT_BadFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_MismatchedDealloc; mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; - mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, - *II_valloc, *II_reallocf, *II_strndup, *II_strdup, - *II_kmalloc, *II_if_nameindex, *II_if_freenameindex; + mutable std::unique_ptr BT_MallocZero[CK_NumCheckKinds]; + mutable IdentifierInfo *II_alloca, *II_alloca_builtin, *II_malloc, *II_free, + *II_realloc, *II_calloc, *II_valloc, *II_reallocf, + *II_strndup, *II_strdup, *II_kmalloc, *II_if_nameindex, + *II_if_freenameindex; mutable Optional KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -252,22 +255,27 @@ MemoryOperationKind MemKind) const; bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; ///@} + + ProgramStateRef BasicAllocationCheck(CheckerContext &C, const CallExpr *CE, + const unsigned NumArgs, + const unsigned SizeArg, + ProgramStateRef State) const; + + ProgramStateRef CheckCallocZero(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State) const; + ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att) const; + const OwnershipAttr* Att, + ProgramStateRef State) const; static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, - const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family = AF_Malloc) { - return MallocMemAux(C, CE, - State->getSVal(SizeEx, C.getLocationContext()), - Init, State, Family); - } - + const Expr *SizeEx, SVal Init, + ProgramStateRef State, + AllocationFamily Family = AF_Malloc); static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, - SVal SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family = AF_Malloc); + SVal SizeEx, SVal Init, + ProgramStateRef State, + AllocationFamily Family = AF_Malloc); // Check if this malloc() for special flags. At present that means M_ZERO or // __GFP_ZERO (in which case, treat it like calloc). @@ -281,7 +289,8 @@ AllocationFamily Family = AF_Malloc); ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att) const; + const OwnershipAttr* Att, + ProgramStateRef State) const; ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, unsigned Num, bool Hold, @@ -295,8 +304,10 @@ bool ReturnsNullOnFailure = false) const; ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE, - bool FreesMemOnFailure) const; - static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE); + bool FreesMemOnFailure, + ProgramStateRef State) const; + static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State); ///\brief Check if the memory associated with this symbol was released. bool isReleased(SymbolRef Sym, CheckerContext &C) const; @@ -337,6 +348,8 @@ ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); + void ReportZeroByteAllocation(CheckerContext &C, ProgramStateRef FalseState, + const Expr *Arg, const Expr *AllocExpr) const; void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const; void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, @@ -497,6 +510,8 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { if (II_malloc) return; + II_alloca = &Ctx.Idents.get("alloca"); + II_alloca_builtin = &Ctx.Idents.get("__builtin_alloca"); II_malloc = &Ctx.Idents.get("malloc"); II_free = &Ctx.Idents.get("free"); II_realloc = &Ctx.Idents.get("realloc"); @@ -545,9 +560,10 @@ } if (Family == AF_Malloc && CheckAlloc) { - if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || - FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || - FunI == II_strndup || FunI == II_kmalloc) + if (FunI == II_alloca || FunI == II_alloca_builtin || FunI == II_malloc || + FunI == II_realloc || FunI == II_reallocf || FunI == II_calloc || + FunI == II_valloc || FunI == II_strdup || FunI == II_strndup || + FunI == II_kmalloc) return true; } @@ -710,6 +726,9 @@ if (FunI == II_malloc) { if (CE->getNumArgs() < 1) return; + else if (CE->getNumArgs() == 1) + State = BasicAllocationCheck(C, CE, 1, 0, State); + if (CE->getNumArgs() < 3) { State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); } else if (CE->getNumArgs() == 3) { @@ -731,20 +750,25 @@ if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = BasicAllocationCheck(C, CE, 1, 0, State); } else if (FunI == II_realloc) { - State = ReallocMem(C, CE, false); + State = ReallocMem(C, CE, false, State); + State = BasicAllocationCheck(C, CE, 2, 1, State); } else if (FunI == II_reallocf) { - State = ReallocMem(C, CE, true); + State = ReallocMem(C, CE, true, State); + State = BasicAllocationCheck(C, CE, 2, 1, State); } else if (FunI == II_calloc) { - State = CallocMem(C, CE); + State = CallocMem(C, CE, State); + State = CheckCallocZero(C, CE, State); } else if (FunI == II_free) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } else if (FunI == II_strdup) { State = MallocUpdateRefState(C, CE, State); } else if (FunI == II_strndup) { State = MallocUpdateRefState(C, CE, State); - } - else if (isStandardNewDelete(FD, C.getASTContext())) { + } else if (FunI == II_alloca || FunI == II_alloca_builtin) { + State = BasicAllocationCheck(C, CE, 1, 0, State); + } else if (isStandardNewDelete(FD, C.getASTContext())) { // Process direct calls to operator new/new[]/delete/delete[] functions // as distinct from new/new[]/delete/delete[] expressions that are // processed by the checkPostStmt callbacks for CXXNewExpr and @@ -778,11 +802,11 @@ for (const auto *I : FD->specific_attrs()) { switch (I->getOwnKind()) { case OwnershipAttr::Returns: - State = MallocMemReturnsAttr(C, CE, I); + State = MallocMemReturnsAttr(C, CE, I, State); break; case OwnershipAttr::Takes: case OwnershipAttr::Holds: - State = FreeMemAttr(C, CE, I); + State = FreeMemAttr(C, CE, I, State); break; } } @@ -790,6 +814,89 @@ C.addTransition(State); } +//===----------------------------------------------------------------------===// +// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" +// with allocation size 0 +//===----------------------------------------------------------------------===// + +// Returns true if we try to do a zero byte allocation, false otherwise. +// Fills in TrueState and TalseState. +static bool IsZeroByteAllocation(ProgramStateRef State, + const SVal ArgVal, + ProgramStateRef *TrueState, + ProgramStateRef *FalseState) { + std::tie(*TrueState, *FalseState) = + State->assume(ArgVal.castAs()); + + return (*FalseState && !*TrueState); +} + +// Does a basic check for 0-sized allocations suitable for most of the below +// functions (modulo "calloc") +ProgramStateRef MallocChecker::BasicAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned NumArgs, + const unsigned SizeArg, + ProgramStateRef State) const { + if (!State) + return nullptr; + + // Sanity check for the correct number of arguments + if (CE->getNumArgs() != NumArgs) + return nullptr; + + // Check if the allocation size is 0. + ProgramStateRef TrueState = nullptr, FalseState = nullptr; + const Expr *Arg = CE->getArg(SizeArg); + SVal ArgVal = State->getSVal(Arg, C.getLocationContext()); + + if (ArgVal.isUnknownOrUndef()) + return nullptr; + + // Is the value perfectly constrained to zero? + if (IsZeroByteAllocation(State, ArgVal, &TrueState, &FalseState)) { + ReportZeroByteAllocation(C, FalseState, Arg, CE); + return nullptr; + } + // Assume the value is non-zero going forward. + assert(TrueState); + return TrueState; +} + +ProgramStateRef MallocChecker::CheckCallocZero(CheckerContext &C, + const CallExpr *CE, + ProgramStateRef State) const { + if (!State) + return nullptr; + + unsigned int NArgs = CE->getNumArgs(); + if (NArgs != 2) + return nullptr; + + ProgramStateRef TrueState = nullptr, FalseState = nullptr; + + unsigned int i; + for (i = 0; i < NArgs; i++) { + const Expr *Arg = CE->getArg(i); + SVal ArgVal = State->getSVal(Arg, C.getLocationContext()); + if (ArgVal.isUnknownOrUndef()) { + if (i == 0) + continue; + else + return nullptr; + } + + if (IsZeroByteAllocation(State, ArgVal, &TrueState, &FalseState)) { + ReportZeroByteAllocation(C, FalseState, Arg, CE); + return nullptr; + } + } + + // Assume the value is non-zero going forward. + assert(TrueState); + return TrueState; +} + static QualType getDeepPointeeType(QualType T) { QualType Result = T, PointeeType = T->getPointeeType(); while (!PointeeType.isNull()) { @@ -919,15 +1026,31 @@ ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr *Att) const { + const OwnershipAttr *Att, + ProgramStateRef State) const { + if (!State) + return nullptr; + if (Att->getModule() != II_malloc) return nullptr; OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); if (I != E) { - return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); + return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State); } - return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), C.getState()); + return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State); +} + +ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + const Expr *SizeEx, SVal Init, + ProgramStateRef State, + AllocationFamily Family) { + if (!State) + return nullptr; + + return MallocMemAux(C, CE, State->getSVal(SizeEx, C.getLocationContext()), + Init, State, Family); } ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, @@ -935,6 +1058,8 @@ SVal Size, SVal Init, ProgramStateRef State, AllocationFamily Family) { + if (!State) + return nullptr; // We expect the malloc functions to return a pointer. if (!Loc::isLocType(CE->getType())) @@ -976,6 +1101,9 @@ const Expr *E, ProgramStateRef State, AllocationFamily Family) { + if (!State) + return nullptr; + // Get the return value. SVal retVal = State->getSVal(E, C.getLocationContext()); @@ -992,11 +1120,14 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr *Att) const { + const OwnershipAttr *Att, + ProgramStateRef State) const { + if (!State) + return nullptr; + if (Att->getModule() != II_malloc) return nullptr; - ProgramStateRef State = C.getState(); bool ReleasedAllocated = false; for (const auto &Arg : Att->args()) { @@ -1011,15 +1142,18 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, - ProgramStateRef state, + ProgramStateRef State, unsigned Num, bool Hold, bool &ReleasedAllocated, bool ReturnsNullOnFailure) const { + if (!State) + return nullptr; + if (CE->getNumArgs() < (Num + 1)) return nullptr; - return FreeMemAux(C, CE->getArg(Num), CE, state, Hold, + return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, ReleasedAllocated, ReturnsNullOnFailure); } @@ -1152,6 +1286,9 @@ bool &ReleasedAllocated, bool ReturnsNullOnFailure) const { + if (!State) + return nullptr; + SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext()); if (!ArgVal.getAs()) return nullptr; @@ -1407,6 +1544,37 @@ } } +void MallocChecker::ReportZeroByteAllocation(CheckerContext &C, + ProgramStateRef FalseState, + const Expr *Arg, + const Expr *AllocExpr) const { + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic]) + return; + + auto CheckKind = getCheckIfTracked(C, AllocExpr); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateSink(FalseState)) { + + if (!BT_MallocZero[*CheckKind]) + BT_MallocZero[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Zero allocation", "Memory Error")); + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to '"; + printAllocDeallocName(os, C, AllocExpr); + os << "' has an allocation size of 0 bytes"; + BugReport *Report = new BugReport(*BT_MallocZero[*CheckKind], os.str(), N); + + Report->addRange(Arg->getSourceRange()); + bugreporter::trackNullOrUndefValue(N, Arg, *Report); + C.emitReport(Report); + } +} + void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const { @@ -1655,14 +1823,17 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE, - bool FreesOnFail) const { + bool FreesOnFail, + ProgramStateRef State) const { + if (!State) + return nullptr; + if (CE->getNumArgs() < 2) return nullptr; - ProgramStateRef state = C.getState(); const Expr *arg0Expr = CE->getArg(0); const LocationContext *LCtx = C.getLocationContext(); - SVal Arg0Val = state->getSVal(arg0Expr, LCtx); + SVal Arg0Val = State->getSVal(arg0Expr, LCtx); if (!Arg0Val.getAs()) return nullptr; DefinedOrUnknownSVal arg0Val = Arg0Val.castAs(); @@ -1670,7 +1841,7 @@ SValBuilder &svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal PtrEQ = - svalBuilder.evalEQ(state, arg0Val, svalBuilder.makeNull()); + svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull()); // Get the size argument. If there is no size arg then give up. const Expr *Arg1 = CE->getArg(1); @@ -1678,20 +1849,20 @@ return nullptr; // Get the value of the size argument. - SVal Arg1ValG = state->getSVal(Arg1, LCtx); + SVal Arg1ValG = State->getSVal(Arg1, LCtx); if (!Arg1ValG.getAs()) return nullptr; DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs(); // Compare the size argument to 0. DefinedOrUnknownSVal SizeZero = - svalBuilder.evalEQ(state, Arg1Val, + svalBuilder.evalEQ(State, Arg1Val, svalBuilder.makeIntValWithPtrWidth(0, false)); ProgramStateRef StatePtrIsNull, StatePtrNotNull; - std::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ); + std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ); ProgramStateRef StateSizeIsZero, StateSizeNotZero; - std::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero); + std::tie(StateSizeIsZero, StateSizeNotZero) = State->assume(SizeZero); // We only assume exceptional states if they are definitely true; if the // state is under-constrained, assume regular realloc behavior. bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull; @@ -1711,7 +1882,7 @@ // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size). assert(!PrtIsNull); SymbolRef FromPtr = arg0Val.getAsSymbol(); - SVal RetVal = state->getSVal(CE, LCtx); + SVal RetVal = State->getSVal(CE, LCtx); SymbolRef ToPtr = RetVal.getAsSymbol(); if (!FromPtr || !ToPtr) return nullptr; @@ -1731,7 +1902,7 @@ // Default behavior. if (ProgramStateRef stateFree = - FreeMemAux(C, CE, state, 0, false, ReleasedAllocated)) { + FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) { ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1), UnknownVal(), stateFree); @@ -1755,20 +1926,23 @@ return nullptr; } -ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE){ +ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State) { + if (!State) + return nullptr; + if (CE->getNumArgs() < 2) return nullptr; - ProgramStateRef state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); const LocationContext *LCtx = C.getLocationContext(); - SVal count = state->getSVal(CE->getArg(0), LCtx); - SVal elementSize = state->getSVal(CE->getArg(1), LCtx); - SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize, + SVal count = State->getSVal(CE->getArg(0), LCtx); + SVal elementSize = State->getSVal(CE->getArg(1), LCtx); + SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize, svalBuilder.getContext().getSizeType()); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - return MallocMemAux(C, CE, TotalSize, zeroVal, state); + return MallocMemAux(C, CE, TotalSize, zeroVal, State); } LeakInfo Index: test/Analysis/malloc-annotations.c =================================================================== --- test/Analysis/malloc-annotations.c +++ test/Analysis/malloc-annotations.c @@ -37,13 +37,14 @@ void f2_realloc_0() { int *p = malloc(12); - realloc(p,0); - realloc(p,0); // expected-warning{{Attempt to free released memory}} + realloc(p, 12); + realloc(p, 12); // expected-warning{{Attempt to free released memory}} } void f2_realloc_1() { int *p = malloc(12); - int *q = realloc(p,0); // no-warning + int *q = realloc(p, 12); // no-warning + free(q); } // ownership attributes tests @@ -181,7 +182,7 @@ if (!p) return; // no-warning else - realloc(p,0); + realloc(p,0); // expected-warning{{Call to 'realloc()' has an allocation size of 0 bytes}} } @@ -203,8 +204,9 @@ void f7_realloc() { char *x = (char*) malloc(4); - realloc(x,0); + char *y = realloc(x, 4); x[0] = 'a'; // expected-warning{{Use of memory after it is freed}} + free(y); } void PR6123() { Index: test/Analysis/malloc.c =================================================================== --- test/Analysis/malloc.c +++ test/Analysis/malloc.c @@ -5,6 +5,7 @@ void clang_analyzer_eval(int); typedef __typeof(sizeof(int)) size_t; +void *alloca(size_t); void *malloc(size_t); void *valloc(size_t); void free(void *); @@ -32,13 +33,13 @@ void f2_realloc_0() { int *p = malloc(12); - realloc(p,0); - realloc(p,0); // expected-warning{{Attempt to free released memory}} + realloc(p, 12); + realloc(p, 12); // expected-warning{{Attempt to free released memory}} } void f2_realloc_1() { int *p = malloc(12); - int *q = realloc(p,0); // no-warning + int *q = realloc(p, 0); // expected-warning{{Call to 'realloc()' has an allocation size of 0 bytes}} } void reallocNotNullPtr(unsigned sizeIn) { @@ -67,11 +68,15 @@ } } +// FIXME: Now unix.Malloc warns about 'zero size allocation' when see zero +// allocation like realloc(p, 0); +// Commented until we decide how zero size realloc should be handled. +/* void reallocSizeZero1() { char *p = malloc(12); char *r = realloc(p, 0); if (!r) { - free(p); // expected-warning {{Attempt to free released memory}} + free(p); // FIXME:expected-warning {{Attempt to free released memory}} } else { free(r); } @@ -81,11 +86,11 @@ char *p = malloc(12); char *r = realloc(p, 0); if (!r) { - free(p); // expected-warning {{Attempt to free released memory}} + free(p); // FIXME:expected-warning {{Attempt to free released memory}} } else { free(r); } - free(p); // expected-warning {{Attempt to free released memory}} + free(p); // FIXME:expected-warning {{Attempt to free released memory}} } void reallocSizeZero3() { @@ -93,7 +98,7 @@ char *r = realloc(p, 0); free(r); } - +*/ void reallocSizeZero4() { char *r = realloc(0, 0); free(r); @@ -229,7 +234,7 @@ if (!p) return; // no-warning else - realloc(p,0); + realloc(p,0); // expected-warning{{Call to 'realloc()' has an allocation size of 0 bytes}} } @@ -257,7 +262,7 @@ void f7_realloc() { char *x = (char*) malloc(4); - realloc(x,0); + realloc(x, 4); x[0] = 'a'; // expected-warning{{Use of memory after it is freed}} } @@ -949,6 +954,79 @@ } // ---------------------------------------------------------------------------- +// Test zero-sized allocations. +void testMallocZero() { + char* foo = malloc(0); // expected-warning{{Call to 'malloc()' has an allocation size of 0 bytes}} + free(foo); +} + +void testMallocZeroNoWarn(size_t size) { + char* foo = malloc(size); // no-warning + free(foo); +} + +void testCalloc1Zero(void) { + char *foo = calloc(0, 42); // expected-warning{{Call to 'calloc()' has an allocation size of 0 bytes}} + free(foo); +} + +void testCalloc2Zero(void) { + char *foo = calloc(42, 0); // expected-warning{{Call to 'calloc()' has an allocation size of 0 bytes}} + free(foo); +} + +void testCallocZeroNoWarn(size_t nmemb, size_t size) { + char *foo = calloc(nmemb, size); // no-warning + free(foo); +} + +void testReallocZero(char *ptr) { + char *foo = realloc(ptr, 0); // expected-warning{{Call to 'realloc()' has an allocation size of 0 bytes}} + free(foo); +} + +void testReallocZeroNoWarn(char *ptr, size_t size) { + char *foo = realloc(ptr, size); // no-warning + free(foo); +} + +void testReallocfZero(char *ptr) { + char *foo = reallocf(ptr, 0); // expected-warning{{Call to 'reallocf()' has an allocation size of 0 bytes}} + free(foo); +} + +void testReallocfZeroNoWarn(char *ptr, size_t size) { + char *foo = reallocf(ptr, size); // no-warning + free(foo); +} + +void testAllocaZero() { + char *foo = alloca(0); // expected-warning{{Call to 'alloca()' has an allocation size of 0 bytes}} +} + +void testAllocaZeroNoWarn(size_t sz) { + char *foo = alloca(sz); // no-warning +} + +void testBuiltinAllocaZero() { + char *foo2 = __builtin_alloca(0); // expected-warning{{Call to '__builtin_alloca()' has an allocation size of 0 bytes}} +} + +void testBuiltinAllocaZeroNoWarn(size_t sz) { + char *foo2 = __builtin_alloca(sz); // no-warning +} + +void testVallocZero() { + char *foo = valloc(0); // expected-warning{{Call to 'valloc()' has an allocation size of 0 bytes}} + free(foo); +} + +void testVallocZeroNoWarn(size_t sz) { + char *foo = valloc(sz); // no-warning + free(foo); +} + +// ---------------------------------------------------------------------------- // Test the system library functions to which the pointer can escape. // This tests false positive suppression.