diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 | 
| commit | 486754660bb926339aefcf012a3f848592babb8b (patch) | |
| tree | ecdbc446c9876f4f120f701c243373cd3cb43db3 /lib/StaticAnalyzer/Checkers/MallocChecker.cpp | |
| parent | 55e6d896ad333f07bb3b1ba487df214fc268a4ab (diff) | |
Notes
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
| -rw-r--r-- | lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 328 | 
1 files changed, 253 insertions, 75 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 851114004b96..8f07f413e81f 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -30,6 +30,7 @@  #include "llvm/ADT/STLExtras.h"  #include "llvm/ADT/SmallString.h"  #include "llvm/ADT/StringExtras.h" +#include "AllocationState.h"  #include <climits>  #include <utility> @@ -45,7 +46,8 @@ enum AllocationFamily {    AF_CXXNew,    AF_CXXNewArray,    AF_IfNameIndex, -  AF_Alloca +  AF_Alloca, +  AF_InnerBuffer  };  class RefState { @@ -134,10 +136,10 @@ enum ReallocPairKind {  };  /// \class ReallocPair -/// \brief Stores information about the symbol being reallocated by a call to +/// Stores information about the symbol being reallocated by a call to  /// 'realloc' to allow modeling failed reallocation later in the path.  struct ReallocPair { -  // \brief The symbol which realloc reallocated. +  // The symbol which realloc reallocated.    SymbolRef ReallocatedSym;    ReallocPairKind Kind; @@ -162,6 +164,7 @@ class MallocChecker : public Checker<check::DeadSymbols,                                       check::PreCall,                                       check::PostStmt<CallExpr>,                                       check::PostStmt<CXXNewExpr>, +                                     check::NewAllocator,                                       check::PreStmt<CXXDeleteExpr>,                                       check::PostStmt<BlockExpr>,                                       check::PostObjCMessage, @@ -207,6 +210,8 @@ public:    void checkPreCall(const CallEvent &Call, CheckerContext &C) const;    void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;    void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; +  void checkNewAllocator(const CXXNewExpr *NE, SVal Target, +                         CheckerContext &C) const;    void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;    void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;    void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; @@ -253,20 +258,20 @@ private:    void initIdentifierInfo(ASTContext &C) const; -  /// \brief Determine family of a deallocation expression. +  /// Determine family of a deallocation expression.    AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const; -  /// \brief Print names of allocators and deallocators. +  /// Print names of allocators and deallocators.    ///    /// \returns true on success.    bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,                               const Expr *E) const; -  /// \brief Print expected name of an allocator based on the deallocator's +  /// Print expected name of an allocator based on the deallocator's    /// family derived from the DeallocExpr.    void printExpectedAllocName(raw_ostream &os, CheckerContext &C,                                const Expr *DeallocExpr) const; -  /// \brief Print expected name of a deallocator based on the allocator's +  /// Print expected name of a deallocator based on the allocator's    /// family.    void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const; @@ -281,10 +286,18 @@ private:    bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;    ///@} -  /// \brief Perform a zero-allocation check. +  /// Process C++ operator new()'s allocation, which is the part of C++ +  /// new-expression that goes before the constructor. +  void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C, +                            SVal Target) const; + +  /// Perform a zero-allocation check. +  /// The optional \p RetVal parameter specifies the newly allocated pointer +  /// value; if unspecified, the value of expression \p E is used.    ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,                                          const unsigned AllocationSizeArg, -                                        ProgramStateRef State) const; +                                        ProgramStateRef State, +                                        Optional<SVal> RetVal = None) const;    ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,                                         const CallExpr *CE, @@ -300,7 +313,7 @@ private:                                        AllocationFamily Family = AF_Malloc);    static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE, -                                       ProgramStateRef State); +                                       ProgramStateRef State, SVal Target);    // Check if this malloc() for special flags. At present that means M_ZERO or    // __GFP_ZERO (in which case, treat it like calloc). @@ -309,9 +322,12 @@ private:                        const ProgramStateRef &State) const;    /// Update the RefState to reflect the new memory allocation. +  /// The optional \p RetVal parameter specifies the newly allocated pointer +  /// value; if unspecified, the value of expression \p E is used.    static ProgramStateRef    MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, -                       AllocationFamily Family = AF_Malloc); +                       AllocationFamily Family = AF_Malloc, +                       Optional<SVal> RetVal = None);    ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,                                const OwnershipAttr* Att, @@ -337,7 +353,7 @@ private:    static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,                                     ProgramStateRef State); -  ///\brief Check if the memory associated with this symbol was released. +  ///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; @@ -415,8 +431,7 @@ private:    /// The bug visitor which allows us to print extra diagnostics along the    /// BugReport path. For example, showing the allocation site of the leaked    /// region. -  class MallocBugVisitor final -      : public BugReporterVisitorImpl<MallocBugVisitor> { +  class MallocBugVisitor final : public BugReporterVisitor {    protected:      enum NotificationMode {        Normal, @@ -432,15 +447,24 @@ private:      // A symbol from when the primary region should have been reallocated.      SymbolRef FailedReallocSymbol; +    // A C++ destructor stack frame in which memory was released. Used for +    // miscellaneous false positive suppression. +    const StackFrameContext *ReleaseDestructorLC; +      bool IsLeak;    public:      MallocBugVisitor(SymbolRef S, bool isLeak = false) -       : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {} +        : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), +          ReleaseDestructorLC(nullptr), IsLeak(isLeak) {} + +    static void *getTag() { +      static int Tag = 0; +      return &Tag; +    }      void Profile(llvm::FoldingSetNodeID &ID) const override { -      static int X = 0; -      ID.AddPointer(&X); +      ID.AddPointer(getTag());        ID.AddPointer(Sym);      } @@ -456,8 +480,13 @@ private:      inline bool isReleased(const RefState *S, const RefState *SPrev,                             const Stmt *Stmt) {        // Did not track -> released. Other state (allocated) -> released. -      return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt)) && -              (S && S->isReleased()) && (!SPrev || !SPrev->isReleased())); +      // The statement associated with the release might be missing. +      bool IsReleased = (S && S->isReleased()) && +                        (!SPrev || !SPrev->isReleased()); +      assert(!IsReleased || +             (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) || +             (!Stmt && S->getAllocationFamily() == AF_InnerBuffer)); +      return IsReleased;      }      inline bool isRelinquished(const RefState *S, const RefState *SPrev, @@ -486,7 +515,7 @@ private:                                                     BugReporterContext &BRC,                                                     BugReport &BR) override; -    std::unique_ptr<PathDiagnosticPiece> +    std::shared_ptr<PathDiagnosticPiece>      getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,                 BugReport &BR) override {        if (!IsLeak) @@ -496,7 +525,7 @@ private:          PathDiagnosticLocation::createEndOfPath(EndPathNode,                                                  BRC.getSourceManager());        // Do not add the statement itself as a range in case of leak. -      return llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(), +      return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),                                                           false);      } @@ -758,7 +787,7 @@ llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(      return None;    const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1); -  const SVal V = State->getSVal(FlagsEx, C.getLocationContext()); +  const SVal V = C.getSVal(FlagsEx);    if (!V.getAs<NonLoc>()) {      // The case where 'V' can be a location can only be due to a bad header,      // so in this case bail out. @@ -949,13 +978,15 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {  }  // Performs a 0-sized allocations check. -ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C, -                                               const Expr *E, -                                               const unsigned AllocationSizeArg, -                                               ProgramStateRef State) const { +ProgramStateRef MallocChecker::ProcessZeroAllocation( +    CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg, +    ProgramStateRef State, Optional<SVal> RetVal) const {    if (!State)      return nullptr; +  if (!RetVal) +    RetVal = C.getSVal(E); +    const Expr *Arg = nullptr;    if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { @@ -972,8 +1003,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,    assert(Arg); -  Optional<DefinedSVal> DefArgVal = -      State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>(); +  Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>();    if (!DefArgVal)      return State; @@ -988,8 +1018,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,        State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));    if (TrueState && !FalseState) { -    SVal retVal = State->getSVal(E, C.getLocationContext()); -    SymbolRef Sym = retVal.getAsLocSymbol(); +    SymbolRef Sym = RetVal->getAsLocSymbol();      if (!Sym)        return State; @@ -1050,9 +1079,9 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {    return false;  } -void MallocChecker::checkPostStmt(const CXXNewExpr *NE, -                                  CheckerContext &C) const { - +void MallocChecker::processNewAllocation(const CXXNewExpr *NE, +                                         CheckerContext &C, +                                         SVal Target) const {    if (NE->getNumPlacementArgs())      for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),           E = NE->placement_arg_end(); I != E; ++I) @@ -1072,37 +1101,48 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,    // MallocUpdateRefState() instead of MallocMemAux() which breakes the    // existing binding.    State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray -                                                           : AF_CXXNew); -  State = addExtentSize(C, NE, State); -  State = ProcessZeroAllocation(C, NE, 0, State); +                                                           : AF_CXXNew, Target); +  State = addExtentSize(C, NE, State, Target); +  State = ProcessZeroAllocation(C, NE, 0, State, Target);    C.addTransition(State);  } +void MallocChecker::checkPostStmt(const CXXNewExpr *NE, +                                  CheckerContext &C) const { +  if (!C.getAnalysisManager().getAnalyzerOptions().mayInlineCXXAllocator()) +    processNewAllocation(NE, C, C.getSVal(NE)); +} + +void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target, +                                      CheckerContext &C) const { +  if (!C.wasInlined) +    processNewAllocation(NE, C, Target); +} +  // Sets the extent value of the MemRegion allocated by  // new expression NE to its size in Bytes.  //  ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,                                               const CXXNewExpr *NE, -                                             ProgramStateRef State) { +                                             ProgramStateRef State, +                                             SVal Target) {    if (!State)      return nullptr;    SValBuilder &svalBuilder = C.getSValBuilder();    SVal ElementCount; -  const LocationContext *LCtx = C.getLocationContext();    const SubRegion *Region;    if (NE->isArray()) {      const Expr *SizeExpr = NE->getArraySize(); -    ElementCount = State->getSVal(SizeExpr, C.getLocationContext()); +    ElementCount = C.getSVal(SizeExpr);      // Store the extent size for the (symbolic)region      // containing the elements. -    Region = (State->getSVal(NE, LCtx)) -                 .getAsRegion() +    Region = Target.getAsRegion()                   ->getAs<SubRegion>() -                 ->getSuperRegion() +                 ->StripCasts()                   ->getAs<SubRegion>();    } else {      ElementCount = svalBuilder.makeIntVal(1, true); -    Region = (State->getSVal(NE, LCtx)).getAsRegion()->getAs<SubRegion>(); +    Region = Target.getAsRegion()->getAs<SubRegion>();    }    assert(Region); @@ -1199,7 +1239,8 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,    OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();    if (I != E) { -    return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State); +    return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(), +                        State);    }    return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);  } @@ -1212,8 +1253,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,    if (!State)      return nullptr; -  return MallocMemAux(C, CE, State->getSVal(SizeEx, C.getLocationContext()), -                      Init, State, Family); +  return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family);  }  ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, @@ -1239,7 +1279,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,    State = State->BindExpr(CE, C.getLocationContext(), RetVal);    // Fill the region with the initialization value. -  State = State->bindDefault(RetVal, Init, LCtx); +  State = State->bindDefaultInitial(RetVal, Init, LCtx);    // Set the region's extent equal to the Size parameter.    const SymbolicRegion *R = @@ -1263,18 +1303,22 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,  ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,                                                      const Expr *E,                                                      ProgramStateRef State, -                                                    AllocationFamily Family) { +                                                    AllocationFamily Family, +                                                    Optional<SVal> RetVal) {    if (!State)      return nullptr;    // Get the return value. -  SVal retVal = State->getSVal(E, C.getLocationContext()); +  if (!RetVal) +    RetVal = C.getSVal(E);    // We expect the malloc functions to return a pointer. -  if (!retVal.getAs<Loc>()) +  if (!RetVal->getAs<Loc>())      return nullptr; -  SymbolRef Sym = retVal.getAsLocSymbol(); +  SymbolRef Sym = RetVal->getAsLocSymbol(); +  // This is a return value of a function that was not inlined, such as malloc() +  // or new(). We've checked that in the caller. Therefore, it must be a symbol.    assert(Sym);    // Set the symbol's state to Allocated. @@ -1294,9 +1338,9 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,    bool ReleasedAllocated = false;    for (const auto &Arg : Att->args()) { -    ProgramStateRef StateI = FreeMemAux(C, CE, State, Arg, -                               Att->getOwnKind() == OwnershipAttr::Holds, -                               ReleasedAllocated); +    ProgramStateRef StateI = FreeMemAux( +        C, CE, State, Arg.getASTIndex(), +        Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);      if (StateI)        State = StateI;    } @@ -1429,6 +1473,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_InnerBuffer: os << "container-specific allocator"; return;      case AF_Alloca:      case AF_None: llvm_unreachable("not a deallocation expression");    } @@ -1441,6 +1486,7 @@ 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_InnerBuffer: os << "container-specific deallocator"; return;      case AF_Alloca:      case AF_None: llvm_unreachable("suspicious argument");    } @@ -1457,7 +1503,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,    if (!State)      return nullptr; -  SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext()); +  SVal ArgVal = C.getSVal(ArgExpr);    if (!ArgVal.getAs<DefinedOrUnknownSVal>())      return nullptr;    DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>(); @@ -1615,7 +1661,9 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,      return Optional<MallocChecker::CheckKind>();    }    case AF_CXXNew: -  case AF_CXXNewArray: { +  case AF_CXXNewArray: +  // FIXME: Add new CheckKind for AF_InnerBuffer. +  case AF_InnerBuffer: {      if (IsALeakCheck) {        if (ChecksEnabled[CK_NewDeleteLeaksChecker])          return CK_NewDeleteLeaksChecker; @@ -1945,6 +1993,11 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,      R->markInteresting(Sym);      R->addRange(Range);      R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + +    const RefState *RS = C.getState()->get<RegionState>(Sym); +    if (RS->getAllocationFamily() == AF_InnerBuffer) +      R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); +      C.emitReport(std::move(R));    }  } @@ -2047,8 +2100,8 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,    if (ExplodedNode *N = C.generateErrorNode()) {      if (!BT_BadFree[*CheckKind]) -      BT_BadFree[*CheckKind].reset( -          new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error")); +      BT_BadFree[*CheckKind].reset(new BugType( +          CheckNames[*CheckKind], "Bad free", categories::MemoryError));      SmallString<100> Buf;      llvm::raw_svector_ostream Os(Buf); @@ -2084,8 +2137,7 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,      return nullptr;    const Expr *arg0Expr = CE->getArg(0); -  const LocationContext *LCtx = C.getLocationContext(); -  SVal Arg0Val = State->getSVal(arg0Expr, LCtx); +  SVal Arg0Val = C.getSVal(arg0Expr);    if (!Arg0Val.getAs<DefinedOrUnknownSVal>())      return nullptr;    DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>(); @@ -2099,7 +2151,7 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,    const Expr *Arg1 = CE->getArg(1);    // Get the value of the size argument. -  SVal TotalSize = State->getSVal(Arg1, LCtx); +  SVal TotalSize = C.getSVal(Arg1);    if (SuffixWithN)      TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));    if (!TotalSize.getAs<DefinedOrUnknownSVal>()) @@ -2133,7 +2185,7 @@ ProgramStateRef MallocChecker::ReallocMemAux(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 = C.getSVal(CE);    SymbolRef ToPtr = RetVal.getAsSymbol();    if (!FromPtr || !ToPtr)      return nullptr; @@ -2216,7 +2268,7 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,              // Do not show local variables belonging to a function other than              // where the error is reported.              if (!VR || -                (VR->getStackFrame() == LeakContext->getCurrentStackFrame())) +                (VR->getStackFrame() == LeakContext->getStackFrame()))                ReferenceRegion = MR;            }          } @@ -2406,7 +2458,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {    // Check if we are returning a symbol.    ProgramStateRef State = C.getState(); -  SVal RetVal = State->getSVal(E, C.getLocationContext()); +  SVal RetVal = C.getSVal(E);    SymbolRef Sym = RetVal.getAsSymbol();    if (!Sym)      // If we are returning a field of the allocated struct or an array element, @@ -2436,8 +2488,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,    ProgramStateRef state = C.getState();    const BlockDataRegion *R = -    cast<BlockDataRegion>(state->getSVal(BE, -                                         C.getLocationContext()).getAsRegion()); +    cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());    BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),                                              E = R->referenced_vars_end(); @@ -2793,36 +2844,133 @@ static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,    return nullptr;  } +static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) { +  if (const IdentifierInfo *II = DD->getParent()->getIdentifier()) { +    StringRef N = II->getName(); +    if (N.contains_lower("ptr") || N.contains_lower("pointer")) { +      if (N.contains_lower("ref") || N.contains_lower("cnt") || +          N.contains_lower("intrusive") || N.contains_lower("shared")) { +        return true; +      } +    } +  } +  return false; +} +  std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(      const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,      BugReport &BR) { +    ProgramStateRef state = N->getState();    ProgramStateRef statePrev = PrevN->getState();    const RefState *RS = state->get<RegionState>(Sym);    const RefState *RSPrev = statePrev->get<RegionState>(Sym); -  if (!RS) -    return nullptr;    const Stmt *S = PathDiagnosticLocation::getStmt(N); -  if (!S) +  // When dealing with containers, we sometimes want to give a note +  // even if the statement is missing. +  if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))      return nullptr; +  const LocationContext *CurrentLC = N->getLocationContext(); + +  // If we find an atomic fetch_add or fetch_sub within the destructor in which +  // the pointer was released (before the release), this is likely a destructor +  // of a shared pointer. +  // Because we don't model atomics, and also because we don't know that the +  // original reference count is positive, we should not report use-after-frees +  // on objects deleted in such destructors. This can probably be improved +  // through better shared pointer modeling. +  if (ReleaseDestructorLC) { +    if (const auto *AE = dyn_cast<AtomicExpr>(S)) { +      AtomicExpr::AtomicOp Op = AE->getOp(); +      if (Op == AtomicExpr::AO__c11_atomic_fetch_add || +          Op == AtomicExpr::AO__c11_atomic_fetch_sub) { +        if (ReleaseDestructorLC == CurrentLC || +            ReleaseDestructorLC->isParentOf(CurrentLC)) { +          BR.markInvalid(getTag(), S); +        } +      } +    } +  } +    // FIXME: We will eventually need to handle non-statement-based events    // (__attribute__((cleanup))).    // Find out if this is an interesting point and what is the kind. -  const char *Msg = nullptr; +  StringRef Msg;    StackHintGeneratorForSymbol *StackHint = nullptr; +  SmallString<256> Buf; +  llvm::raw_svector_ostream OS(Buf); +    if (Mode == Normal) {      if (isAllocated(RS, RSPrev, S)) {        Msg = "Memory is allocated";        StackHint = new StackHintGeneratorForSymbol(Sym,                                                    "Returned allocated memory");      } else if (isReleased(RS, RSPrev, S)) { -      Msg = "Memory is released"; +      const auto Family = RS->getAllocationFamily(); +      switch (Family) { +        case AF_Alloca: +        case AF_Malloc: +        case AF_CXXNew: +        case AF_CXXNewArray: +        case AF_IfNameIndex: +          Msg = "Memory is released"; +          break; +        case AF_InnerBuffer: { +          OS << "Inner pointer invalidated by call to "; +          if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { +            OS << "destructor"; +          } else { +            OS << "'"; +            const Stmt *S = RS->getStmt(); +            if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) { +              OS << MemCallE->getMethodDecl()->getNameAsString(); +            } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { +              OS << OpCallE->getDirectCallee()->getNameAsString(); +            } +            OS << "'"; +          } +          Msg = OS.str(); +          break; +        } +        case AF_None: +          llvm_unreachable("Unhandled allocation family!"); +      }        StackHint = new StackHintGeneratorForSymbol(Sym,                                               "Returning; memory was released"); + +      // See if we're releasing memory while inlining a destructor +      // (or one of its callees). This turns on various common +      // false positive suppressions. +      bool FoundAnyDestructor = false; +      for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) { +        if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) { +          if (isReferenceCountingPointerDestructor(DD)) { +            // This immediately looks like a reference-counting destructor. +            // We're bad at guessing the original reference count of the object, +            // so suppress the report for now. +            BR.markInvalid(getTag(), DD); +          } else if (!FoundAnyDestructor) { +            assert(!ReleaseDestructorLC && +                   "There can be only one release point!"); +            // Suspect that it's a reference counting pointer destructor. +            // On one of the next nodes might find out that it has atomic +            // reference counting operations within it (see the code above), +            // and if so, we'd conclude that it likely is a reference counting +            // pointer destructor. +            ReleaseDestructorLC = LC->getStackFrame(); +            // It is unlikely that releasing memory is delegated to a destructor +            // inside a destructor of a shared pointer, because it's fairly hard +            // to pass the information that the pointer indeed needs to be +            // released into it. So we're only interested in the innermost +            // destructor. +            FoundAnyDestructor = true; +          } +        } +      }      } else if (isRelinquished(RS, RSPrev, S)) {        Msg = "Memory ownership is transferred";        StackHint = new StackHintGeneratorForSymbol(Sym, ""); @@ -2856,13 +3004,24 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(      }    } -  if (!Msg) +  if (Msg.empty())      return nullptr;    assert(StackHint);    // Generate the extra diagnostic. -  PathDiagnosticLocation Pos(S, BRC.getSourceManager(), -                             N->getLocationContext()); +  PathDiagnosticLocation Pos; +  if (!S) { +    assert(RS->getAllocationFamily() == AF_InnerBuffer); +    auto PostImplCall = N->getLocation().getAs<PostImplicitCall>(); +    if (!PostImplCall) +      return nullptr; +    Pos = PathDiagnosticLocation(PostImplCall->getLocation(), +                                 BRC.getSourceManager()); +  } else { +    Pos = PathDiagnosticLocation(S, BRC.getSourceManager(), +                                 N->getLocationContext()); +  } +    return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint);  } @@ -2890,6 +3049,20 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,    }  } +namespace clang { +namespace ento { +namespace allocation_state { + +ProgramStateRef +markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) { +  AllocationFamily Family = AF_InnerBuffer; +  return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin)); +} + +} // end namespace allocation_state +} // end namespace ento +} // end namespace clang +  void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {    registerCStringCheckerBasic(mgr);    MallocChecker *checker = mgr.registerChecker<MallocChecker>(); @@ -2900,8 +3073,13 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {        mgr.getCurrentCheckName();    // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete    // checker. -  if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) +  if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) {      checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; +    // FIXME: This does not set the correct name, but without this workaround +    //        no name will be set at all. +    checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = +        mgr.getCurrentCheckName(); +  }  }  #define REGISTER_CHECKER(name)                                                 \  | 
