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_ZerroAllocation[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,24 @@ MemoryOperationKind MemKind) const; bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; ///@} + + /// \brief Perform a zero-allocation check. + ProgramStateRef ZeroAllocationCheck(CheckerContext &C, const CallExpr *CE, + const unsigned AllocationSizeArg, + 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 +286,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 +301,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 +345,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 +507,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 +557,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 +723,9 @@ if (FunI == II_malloc) { if (CE->getNumArgs() < 1) return; + else if (CE->getNumArgs() == 1) + State = ZeroAllocationCheck(C, CE, 0, State); + if (CE->getNumArgs() < 3) { State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); } else if (CE->getNumArgs() == 3) { @@ -731,20 +747,30 @@ if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = ZeroAllocationCheck(C, CE, 0, State); } else if (FunI == II_realloc) { - State = ReallocMem(C, CE, false); + State = ReallocMem(C, CE, false, State); + ParentMap &PM = C.getLocationContext()->getParentMap(); + if (PM.isConsumedExpr(CE)) + State = ZeroAllocationCheck(C, CE, 1, State); } else if (FunI == II_reallocf) { - State = ReallocMem(C, CE, true); + State = ReallocMem(C, CE, true, State); + ParentMap &PM = C.getLocationContext()->getParentMap(); + if (PM.isConsumedExpr(CE)) + State = ZeroAllocationCheck(C, CE, 1, State); } else if (FunI == II_calloc) { - State = CallocMem(C, CE); + State = CallocMem(C, CE, State); + State = ZeroAllocationCheck(C, CE, 0, State); + State = ZeroAllocationCheck(C, CE, 1, 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 = ZeroAllocationCheck(C, CE, 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 +804,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 +816,41 @@ C.addTransition(State); } +// Performs a 0-sized allocations check. +ProgramStateRef MallocChecker::ZeroAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned AllocationSizeArg, + ProgramStateRef State) const { + if (!State) + return nullptr; + + const Expr *Arg = CE->getArg(AllocationSizeArg); + + Optional DefArgVal = + State->getSVal(Arg, C.getLocationContext()).getAs(); + + if (!DefArgVal) + return State; + + // Check if the allocation size is 0. + ProgramStateRef TrueState, FalseState; + SValBuilder &SvalBuilder = C.getSValBuilder(); + DefinedSVal Zero = + SvalBuilder.makeZeroVal(Arg->getType()).castAs(); + + std::tie(TrueState, FalseState) = + State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero)); + + if (TrueState && !FalseState) { + ReportZeroByteAllocation(C, FalseState, Arg, CE); + return nullptr; + } + + // Assume the value is non-zero going forward. + assert(FalseState); + return FalseState; +} + static QualType getDeepPointeeType(QualType T) { QualType Result = T, PointeeType = T->getPointeeType(); while (!PointeeType.isNull()) { @@ -919,15 +980,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 +1012,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 +1055,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 +1074,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 +1096,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 +1240,9 @@ bool &ReleasedAllocated, bool ReturnsNullOnFailure) const { + if (!State) + return nullptr; + SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext()); if (!ArgVal.getAs()) return nullptr; @@ -1407,6 +1498,38 @@ } } +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_ZerroAllocation[*CheckKind]) + BT_ZerroAllocation[*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_ZerroAllocation[*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 +1778,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 +1796,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 +1804,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 +1837,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 +1857,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 +1881,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: lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -30,7 +30,7 @@ namespace { class UnixAPIChecker : public Checker< check::PreStmt > { - mutable std::unique_ptr BT_open, BT_pthreadOnce, BT_mallocZero; + mutable std::unique_ptr BT_open, BT_pthreadOnce; mutable Optional Val_O_CREAT; public: @@ -38,25 +38,10 @@ void CheckOpen(CheckerContext &C, const CallExpr *CE) const; void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; - void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; - void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; - void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; - void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const; - void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; - void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, const CallExpr *) const; private: - bool ReportZeroByteAllocation(CheckerContext &C, - ProgramStateRef falseState, - const Expr *arg, - const char *fn_name) const; - void BasicAllocationCheck(CheckerContext &C, - const CallExpr *CE, - const unsigned numArgs, - const unsigned sizeArg, - const char *fn) const; void LazyInitialize(std::unique_ptr &BT, const char *name) const { if (BT) return; @@ -206,144 +191,6 @@ } //===----------------------------------------------------------------------===// -// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" -// with allocation size 0 -//===----------------------------------------------------------------------===// -// FIXME: Eventually these should be rolled into the MallocChecker, but right now -// they're more basic and valuable for widespread use. - -// Returns true if we try to do a zero byte allocation, false otherwise. -// Fills in trueState and falseState. -static bool IsZeroByteAllocation(ProgramStateRef state, - const SVal argVal, - ProgramStateRef *trueState, - ProgramStateRef *falseState) { - std::tie(*trueState, *falseState) = - state->assume(argVal.castAs()); - - return (*falseState && !*trueState); -} - -// Generates an error report, indicating that the function whose name is given -// will perform a zero byte allocation. -// Returns false if an error occurred, true otherwise. -bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, - ProgramStateRef falseState, - const Expr *arg, - const char *fn_name) const { - ExplodedNode *N = C.generateSink(falseState); - if (!N) - return false; - - LazyInitialize(BT_mallocZero, - "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); - - SmallString<256> S; - llvm::raw_svector_ostream os(S); - os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; - BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); - - report->addRange(arg->getSourceRange()); - bugreporter::trackNullOrUndefValue(N, arg, *report); - C.emitReport(report); - - return true; -} - -// Does a basic check for 0-sized allocations suitable for most of the below -// functions (modulo "calloc") -void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, - const CallExpr *CE, - const unsigned numArgs, - const unsigned sizeArg, - const char *fn) const { - // Sanity check for the correct number of arguments - if (CE->getNumArgs() != numArgs) - return; - - // Check if the allocation size is 0. - ProgramStateRef state = C.getState(); - ProgramStateRef trueState = nullptr, falseState = nullptr; - const Expr *arg = CE->getArg(sizeArg); - SVal argVal = state->getSVal(arg, C.getLocationContext()); - - if (argVal.isUnknownOrUndef()) - return; - - // Is the value perfectly constrained to zero? - if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { - (void) ReportZeroByteAllocation(C, falseState, arg, fn); - return; - } - // Assume the value is non-zero going forward. - assert(trueState); - if (trueState != state) - C.addTransition(trueState); -} - -void UnixAPIChecker::CheckCallocZero(CheckerContext &C, - const CallExpr *CE) const { - unsigned int nArgs = CE->getNumArgs(); - if (nArgs != 2) - return; - - ProgramStateRef state = C.getState(); - 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; - } - - if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { - if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) - return; - else if (i == 0) - continue; - else - return; - } - } - - // Assume the value is non-zero going forward. - assert(trueState); - if (trueState != state) - C.addTransition(trueState); -} - -void UnixAPIChecker::CheckMallocZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 1, 0, "malloc"); -} - -void UnixAPIChecker::CheckReallocZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 2, 1, "realloc"); -} - -void UnixAPIChecker::CheckReallocfZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 2, 1, "reallocf"); -} - -void UnixAPIChecker::CheckAllocaZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 1, 0, "alloca"); -} - -void UnixAPIChecker::CheckVallocZero(CheckerContext &C, - const CallExpr *CE) const { - BasicAllocationCheck(C, CE, 1, 0, "valloc"); -} - - -//===----------------------------------------------------------------------===// // Central dispatch function. //===----------------------------------------------------------------------===// @@ -361,12 +208,6 @@ llvm::StringSwitch(FName) .Case("open", &UnixAPIChecker::CheckOpen) .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) - .Case("calloc", &UnixAPIChecker::CheckCallocZero) - .Case("malloc", &UnixAPIChecker::CheckMallocZero) - .Case("realloc", &UnixAPIChecker::CheckReallocZero) - .Case("reallocf", &UnixAPIChecker::CheckReallocfZero) - .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero) - .Case("valloc", &UnixAPIChecker::CheckVallocZero) .Default(nullptr); if (SC) Index: test/Analysis/malloc-annotations.c =================================================================== --- test/Analysis/malloc-annotations.c +++ test/Analysis/malloc-annotations.c @@ -43,7 +43,12 @@ 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 f2_realloc_2() { + int *p = malloc(12); + realloc(p,0); // no-warning } // ownership attributes tests 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 *); @@ -38,7 +39,7 @@ void f2_realloc_1() { int *p = malloc(12); - int *q = realloc(p,0); // no-warning + realloc(p,0); // no-warning } void reallocNotNullPtr(unsigned sizeIn) { @@ -69,38 +70,17 @@ void reallocSizeZero1() { char *p = malloc(12); - char *r = realloc(p, 0); - if (!r) { - free(p); // expected-warning {{Attempt to free released memory}} - } else { - free(r); - } + realloc(p, 0); } void reallocSizeZero2() { char *p = malloc(12); - char *r = realloc(p, 0); - if (!r) { - free(p); // expected-warning {{Attempt to free released memory}} - } else { - free(r); - } + realloc(p, 0); free(p); // expected-warning {{Attempt to free released memory}} } void reallocSizeZero3() { - char *p = malloc(12); - char *r = realloc(p, 0); - free(r); -} - -void reallocSizeZero4() { - char *r = realloc(0, 0); - free(r); -} - -void reallocSizeZero5() { - char *r = realloc(0, 0); + realloc(0, 0); } void reallocPtrZero1() { @@ -949,6 +929,115 @@ } // ---------------------------------------------------------------------------- +// 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 testMallocPathZero(int i) { + if (!i) { + char* foo = malloc(i); // expected-warning{{Call to 'malloc()' has an allocation size of 0 bytes}} + free(foo); + } +} + +// Perform inline defensive check. +void idc(int i) { + if (i) + ; +} +void testMallocIdc(int i) { + idc(i); + char* foo = malloc(i); // no-warning + free(foo); +} + +void testCallocZero(void) { + char *foo = calloc(0, 0); // expected-warning{{Call to 'calloc()' has an allocation size of 0 bytes}} + 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 testCalloc1Unknown2Zero(int *i, int j) { + char *foo = calloc(*(i+j), 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 testReallocZeroNoWarn1(char *ptr) { + realloc(ptr, 0); // no-warning +} + +void testReallocZeroNoWarn2(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 testReallocfZeroNoWarn1(char *ptr) { + reallocf(ptr, 0); // no-warning +} + +void testReallocfZeroNoWarn2(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. Index: test/Analysis/unix-fns.c =================================================================== --- test/Analysis/unix-fns.c +++ test/Analysis/unix-fns.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,unix.API,osx.API %s -analyzer-store=region -analyzer-output=plist -analyzer-eagerly-assume -analyzer-config faux-bodies=true -analyzer-config path-diagnostics-alternate=false -fblocks -verify -o %t.plist +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,unix.API,osx.API,unix.Malloc %s -analyzer-store=region -analyzer-output=plist -analyzer-eagerly-assume -analyzer-config faux-bodies=true -analyzer-config path-diagnostics-alternate=false -fblocks -verify -o %t.plist // RUN: FileCheck --input-file=%t.plist %s // RUN: mkdir -p %t.dir -// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.API,osx.API -analyzer-output=html -analyzer-config faux-bodies=true -fblocks -o %t.dir %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.API,osx.API,unix.Malloc -analyzer-output=html -analyzer-config faux-bodies=true -fblocks -o %t.dir %s // RUN: rm -fR %t.dir struct _opaque_pthread_once_t { long __sig; @@ -79,7 +79,7 @@ // PR 2899 - warn of zero-sized allocations to malloc(). void pr2899() { - char* foo = malloc(0); // expected-warning{{Call to 'malloc' has an allocation size of 0 bytes}} + char* foo = malloc(0); // expected-warning{{Call to 'malloc()' has an allocation size of 0 bytes}} for (unsigned i = 0; i < 100; i++) { foo[i] = 0; } @@ -91,13 +91,13 @@ } } void test_calloc(void) { - char *foo = calloc(0, 42); // expected-warning{{Call to 'calloc' has an allocation size of 0 bytes}} + char *foo = calloc(0, 42); // expected-warning{{Call to 'calloc()' has an allocation size of 0 bytes}} for (unsigned i = 0; i < 100; i++) { foo[i] = 0; } } void test_calloc2(void) { - char *foo = calloc(42, 0); // expected-warning{{Call to 'calloc' has an allocation size of 0 bytes}} + char *foo = calloc(42, 0); // expected-warning{{Call to 'calloc()' has an allocation size of 0 bytes}} for (unsigned i = 0; i < 100; i++) { foo[i] = 0; } @@ -109,13 +109,13 @@ } } void test_realloc(char *ptr) { - char *foo = realloc(ptr, 0); // expected-warning{{Call to 'realloc' has an allocation size of 0 bytes}} + char *foo = realloc(ptr, 0); // expected-warning{{Call to 'realloc()' has an allocation size of 0 bytes}} for (unsigned i = 0; i < 100; i++) { foo[i] = 0; } } void test_reallocf(char *ptr) { - char *foo = reallocf(ptr, 0); // expected-warning{{Call to 'reallocf' has an allocation size of 0 bytes}} + char *foo = reallocf(ptr, 0); // expected-warning{{Call to 'reallocf()' has an allocation size of 0 bytes}} for (unsigned i = 0; i < 100; i++) { foo[i] = 0; } @@ -133,7 +133,7 @@ } } void test_alloca() { - char *foo = alloca(0); // expected-warning{{Call to 'alloca' has an allocation size of 0 bytes}} + char *foo = alloca(0); // expected-warning{{Call to 'alloca()' has an allocation size of 0 bytes}} for(unsigned i = 0; i < 100; i++) { foo[i] = 0; } @@ -145,7 +145,7 @@ } } void test_builtin_alloca() { - char *foo2 = __builtin_alloca(0); // expected-warning{{Call to 'alloca' has an allocation size of 0 bytes}} + char *foo2 = __builtin_alloca(0); // expected-warning{{Call to '__builtin_alloca()' has an allocation size of 0 bytes}} for(unsigned i = 0; i < 100; i++) { foo2[i] = 0; } @@ -157,7 +157,7 @@ } } void test_valloc() { - char *foo = valloc(0); // expected-warning{{Call to 'valloc' has an allocation size of 0 bytes}} + char *foo = valloc(0); // expected-warning{{Call to 'valloc()' has an allocation size of 0 bytes}} for(unsigned i = 0; i < 100; i++) { foo[i] = 0; } @@ -706,14 +706,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'malloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'malloc()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'malloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'malloc()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'malloc' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'malloc()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextpr2899 // CHECK-NEXT: issue_hash1 @@ -786,14 +786,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'calloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'calloc()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'calloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'calloc()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'calloc' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'calloc()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_calloc // CHECK-NEXT: issue_hash1 @@ -866,14 +866,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'calloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'calloc()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'calloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'calloc()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'calloc' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'calloc()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_calloc2 // CHECK-NEXT: issue_hash1 @@ -946,14 +946,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'realloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'realloc()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'realloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'realloc()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'realloc' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'realloc()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_realloc // CHECK-NEXT: issue_hash1 @@ -1026,14 +1026,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'reallocf' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'reallocf()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'reallocf' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'reallocf()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'reallocf' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'reallocf()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_reallocf // CHECK-NEXT: issue_hash1 @@ -1106,14 +1106,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'alloca' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'alloca()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'alloca' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'alloca()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'alloca' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'alloca()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_alloca // CHECK-NEXT: issue_hash1 @@ -1186,14 +1186,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'alloca' has an allocation size of 0 bytes +// CHECK-NEXT: Call to '__builtin_alloca()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'alloca' has an allocation size of 0 bytes +// CHECK-NEXT: Call to '__builtin_alloca()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'alloca' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to '__builtin_alloca()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_builtin_alloca // CHECK-NEXT: issue_hash1 @@ -1266,14 +1266,14 @@ // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Call to 'valloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'valloc()' has an allocation size of 0 bytes // CHECK-NEXT: message -// CHECK-NEXT: Call to 'valloc' has an allocation size of 0 bytes +// CHECK-NEXT: Call to 'valloc()' has an allocation size of 0 bytes // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionCall to 'valloc' has an allocation size of 0 bytes -// CHECK-NEXT: categoryUnix API -// CHECK-NEXT: typeUndefined allocation of 0 bytes (CERT MEM04-C; CWE-131) +// CHECK-NEXT: descriptionCall to 'valloc()' has an allocation size of 0 bytes +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeZero allocation // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contexttest_valloc // CHECK-NEXT: issue_hash1