diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
| -rw-r--r-- | lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 507 | 
1 files changed, 375 insertions, 132 deletions
| diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index aee5a43048b9..0cf009407349 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -43,12 +43,15 @@ enum AllocationFamily {    AF_Malloc,    AF_CXXNew,    AF_CXXNewArray, -  AF_IfNameIndex +  AF_IfNameIndex, +  AF_Alloca  };  class RefState {    enum Kind { // Reference to allocated memory.                Allocated, +              // Reference to zero-allocated memory. +              AllocatedOfSizeZero,                // Reference to released/freed memory.                Released,                // The responsibility for freeing resources has transferred from @@ -61,8 +64,8 @@ class RefState {    };    const Stmt *S; -  unsigned K : 2; // Kind enum, but stored as a bitfield. -  unsigned Family : 30; // Rest of 32-bit word, currently just an allocation  +  unsigned K : 3; // Kind enum, but stored as a bitfield. +  unsigned Family : 29; // Rest of 32-bit word, currently just an allocation                           // family.    RefState(Kind k, const Stmt *s, unsigned family)  @@ -71,6 +74,7 @@ class RefState {    }  public:    bool isAllocated() const { return K == Allocated; } +  bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; }    bool isReleased() const { return K == Released; }    bool isRelinquished() const { return K == Relinquished; }    bool isEscaped() const { return K == Escaped; } @@ -86,6 +90,10 @@ public:    static RefState getAllocated(unsigned family, const Stmt *s) {      return RefState(Allocated, s, family);    } +  static RefState getAllocatedOfSizeZero(const RefState *RS) { +    return RefState(AllocatedOfSizeZero, RS->getStmt(), +                    RS->getAllocationFamily()); +  }    static RefState getReleased(unsigned family, const Stmt *s) {       return RefState(Released, s, family);    } @@ -106,6 +114,7 @@ public:      switch (static_cast<Kind>(K)) {  #define CASE(ID) case ID: OS << #ID; break;      CASE(Allocated) +    CASE(AllocatedOfSizeZero)      CASE(Released)      CASE(Relinquished)      CASE(Escaped) @@ -160,16 +169,16 @@ class MallocChecker : public Checker<check::DeadSymbols,  {  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_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.    enum CheckKind { -    CK_MallocPessimistic, -    CK_MallocOptimistic, +    CK_MallocChecker,      CK_NewDeleteChecker,      CK_NewDeleteLeaksChecker,      CK_MismatchedDeallocatorChecker, @@ -182,6 +191,8 @@ public:      MOK_Any    }; +  DefaultBool IsOptimistic; +    DefaultBool ChecksEnabled[CK_NumCheckKinds];    CheckName CheckNames[CK_NumCheckKinds]; @@ -216,11 +227,14 @@ private:    mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds];    mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds];    mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds]; +  mutable std::unique_ptr<BugType> BT_FreeAlloca[CK_NumCheckKinds];    mutable std::unique_ptr<BugType> BT_MismatchedDealloc;    mutable std::unique_ptr<BugType> 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<BugType> BT_UseZerroAllocated[CK_NumCheckKinds]; +  mutable IdentifierInfo *II_alloca, *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<uint64_t> KernelZeroFlagVal;    void initIdentifierInfo(ASTContext &C) const; @@ -252,22 +266,24 @@ private:                        MemoryOperationKind MemKind) const;    bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;    ///@} + +  /// \brief Perform a zero-allocation check. +  ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E, +                                        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 +297,8 @@ private:                         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,14 +312,19 @@ private:                               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;    bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; +  void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,  +                             const Stmt *S) const; +    bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;    /// Check if the function is known free memory, or if it is @@ -330,15 +352,20 @@ private:    /// Tells if a given family/call/symbol is tracked by the current checker.    /// Sets CheckKind to the kind of the checker responsible for this    /// family/call/symbol. -  Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const; +  Optional<CheckKind> getCheckIfTracked(AllocationFamily Family, +                                        bool IsALeakCheck = false) const;    Optional<CheckKind> getCheckIfTracked(CheckerContext &C, -                                        const Stmt *AllocDeallocStmt) const; -  Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const; +                                        const Stmt *AllocDeallocStmt, +                                        bool IsALeakCheck = false) const; +  Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,  +                                        bool IsALeakCheck = false) const;    ///@}    static bool SummarizeValue(raw_ostream &os, SVal V);    static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);    void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,                        const Expr *DeallocExpr) const; +  void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, +                        SourceRange Range) const;    void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,                                 const Expr *DeallocExpr, const RefState *RS,                                 SymbolRef Sym, bool OwnershipTransferred) const; @@ -352,6 +379,9 @@ private:    void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const; +  void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range, +                              SymbolRef Sym) const; +    /// Find the location of the allocation for Sym on the path leading to the    /// exploded node N.    LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, @@ -384,7 +414,7 @@ private:      MallocBugVisitor(SymbolRef S, bool isLeak = false)         : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {} -    virtual ~MallocBugVisitor() {} +    ~MallocBugVisitor() override {}      void Profile(llvm::FoldingSetNodeID &ID) const override {        static int X = 0; @@ -396,7 +426,9 @@ private:                              const Stmt *Stmt) {        // Did not track -> allocated. Other state (released) -> allocated.        return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && -              (S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated())); +              (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&  +              (!SPrev || !(SPrev->isAllocated() ||  +                           SPrev->isAllocatedOfSizeZero())));      }      inline bool isReleased(const RefState *S, const RefState *SPrev, @@ -422,7 +454,9 @@ private:        // check. If we have to handle more cases here, it might be cleaner just        // to track this extra bit in the state itself.        return ((!Stmt || !isa<CallExpr>(Stmt)) && -              (S && S->isAllocated()) && (SPrev && !SPrev->isAllocated())); +              (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && +              (SPrev && !(SPrev->isAllocated() || +                          SPrev->isAllocatedOfSizeZero())));      }      PathDiagnosticPiece *VisitNode(const ExplodedNode *N, @@ -497,6 +531,7 @@ public:  void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {    if (II_malloc)      return; +  II_alloca = &Ctx.Idents.get("alloca");    II_malloc = &Ctx.Idents.get("malloc");    II_free = &Ctx.Idents.get("free");    II_realloc = &Ctx.Idents.get("realloc"); @@ -517,6 +552,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {    if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any))      return true; +  if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any)) +    return true; +    if (isStandardNewDelete(FD, C))      return true; @@ -560,12 +598,17 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,        if (FunI == II_if_nameindex)          return true;      } + +    if (Family == AF_Alloca && CheckAlloc) { +      if (FunI == II_alloca) +        return true; +    }    }    if (Family != AF_Malloc)      return false; -  if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) { +  if (IsOptimistic && FD->hasAttrs()) {      for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {        OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();        if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { @@ -712,6 +755,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {          return;        if (CE->getNumArgs() < 3) {          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); +        if (CE->getNumArgs() == 1) +          State = ProcessZeroAllocation(C, CE, 0, State);        } else if (CE->getNumArgs() == 3) {          llvm::Optional<ProgramStateRef> MaybeState =            performKernelMalloc(CE, C, State); @@ -731,31 +776,43 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {        if (CE->getNumArgs() < 1)          return;        State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); +      State = ProcessZeroAllocation(C, CE, 0, State);      } else if (FunI == II_realloc) { -      State = ReallocMem(C, CE, false); +      State = ReallocMem(C, CE, false, State); +      State = ProcessZeroAllocation(C, CE, 1, State);      } else if (FunI == II_reallocf) { -      State = ReallocMem(C, CE, true); +      State = ReallocMem(C, CE, true, State); +      State = ProcessZeroAllocation(C, CE, 1, State);      } else if (FunI == II_calloc) { -      State = CallocMem(C, CE); +      State = CallocMem(C, CE, State); +      State = ProcessZeroAllocation(C, CE, 0, State); +      State = ProcessZeroAllocation(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) { +      State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, +                           AF_Alloca); +      State = ProcessZeroAllocation(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         // CXXDeleteExpr.        OverloadedOperatorKind K = FD->getOverloadedOperator(); -      if (K == OO_New) +      if (K == OO_New) {          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,                               AF_CXXNew); -      else if (K == OO_Array_New) +        State = ProcessZeroAllocation(C, CE, 0, State); +      } +      else if (K == OO_Array_New) {          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,                               AF_CXXNewArray); +        State = ProcessZeroAllocation(C, CE, 0, State); +      }        else if (K == OO_Delete || K == OO_Array_Delete)          State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);        else @@ -770,19 +827,18 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {      }    } -  if (ChecksEnabled[CK_MallocOptimistic] || -      ChecksEnabled[CK_MismatchedDeallocatorChecker]) { +  if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) {      // Check all the attributes, if there are any.      // There can be multiple of these attributes.      if (FD->hasAttrs())        for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {          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 +846,68 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {    C.addTransition(State);  } +// Performs a 0-sized allocations check. +ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C, +                                               const Expr *E, +                                               const unsigned AllocationSizeArg, +                                               ProgramStateRef State) const { +  if (!State) +    return nullptr; + +  const Expr *Arg = nullptr; + +  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { +    Arg = CE->getArg(AllocationSizeArg); +  } +  else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { +    if (NE->isArray()) +      Arg = NE->getArraySize(); +    else +      return State; +  } +  else +    llvm_unreachable("not a CallExpr or CXXNewExpr"); + +  assert(Arg); + +  Optional<DefinedSVal> DefArgVal =  +      State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>(); + +  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<DefinedSVal>(); + +  std::tie(TrueState, FalseState) =  +      State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero)); + +  if (TrueState && !FalseState) { +    SVal retVal = State->getSVal(E, C.getLocationContext()); +    SymbolRef Sym = retVal.getAsLocSymbol(); +    if (!Sym) +      return State; + +    const RefState *RS = State->get<RegionState>(Sym); +    if (!RS) +      return State; // TODO: change to assert(RS); after realloc() will  +                    // guarantee have a RegionState attached. + +    if (!RS->isAllocated()) +      return State; + +    return TrueState->set<RegionState>(Sym, +                                       RefState::getAllocatedOfSizeZero(RS)); +  } + +  // 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()) { @@ -849,6 +967,7 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,    // existing binding.    State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray                                                              : AF_CXXNew); +  State = ProcessZeroAllocation(C, NE, 0, State);    C.addTransition(State);  } @@ -919,15 +1038,31 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,  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 +1070,8 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,                                             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 +1113,9 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,                                                      const Expr *E,                                                      ProgramStateRef State,                                                      AllocationFamily Family) { +  if (!State) +    return nullptr; +    // Get the return value.    SVal retVal = State->getSVal(E, C.getLocationContext()); @@ -992,11 +1132,14 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,  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 +1154,18 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,  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);  } @@ -1065,6 +1211,9 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,      if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any))        return AF_IfNameIndex; +    if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any)) +      return AF_Alloca; +      return AF_None;    } @@ -1129,6 +1278,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,      case AF_CXXNew: os << "'new'"; return;      case AF_CXXNewArray: os << "'new[]'"; return;      case AF_IfNameIndex: os << "'if_nameindex()'"; return; +    case AF_Alloca:      case AF_None: llvm_unreachable("not a deallocation expression");    }  } @@ -1140,7 +1290,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os,      case AF_CXXNew: os << "'delete'"; return;      case AF_CXXNewArray: os << "'delete[]'"; return;      case AF_IfNameIndex: os << "'if_freenameindex()'"; return; -    case AF_None: llvm_unreachable("suspicious AF_None argument"); +    case AF_Alloca: +    case AF_None: llvm_unreachable("suspicious argument");    }  } @@ -1152,6 +1303,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,                                            bool &ReleasedAllocated,                                            bool ReturnsNullOnFailure) const { +  if (!State) +    return nullptr; +    SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext());    if (!ArgVal.getAs<DefinedOrUnknownSVal>())      return nullptr; @@ -1191,8 +1345,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,    const MemSpaceRegion *MS = R->getMemorySpace(); -  // Parameters, locals, statics, globals, and memory returned by alloca()  -  // shouldn't be freed. +  // Parameters, locals, statics, globals, and memory returned by  +  // __builtin_alloca() shouldn't be freed.    if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {      // FIXME: at the time this code was written, malloc() regions were      // represented by conjured symbols, which are all in UnknownSpaceRegion. @@ -1201,8 +1355,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,      // Of course, free() can work on memory allocated outside the current      // function, so UnknownSpaceRegion is always a possibility.      // False negatives are better than false positives. -     -    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + +    if (isa<AllocaRegion>(R)) +      ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); +    else +      ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); +      return nullptr;    } @@ -1218,6 +1376,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,    if (RsBase) { +    // Memory returned by alloca() shouldn't be freed. +    if (RsBase->getAllocationFamily() == AF_Alloca) { +      ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); +      return nullptr; +    } +      // Check for double free first.      if ((RsBase->isReleased() || RsBase->isRelinquished()) &&          !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { @@ -1227,7 +1391,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,      // If the pointer is allocated or escaped, but we are now trying to free it,      // check that the call to free is proper. -    } else if (RsBase->isAllocated() || RsBase->isEscaped()) { +    } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||  +               RsBase->isEscaped()) {        // Check if an expected deallocation function matches the real one.        bool DeallocMatchesAlloc = @@ -1252,7 +1417,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,      }    } -  ReleasedAllocated = (RsBase != nullptr) && RsBase->isAllocated(); +  ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||  +                                              RsBase->isAllocatedOfSizeZero());    // Clean out the info on previous call to free return info.    State = State->remove<FreeReturnValue>(SymBase); @@ -1281,21 +1447,26 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,  }  Optional<MallocChecker::CheckKind> -MallocChecker::getCheckIfTracked(AllocationFamily Family) const { +MallocChecker::getCheckIfTracked(AllocationFamily Family, +                                 bool IsALeakCheck) const {    switch (Family) {    case AF_Malloc: +  case AF_Alloca:    case AF_IfNameIndex: { -    if (ChecksEnabled[CK_MallocOptimistic]) { -      return CK_MallocOptimistic; -    } else if (ChecksEnabled[CK_MallocPessimistic]) { -      return CK_MallocPessimistic; -    } +    if (ChecksEnabled[CK_MallocChecker]) +      return CK_MallocChecker; +      return Optional<MallocChecker::CheckKind>();    }    case AF_CXXNew:    case AF_CXXNewArray: { -    if (ChecksEnabled[CK_NewDeleteChecker]) { -      return CK_NewDeleteChecker; +    if (IsALeakCheck) { +      if (ChecksEnabled[CK_NewDeleteLeaksChecker]) +        return CK_NewDeleteLeaksChecker; +    }  +    else { +      if (ChecksEnabled[CK_NewDeleteChecker]) +        return CK_NewDeleteChecker;      }      return Optional<MallocChecker::CheckKind>();    } @@ -1308,16 +1479,18 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family) const {  Optional<MallocChecker::CheckKind>  MallocChecker::getCheckIfTracked(CheckerContext &C, -                                 const Stmt *AllocDeallocStmt) const { -  return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt)); +                                 const Stmt *AllocDeallocStmt, +                                 bool IsALeakCheck) const { +  return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt), +                           IsALeakCheck);  }  Optional<MallocChecker::CheckKind> -MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const { - +MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, +                                 bool IsALeakCheck) const {    const RefState *RS = C.getState()->get<RegionState>(Sym);    assert(RS); -  return getCheckIfTracked(RS->getAllocationFamily()); +  return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck);  }  bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -1411,8 +1584,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,                                    SourceRange Range,                                     const Expr *DeallocExpr) const { -  if (!ChecksEnabled[CK_MallocOptimistic] && -      !ChecksEnabled[CK_MallocPessimistic] && +  if (!ChecksEnabled[CK_MallocChecker] &&        !ChecksEnabled[CK_NewDeleteChecker])      return; @@ -1433,23 +1605,19 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,      while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))        MR = ER->getSuperRegion(); -    if (MR && isa<AllocaRegion>(MR)) -      os << "Memory allocated by alloca() should not be deallocated"; -    else { -      os << "Argument to "; -      if (!printAllocDeallocName(os, C, DeallocExpr)) -        os << "deallocator"; - -      os << " is "; -      bool Summarized = MR ? SummarizeRegion(os, MR)  -                           : SummarizeValue(os, ArgVal); -      if (Summarized) -        os << ", which is not memory allocated by "; -      else -        os << "not memory allocated by "; +    os << "Argument to "; +    if (!printAllocDeallocName(os, C, DeallocExpr)) +      os << "deallocator"; -      printExpectedAllocName(os, C, DeallocExpr); -    } +    os << " is "; +    bool Summarized = MR ? SummarizeRegion(os, MR)  +                         : SummarizeValue(os, ArgVal); +    if (Summarized) +      os << ", which is not memory allocated by "; +    else +      os << "not memory allocated by "; + +    printExpectedAllocName(os, C, DeallocExpr);      BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N);      R->markInteresting(MR); @@ -1458,6 +1626,31 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,    }  } +void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,  +                                     SourceRange Range) const { + +  Optional<MallocChecker::CheckKind> CheckKind; + +  if (ChecksEnabled[CK_MallocChecker]) +    CheckKind = CK_MallocChecker; +  else if (ChecksEnabled[CK_MismatchedDeallocatorChecker]) +    CheckKind = CK_MismatchedDeallocatorChecker; +  else +    return; + +  if (ExplodedNode *N = C.generateSink()) { +    if (!BT_FreeAlloca[*CheckKind]) +      BT_FreeAlloca[*CheckKind].reset( +          new BugType(CheckNames[*CheckKind], "Free alloca()", "Memory Error")); + +    BugReport *R = new BugReport(*BT_FreeAlloca[*CheckKind],  +                 "Memory allocated by alloca() should not be deallocated", N); +    R->markInteresting(ArgVal.getAsRegion()); +    R->addRange(Range); +    C.emitReport(R); +  } +} +  void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,                                               SourceRange Range,                                              const Expr *DeallocExpr,  @@ -1517,8 +1710,8 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,                                       SourceRange Range, const Expr *DeallocExpr,                                       const Expr *AllocExpr) const { -  if (!ChecksEnabled[CK_MallocOptimistic] && -      !ChecksEnabled[CK_MallocPessimistic] && + +  if (!ChecksEnabled[CK_MallocChecker] &&        !ChecksEnabled[CK_NewDeleteChecker])      return; @@ -1573,8 +1766,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,  void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,                                         SymbolRef Sym) const { -  if (!ChecksEnabled[CK_MallocOptimistic] && -      !ChecksEnabled[CK_MallocPessimistic] && +  if (!ChecksEnabled[CK_MallocChecker] &&        !ChecksEnabled[CK_NewDeleteChecker])      return; @@ -1601,8 +1793,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,                                       bool Released, SymbolRef Sym,                                        SymbolRef PrevSym) const { -  if (!ChecksEnabled[CK_MallocOptimistic] && -      !ChecksEnabled[CK_MallocPessimistic] && +  if (!ChecksEnabled[CK_MallocChecker] &&        !ChecksEnabled[CK_NewDeleteChecker])      return; @@ -1637,7 +1828,6 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {    Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);    if (!CheckKind.hasValue())      return; -  assert(*CheckKind == CK_NewDeleteChecker && "invalid check kind");    if (ExplodedNode *N = C.generateSink()) {      if (!BT_DoubleDelete) @@ -1653,16 +1843,49 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {    }  } +void MallocChecker::ReportUseZeroAllocated(CheckerContext &C, +                                           SourceRange Range, +                                           SymbolRef Sym) const { + +  if (!ChecksEnabled[CK_MallocChecker] && +      !ChecksEnabled[CK_NewDeleteChecker]) +    return; + +  Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + +  if (!CheckKind.hasValue()) +    return; + +  if (ExplodedNode *N = C.generateSink()) { +    if (!BT_UseZerroAllocated[*CheckKind]) +      BT_UseZerroAllocated[*CheckKind].reset(new BugType( +          CheckNames[*CheckKind], "Use of zero allocated", "Memory Error")); + +    BugReport *R = new BugReport(*BT_UseZerroAllocated[*CheckKind], +                                 "Use of zero-allocated memory", N); + +    R->addRange(Range); +    if (Sym) { +      R->markInteresting(Sym); +      R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); +    } +    C.emitReport(R); +  } +} +  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<DefinedOrUnknownSVal>())      return nullptr;    DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>(); @@ -1670,7 +1893,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,    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 +1901,20 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,      return nullptr;    // Get the value of the size argument. -  SVal Arg1ValG = state->getSVal(Arg1, LCtx); +  SVal Arg1ValG = State->getSVal(Arg1, LCtx);    if (!Arg1ValG.getAs<DefinedOrUnknownSVal>())      return nullptr;    DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>();    // 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 +1934,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,    // 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 +1954,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,    // 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 +1978,23 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,    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 @@ -1801,9 +2027,11 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,          }        } -    // Allocation node, is the last node in the current context in which the -    // symbol was tracked. -    if (N->getLocationContext() == LeakContext) +    // Allocation node, is the last node in the current or parent context in +    // which the symbol was tracked. +    const LocationContext *NContext = N->getLocationContext(); +    if (NContext == LeakContext || +        NContext->isParentOf(LeakContext))        AllocNode = N;      N = N->pred_empty() ? nullptr : *(N->pred_begin());    } @@ -1814,23 +2042,22 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,  void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,                                 CheckerContext &C) const { -  if (!ChecksEnabled[CK_MallocOptimistic] && -      !ChecksEnabled[CK_MallocPessimistic] && +  if (!ChecksEnabled[CK_MallocChecker] &&        !ChecksEnabled[CK_NewDeleteLeaksChecker])      return;    const RefState *RS = C.getState()->get<RegionState>(Sym);    assert(RS && "cannot leak an untracked symbol");    AllocationFamily Family = RS->getAllocationFamily(); -  Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); -  if (!CheckKind.hasValue()) + +  if (Family == AF_Alloca)      return; -  // Special case for new and new[]; these are controlled by a separate checker -  // flag so that they can be selectively disabled. -  if (Family == AF_CXXNew || Family == AF_CXXNewArray) -    if (!ChecksEnabled[CK_NewDeleteLeaksChecker]) -      return; +  Optional<MallocChecker::CheckKind> +      CheckKind = getCheckIfTracked(Family, true); + +  if (!CheckKind.hasValue()) +    return;    assert(N);    if (!BT_Leak[*CheckKind]) { @@ -1893,7 +2120,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,    SmallVector<SymbolRef, 2> Errors;    for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {      if (SymReaper.isDead(I->first)) { -      if (I->second.isAllocated()) +      if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero())          Errors.push_back(I->first);        // Remove the dead symbol from the map.        RS = F.remove(RS, I->first); @@ -1949,8 +2176,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,        return;      ASTContext &Ctx = C.getASTContext(); -    if ((ChecksEnabled[CK_MallocOptimistic] || -         ChecksEnabled[CK_MallocPessimistic]) && +    if (ChecksEnabled[CK_MallocChecker] &&          (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) ||           isCMemFunction(FD, Ctx, AF_IfNameIndex,                          MemoryOperationKind::MOK_Free))) @@ -2062,6 +2288,15 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,    return false;  } +void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, +                                          const Stmt *S) const { +  assert(Sym); +  const RefState *RS = C.getState()->get<RegionState>(Sym); + +  if (RS && RS->isAllocatedOfSizeZero()) +    ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym); +} +  bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {    if (isReleased(Sym, C)) { @@ -2075,8 +2310,10 @@ bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {  void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,                                    CheckerContext &C) const {    SymbolRef Sym = l.getLocSymbolInBase(); -  if (Sym) +  if (Sym) {      checkUseAfterFree(Sym, C, S); +    checkUseZeroAllocated(Sym, C, S); +  }  }  // If a symbolic region is assumed to NULL (or another constant), stop tracking @@ -2320,7 +2557,8 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,        continue;      if (const RefState *RS = State->get<RegionState>(sym)) { -      if (RS->isAllocated() && CheckRefState(RS)) { +      if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && +          CheckRefState(RS)) {          State = State->remove<RegionState>(sym);          State = State->set<RegionState>(sym, RefState::getEscaped(RS));        } @@ -2443,6 +2681,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,        const RefState *RefS = State->get<RegionState>(I.getKey());        AllocationFamily Family = RefS->getAllocationFamily();        Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); +      if (!CheckKind.hasValue()) +         CheckKind = getCheckIfTracked(Family, true);        I.getKey()->dumpToStream(Out);        Out << " : "; @@ -2457,6 +2697,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,  void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {    registerCStringCheckerBasic(mgr);    MallocChecker *checker = mgr.registerChecker<MallocChecker>(); +  checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( +      "Optimistic", false, checker);    checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;    checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =        mgr.getCurrentCheckName(); @@ -2470,11 +2712,12 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {    void ento::register##name(CheckerManager &mgr) {                             \      registerCStringCheckerBasic(mgr);                                          \      MallocChecker *checker = mgr.registerChecker<MallocChecker>();             \ +    checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(         \ +        "Optimistic", false, checker);                                         \      checker->ChecksEnabled[MallocChecker::CK_##name] = true;                   \      checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \    } -REGISTER_CHECKER(MallocPessimistic) -REGISTER_CHECKER(MallocOptimistic) +REGISTER_CHECKER(MallocChecker)  REGISTER_CHECKER(NewDeleteChecker)  REGISTER_CHECKER(MismatchedDeallocatorChecker) | 
