|  | //=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file defines malloc/free checker, which checks for potential memory | 
|  | // leaks, double free, and use-after-free problems. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | 
|  | #include "InterCheckerAPI.h" | 
|  | #include "clang/AST/Attr.h" | 
|  | #include "clang/AST/ParentMap.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "clang/Basic/TargetInfo.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | 
|  | #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" | 
|  | #include "clang/StaticAnalyzer/Core/Checker.h" | 
|  | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  | #include "AllocationState.h" | 
|  | #include <climits> | 
|  | #include <utility> | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace ento; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Used to check correspondence between allocators and deallocators. | 
|  | enum AllocationFamily { | 
|  | AF_None, | 
|  | AF_Malloc, | 
|  | AF_CXXNew, | 
|  | AF_CXXNewArray, | 
|  | AF_IfNameIndex, | 
|  | AF_Alloca, | 
|  | AF_InnerBuffer | 
|  | }; | 
|  |  | 
|  | 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 | 
|  | // this reference. A relinquished symbol should not be freed. | 
|  | Relinquished, | 
|  | // We are no longer guaranteed to have observed all manipulations | 
|  | // of this pointer/memory. For example, it could have been | 
|  | // passed as a parameter to an opaque function. | 
|  | Escaped | 
|  | }; | 
|  |  | 
|  | const Stmt *S; | 
|  | 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) | 
|  | : S(s), K(k), Family(family) { | 
|  | assert(family != AF_None); | 
|  | } | 
|  | 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; } | 
|  | AllocationFamily getAllocationFamily() const { | 
|  | return (AllocationFamily)Family; | 
|  | } | 
|  | const Stmt *getStmt() const { return S; } | 
|  |  | 
|  | bool operator==(const RefState &X) const { | 
|  | return K == X.K && S == X.S && Family == X.Family; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  | static RefState getRelinquished(unsigned family, const Stmt *s) { | 
|  | return RefState(Relinquished, s, family); | 
|  | } | 
|  | static RefState getEscaped(const RefState *RS) { | 
|  | return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily()); | 
|  | } | 
|  |  | 
|  | void Profile(llvm::FoldingSetNodeID &ID) const { | 
|  | ID.AddInteger(K); | 
|  | ID.AddPointer(S); | 
|  | ID.AddInteger(Family); | 
|  | } | 
|  |  | 
|  | void dump(raw_ostream &OS) const { | 
|  | switch (static_cast<Kind>(K)) { | 
|  | #define CASE(ID) case ID: OS << #ID; break; | 
|  | CASE(Allocated) | 
|  | CASE(AllocatedOfSizeZero) | 
|  | CASE(Released) | 
|  | CASE(Relinquished) | 
|  | CASE(Escaped) | 
|  | } | 
|  | } | 
|  |  | 
|  | LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } | 
|  | }; | 
|  |  | 
|  | enum ReallocPairKind { | 
|  | RPToBeFreedAfterFailure, | 
|  | // The symbol has been freed when reallocation failed. | 
|  | RPIsFreeOnFailure, | 
|  | // The symbol does not need to be freed after reallocation fails. | 
|  | RPDoNotTrackAfterFailure | 
|  | }; | 
|  |  | 
|  | /// \class ReallocPair | 
|  | /// Stores information about the symbol being reallocated by a call to | 
|  | /// 'realloc' to allow modeling failed reallocation later in the path. | 
|  | struct ReallocPair { | 
|  | // The symbol which realloc reallocated. | 
|  | SymbolRef ReallocatedSym; | 
|  | ReallocPairKind Kind; | 
|  |  | 
|  | ReallocPair(SymbolRef S, ReallocPairKind K) : | 
|  | ReallocatedSym(S), Kind(K) {} | 
|  | void Profile(llvm::FoldingSetNodeID &ID) const { | 
|  | ID.AddInteger(Kind); | 
|  | ID.AddPointer(ReallocatedSym); | 
|  | } | 
|  | bool operator==(const ReallocPair &X) const { | 
|  | return ReallocatedSym == X.ReallocatedSym && | 
|  | Kind == X.Kind; | 
|  | } | 
|  | }; | 
|  |  | 
|  | typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo; | 
|  |  | 
|  | class MallocChecker : public Checker<check::DeadSymbols, | 
|  | check::PointerEscape, | 
|  | check::ConstPointerEscape, | 
|  | check::PreStmt<ReturnStmt>, | 
|  | check::EndFunction, | 
|  | check::PreCall, | 
|  | check::PostStmt<CallExpr>, | 
|  | check::PostStmt<CXXNewExpr>, | 
|  | check::NewAllocator, | 
|  | check::PreStmt<CXXDeleteExpr>, | 
|  | check::PostStmt<BlockExpr>, | 
|  | check::PostObjCMessage, | 
|  | check::Location, | 
|  | eval::Assume> | 
|  | { | 
|  | public: | 
|  | MallocChecker() | 
|  | : II_alloca(nullptr), II_win_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_win_strdup(nullptr), II_kmalloc(nullptr), | 
|  | II_kfree(nullptr), II_if_nameindex(nullptr), | 
|  | II_if_freenameindex(nullptr), II_wcsdup(nullptr), | 
|  | II_win_wcsdup(nullptr), II_g_malloc(nullptr), II_g_malloc0(nullptr), | 
|  | II_g_realloc(nullptr), II_g_try_malloc(nullptr), | 
|  | II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), | 
|  | II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), | 
|  | II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), | 
|  | II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), | 
|  | II_g_try_realloc_n(nullptr) {} | 
|  |  | 
|  | /// In pessimistic mode, the checker assumes that it does not know which | 
|  | /// functions might free the memory. | 
|  | enum CheckKind { | 
|  | CK_MallocChecker, | 
|  | CK_NewDeleteChecker, | 
|  | CK_NewDeleteLeaksChecker, | 
|  | CK_MismatchedDeallocatorChecker, | 
|  | CK_InnerPointerChecker, | 
|  | CK_NumCheckKinds | 
|  | }; | 
|  |  | 
|  | enum class MemoryOperationKind { | 
|  | MOK_Allocate, | 
|  | MOK_Free, | 
|  | MOK_Any | 
|  | }; | 
|  |  | 
|  | DefaultBool IsOptimistic; | 
|  |  | 
|  | DefaultBool ChecksEnabled[CK_NumCheckKinds]; | 
|  | CheckName CheckNames[CK_NumCheckKinds]; | 
|  |  | 
|  | 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; | 
|  | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; | 
|  | void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; | 
|  | void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const; | 
|  | ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, | 
|  | bool Assumption) const; | 
|  | void checkLocation(SVal l, bool isLoad, const Stmt *S, | 
|  | CheckerContext &C) const; | 
|  |  | 
|  | ProgramStateRef checkPointerEscape(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind) const; | 
|  | ProgramStateRef checkConstPointerEscape(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind) const; | 
|  |  | 
|  | void printState(raw_ostream &Out, ProgramStateRef State, | 
|  | const char *NL, const char *Sep) const override; | 
|  |  | 
|  | private: | 
|  | mutable std::unique_ptr<BugType> BT_DoubleFree[CK_NumCheckKinds]; | 
|  | mutable std::unique_ptr<BugType> BT_DoubleDelete; | 
|  | 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 std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds]; | 
|  | mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, | 
|  | *II_realloc, *II_calloc, *II_valloc, *II_reallocf, | 
|  | *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, | 
|  | *II_kfree, *II_if_nameindex, *II_if_freenameindex, | 
|  | *II_wcsdup, *II_win_wcsdup, *II_g_malloc, | 
|  | *II_g_malloc0, *II_g_realloc, *II_g_try_malloc, | 
|  | *II_g_try_malloc0, *II_g_try_realloc, *II_g_free, | 
|  | *II_g_memdup, *II_g_malloc_n, *II_g_malloc0_n, | 
|  | *II_g_realloc_n, *II_g_try_malloc_n, | 
|  | *II_g_try_malloc0_n, *II_g_try_realloc_n; | 
|  | mutable Optional<uint64_t> KernelZeroFlagVal; | 
|  |  | 
|  | void initIdentifierInfo(ASTContext &C) const; | 
|  |  | 
|  | /// Determine family of a deallocation expression. | 
|  | AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const; | 
|  |  | 
|  | /// Print names of allocators and deallocators. | 
|  | /// | 
|  | /// \returns true on success. | 
|  | bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, | 
|  | const Expr *E) const; | 
|  |  | 
|  | /// 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; | 
|  | /// Print expected name of a deallocator based on the allocator's | 
|  | /// family. | 
|  | void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const; | 
|  |  | 
|  | ///@{ | 
|  | /// Check if this is one of the functions which can allocate/reallocate memory | 
|  | /// pointed to by one of its arguments. | 
|  | bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; | 
|  | bool isCMemFunction(const FunctionDecl *FD, | 
|  | ASTContext &C, | 
|  | AllocationFamily Family, | 
|  | MemoryOperationKind MemKind) const; | 
|  | bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; | 
|  | ///@} | 
|  |  | 
|  | /// 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, | 
|  | Optional<SVal> RetVal = None) const; | 
|  |  | 
|  | ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, | 
|  | const CallExpr *CE, | 
|  | 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); | 
|  | static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, | 
|  | SVal SizeEx, SVal Init, | 
|  | ProgramStateRef State, | 
|  | AllocationFamily Family = AF_Malloc); | 
|  |  | 
|  | static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE, | 
|  | 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). | 
|  | llvm::Optional<ProgramStateRef> | 
|  | performKernelMalloc(const CallExpr *CE, CheckerContext &C, | 
|  | 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, | 
|  | Optional<SVal> RetVal = None); | 
|  |  | 
|  | ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, | 
|  | const OwnershipAttr* Att, | 
|  | ProgramStateRef State) const; | 
|  | ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, | 
|  | ProgramStateRef state, unsigned Num, | 
|  | bool Hold, | 
|  | bool &ReleasedAllocated, | 
|  | bool ReturnsNullOnFailure = false) const; | 
|  | ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg, | 
|  | const Expr *ParentExpr, | 
|  | ProgramStateRef State, | 
|  | bool Hold, | 
|  | bool &ReleasedAllocated, | 
|  | bool ReturnsNullOnFailure = false) const; | 
|  |  | 
|  | ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, | 
|  | bool FreesMemOnFailure, | 
|  | ProgramStateRef State, | 
|  | bool SuffixWithN = false) const; | 
|  | static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, | 
|  | const Expr *BlockBytes); | 
|  | static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, | 
|  | ProgramStateRef State); | 
|  |  | 
|  | /// Check if the memory associated with this symbol was released. | 
|  | bool isReleased(SymbolRef Sym, CheckerContext &C) const; | 
|  |  | 
|  | /// See if deallocation happens in a suspicious context. If so, escape the | 
|  | /// pointers that otherwise would have been deallocated and return true. | 
|  | bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE, | 
|  | 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 | 
|  | /// "interesting" and should be modeled explicitly. | 
|  | /// | 
|  | /// \param [out] EscapingSymbol A function might not free memory in general, | 
|  | ///   but could be known to free a particular symbol. In this case, false is | 
|  | ///   returned and the single escaping symbol is returned through the out | 
|  | ///   parameter. | 
|  | /// | 
|  | /// We assume that pointers do not escape through calls to system functions | 
|  | /// not handled by this checker. | 
|  | bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call, | 
|  | ProgramStateRef State, | 
|  | SymbolRef &EscapingSymbol) const; | 
|  |  | 
|  | // Implementation of the checkPointerEscape callbacks. | 
|  | ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind, | 
|  | bool(*CheckRefState)(const RefState*)) const; | 
|  |  | 
|  | // Implementation of the checkPreStmt and checkEndFunction callbacks. | 
|  | void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const; | 
|  |  | 
|  | ///@{ | 
|  | /// 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, | 
|  | bool IsALeakCheck = false) const; | 
|  | Optional<CheckKind> getCheckIfTracked(CheckerContext &C, | 
|  | 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; | 
|  | void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, | 
|  | const Expr *DeallocExpr, | 
|  | const Expr *AllocExpr = nullptr) const; | 
|  | void ReportUseAfterFree(CheckerContext &C, SourceRange Range, | 
|  | SymbolRef Sym) const; | 
|  | void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, | 
|  | SymbolRef Sym, SymbolRef PrevSym) const; | 
|  |  | 
|  | void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const; | 
|  |  | 
|  | void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range, | 
|  | SymbolRef Sym) const; | 
|  |  | 
|  | void ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, | 
|  | SourceRange Range, const Expr *FreeExpr) 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, | 
|  | CheckerContext &C) const; | 
|  |  | 
|  | void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const; | 
|  |  | 
|  | /// 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 BugReporterVisitor { | 
|  | protected: | 
|  | enum NotificationMode { | 
|  | Normal, | 
|  | ReallocationFailed | 
|  | }; | 
|  |  | 
|  | // The allocated region symbol tracked by the main analysis. | 
|  | SymbolRef Sym; | 
|  |  | 
|  | // The mode we are in, i.e. what kind of diagnostics will be emitted. | 
|  | NotificationMode Mode; | 
|  |  | 
|  | // 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), | 
|  | ReleaseDestructorLC(nullptr), IsLeak(isLeak) {} | 
|  |  | 
|  | static void *getTag() { | 
|  | static int Tag = 0; | 
|  | return &Tag; | 
|  | } | 
|  |  | 
|  | void Profile(llvm::FoldingSetNodeID &ID) const override { | 
|  | ID.AddPointer(getTag()); | 
|  | ID.AddPointer(Sym); | 
|  | } | 
|  |  | 
|  | inline bool isAllocated(const RefState *S, const RefState *SPrev, | 
|  | const Stmt *Stmt) { | 
|  | // Did not track -> allocated. Other state (released) -> allocated. | 
|  | return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && | 
|  | (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && | 
|  | (!SPrev || !(SPrev->isAllocated() || | 
|  | SPrev->isAllocatedOfSizeZero()))); | 
|  | } | 
|  |  | 
|  | inline bool isReleased(const RefState *S, const RefState *SPrev, | 
|  | const Stmt *Stmt) { | 
|  | // Did not track -> released. Other state (allocated) -> released. | 
|  | // 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, | 
|  | const Stmt *Stmt) { | 
|  | // Did not track -> relinquished. Other state (allocated) -> relinquished. | 
|  | return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || | 
|  | isa<ObjCPropertyRefExpr>(Stmt)) && | 
|  | (S && S->isRelinquished()) && | 
|  | (!SPrev || !SPrev->isRelinquished())); | 
|  | } | 
|  |  | 
|  | inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, | 
|  | const Stmt *Stmt) { | 
|  | // If the expression is not a call, and the state change is | 
|  | // released -> allocated, it must be the realloc return value | 
|  | // 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() || S->isAllocatedOfSizeZero())) && | 
|  | (SPrev && !(SPrev->isAllocated() || SPrev->isAllocatedOfSizeZero()))); | 
|  | } | 
|  |  | 
|  | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, | 
|  | BugReporterContext &BRC, | 
|  | PathSensitiveBugReport &BR) override; | 
|  |  | 
|  | PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, | 
|  | const ExplodedNode *EndPathNode, | 
|  | PathSensitiveBugReport &BR) override { | 
|  | if (!IsLeak) | 
|  | return nullptr; | 
|  |  | 
|  | PathDiagnosticLocation L = | 
|  | PathDiagnosticLocation::createEndOfPath(EndPathNode); | 
|  | // Do not add the statement itself as a range in case of leak. | 
|  | return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), | 
|  | false); | 
|  | } | 
|  |  | 
|  | private: | 
|  | class StackHintGeneratorForReallocationFailed | 
|  | : public StackHintGeneratorForSymbol { | 
|  | public: | 
|  | StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) | 
|  | : StackHintGeneratorForSymbol(S, M) {} | 
|  |  | 
|  | std::string getMessageForArg(const Expr *ArgE, | 
|  | unsigned ArgIndex) override { | 
|  | // Printed parameters start at 1, not 0. | 
|  | ++ArgIndex; | 
|  |  | 
|  | SmallString<200> buf; | 
|  | llvm::raw_svector_ostream os(buf); | 
|  |  | 
|  | os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) | 
|  | << " parameter failed"; | 
|  |  | 
|  | return os.str(); | 
|  | } | 
|  |  | 
|  | std::string getMessageForReturn(const CallExpr *CallExpr) override { | 
|  | return "Reallocation of returned value failed"; | 
|  | } | 
|  | }; | 
|  | }; | 
|  | }; | 
|  | } // end anonymous namespace | 
|  |  | 
|  | REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) | 
|  | REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) | 
|  | REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) | 
|  |  | 
|  | // A map from the freed symbol to the symbol representing the return value of | 
|  | // the free function. | 
|  | REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef) | 
|  |  | 
|  | namespace { | 
|  | class StopTrackingCallback final : public SymbolVisitor { | 
|  | ProgramStateRef state; | 
|  | public: | 
|  | StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {} | 
|  | ProgramStateRef getState() const { return state; } | 
|  |  | 
|  | bool VisitSymbol(SymbolRef sym) override { | 
|  | state = state->remove<RegionState>(sym); | 
|  | return true; | 
|  | } | 
|  | }; | 
|  | } // end anonymous namespace | 
|  |  | 
|  | 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"); | 
|  | II_reallocf = &Ctx.Idents.get("reallocf"); | 
|  | II_calloc = &Ctx.Idents.get("calloc"); | 
|  | II_valloc = &Ctx.Idents.get("valloc"); | 
|  | II_strdup = &Ctx.Idents.get("strdup"); | 
|  | II_strndup = &Ctx.Idents.get("strndup"); | 
|  | II_wcsdup = &Ctx.Idents.get("wcsdup"); | 
|  | II_kmalloc = &Ctx.Idents.get("kmalloc"); | 
|  | II_kfree = &Ctx.Idents.get("kfree"); | 
|  | II_if_nameindex = &Ctx.Idents.get("if_nameindex"); | 
|  | II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); | 
|  |  | 
|  | //MSVC uses `_`-prefixed instead, so we check for them too. | 
|  | II_win_strdup = &Ctx.Idents.get("_strdup"); | 
|  | II_win_wcsdup = &Ctx.Idents.get("_wcsdup"); | 
|  | II_win_alloca = &Ctx.Idents.get("_alloca"); | 
|  |  | 
|  | // Glib | 
|  | II_g_malloc = &Ctx.Idents.get("g_malloc"); | 
|  | II_g_malloc0 = &Ctx.Idents.get("g_malloc0"); | 
|  | II_g_realloc = &Ctx.Idents.get("g_realloc"); | 
|  | II_g_try_malloc = &Ctx.Idents.get("g_try_malloc"); | 
|  | II_g_try_malloc0 = &Ctx.Idents.get("g_try_malloc0"); | 
|  | II_g_try_realloc = &Ctx.Idents.get("g_try_realloc"); | 
|  | II_g_free = &Ctx.Idents.get("g_free"); | 
|  | II_g_memdup = &Ctx.Idents.get("g_memdup"); | 
|  | II_g_malloc_n = &Ctx.Idents.get("g_malloc_n"); | 
|  | II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n"); | 
|  | II_g_realloc_n = &Ctx.Idents.get("g_realloc_n"); | 
|  | II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n"); | 
|  | II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n"); | 
|  | II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n"); | 
|  | } | 
|  |  | 
|  | bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { | 
|  | if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any)) | 
|  | return true; | 
|  |  | 
|  | 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; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool MallocChecker::isCMemFunction(const FunctionDecl *FD, | 
|  | ASTContext &C, | 
|  | AllocationFamily Family, | 
|  | MemoryOperationKind MemKind) const { | 
|  | if (!FD) | 
|  | return false; | 
|  |  | 
|  | bool CheckFree = (MemKind == MemoryOperationKind::MOK_Any || | 
|  | MemKind == MemoryOperationKind::MOK_Free); | 
|  | bool CheckAlloc = (MemKind == MemoryOperationKind::MOK_Any || | 
|  | MemKind == MemoryOperationKind::MOK_Allocate); | 
|  |  | 
|  | if (FD->getKind() == Decl::Function) { | 
|  | const IdentifierInfo *FunI = FD->getIdentifier(); | 
|  | initIdentifierInfo(C); | 
|  |  | 
|  | if (Family == AF_Malloc && CheckFree) { | 
|  | if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || | 
|  | FunI == II_g_free || FunI == II_kfree) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | 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_win_strdup || FunI == II_strndup || FunI == II_wcsdup || | 
|  | FunI == II_win_wcsdup || FunI == II_kmalloc || | 
|  | FunI == II_g_malloc || FunI == II_g_malloc0 || | 
|  | FunI == II_g_realloc || FunI == II_g_try_malloc || | 
|  | FunI == II_g_try_malloc0 || FunI == II_g_try_realloc || | 
|  | FunI == II_g_memdup || FunI == II_g_malloc_n || | 
|  | FunI == II_g_malloc0_n || FunI == II_g_realloc_n || | 
|  | FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n || | 
|  | FunI == II_g_try_realloc_n) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (Family == AF_IfNameIndex && CheckFree) { | 
|  | if (FunI == II_if_freenameindex) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (Family == AF_IfNameIndex && CheckAlloc) { | 
|  | if (FunI == II_if_nameindex) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (Family == AF_Alloca && CheckAlloc) { | 
|  | if (FunI == II_alloca || FunI == II_win_alloca) | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (Family != AF_Malloc) | 
|  | return false; | 
|  |  | 
|  | if (IsOptimistic && FD->hasAttrs()) { | 
|  | for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { | 
|  | OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); | 
|  | if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { | 
|  | if (CheckFree) | 
|  | return true; | 
|  | } else if (OwnKind == OwnershipAttr::Returns) { | 
|  | if (CheckAlloc) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Tells if the callee is one of the builtin new/delete operators, including | 
|  | // placement operators and other standard overloads. | 
|  | bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, | 
|  | ASTContext &C) const { | 
|  | if (!FD) | 
|  | return false; | 
|  |  | 
|  | OverloadedOperatorKind Kind = FD->getOverloadedOperator(); | 
|  | if (Kind != OO_New && Kind != OO_Array_New && | 
|  | Kind != OO_Delete && Kind != OO_Array_Delete) | 
|  | return false; | 
|  |  | 
|  | // This is standard if and only if it's not defined in a user file. | 
|  | SourceLocation L = FD->getLocation(); | 
|  | // If the header for operator delete is not included, it's still defined | 
|  | // in an invalid source location. Check to make sure we don't crash. | 
|  | return !L.isValid() || C.getSourceManager().isInSystemHeader(L); | 
|  | } | 
|  |  | 
|  | llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc( | 
|  | const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const { | 
|  | // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: | 
|  | // | 
|  | // void *malloc(unsigned long size, struct malloc_type *mtp, int flags); | 
|  | // | 
|  | // One of the possible flags is M_ZERO, which means 'give me back an | 
|  | // allocation which is already zeroed', like calloc. | 
|  |  | 
|  | // 2-argument kmalloc(), as used in the Linux kernel: | 
|  | // | 
|  | // void *kmalloc(size_t size, gfp_t flags); | 
|  | // | 
|  | // Has the similar flag value __GFP_ZERO. | 
|  |  | 
|  | // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some | 
|  | // code could be shared. | 
|  |  | 
|  | ASTContext &Ctx = C.getASTContext(); | 
|  | llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS(); | 
|  |  | 
|  | if (!KernelZeroFlagVal.hasValue()) { | 
|  | if (OS == llvm::Triple::FreeBSD) | 
|  | KernelZeroFlagVal = 0x0100; | 
|  | else if (OS == llvm::Triple::NetBSD) | 
|  | KernelZeroFlagVal = 0x0002; | 
|  | else if (OS == llvm::Triple::OpenBSD) | 
|  | KernelZeroFlagVal = 0x0008; | 
|  | else if (OS == llvm::Triple::Linux) | 
|  | // __GFP_ZERO | 
|  | KernelZeroFlagVal = 0x8000; | 
|  | else | 
|  | // FIXME: We need a more general way of getting the M_ZERO value. | 
|  | // See also: O_CREAT in UnixAPIChecker.cpp. | 
|  |  | 
|  | // Fall back to normal malloc behavior on platforms where we don't | 
|  | // know M_ZERO. | 
|  | return None; | 
|  | } | 
|  |  | 
|  | // We treat the last argument as the flags argument, and callers fall-back to | 
|  | // normal malloc on a None return. This works for the FreeBSD kernel malloc | 
|  | // as well as Linux kmalloc. | 
|  | if (CE->getNumArgs() < 2) | 
|  | return None; | 
|  |  | 
|  | const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1); | 
|  | 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. | 
|  | return None; | 
|  | } | 
|  |  | 
|  | NonLoc Flags = V.castAs<NonLoc>(); | 
|  | NonLoc ZeroFlag = C.getSValBuilder() | 
|  | .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType()) | 
|  | .castAs<NonLoc>(); | 
|  | SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And, | 
|  | Flags, ZeroFlag, | 
|  | FlagsEx->getType()); | 
|  | if (MaskedFlagsUC.isUnknownOrUndef()) | 
|  | return None; | 
|  | DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>(); | 
|  |  | 
|  | // Check if maskedFlags is non-zero. | 
|  | ProgramStateRef TrueState, FalseState; | 
|  | std::tie(TrueState, FalseState) = State->assume(MaskedFlags); | 
|  |  | 
|  | // If M_ZERO is set, treat this like calloc (initialized). | 
|  | if (TrueState && !FalseState) { | 
|  | SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy); | 
|  | return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState); | 
|  | } | 
|  |  | 
|  | return None; | 
|  | } | 
|  |  | 
|  | SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, | 
|  | const Expr *BlockBytes) { | 
|  | SValBuilder &SB = C.getSValBuilder(); | 
|  | SVal BlocksVal = C.getSVal(Blocks); | 
|  | SVal BlockBytesVal = C.getSVal(BlockBytes); | 
|  | ProgramStateRef State = C.getState(); | 
|  | SVal TotalSize = SB.evalBinOp(State, BO_Mul, BlocksVal, BlockBytesVal, | 
|  | SB.getContext().getSizeType()); | 
|  | return TotalSize; | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { | 
|  | if (C.wasInlined) | 
|  | return; | 
|  |  | 
|  | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
|  | if (!FD) | 
|  | return; | 
|  |  | 
|  | ProgramStateRef State = C.getState(); | 
|  | bool ReleasedAllocatedMemory = false; | 
|  |  | 
|  | if (FD->getKind() == Decl::Function) { | 
|  | initIdentifierInfo(C.getASTContext()); | 
|  | IdentifierInfo *FunI = FD->getIdentifier(); | 
|  |  | 
|  | if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) { | 
|  | if (CE->getNumArgs() < 1) | 
|  | 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); | 
|  | if (MaybeState.hasValue()) | 
|  | State = MaybeState.getValue(); | 
|  | else | 
|  | State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); | 
|  | } | 
|  | } else if (FunI == II_kmalloc) { | 
|  | if (CE->getNumArgs() < 1) | 
|  | return; | 
|  | llvm::Optional<ProgramStateRef> MaybeState = | 
|  | performKernelMalloc(CE, C, State); | 
|  | if (MaybeState.hasValue()) | 
|  | State = MaybeState.getValue(); | 
|  | else | 
|  | State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); | 
|  | } else if (FunI == II_valloc) { | 
|  | 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 || FunI == II_g_realloc || | 
|  | FunI == II_g_try_realloc) { | 
|  | State = ReallocMemAux(C, CE, false, State); | 
|  | State = ProcessZeroAllocation(C, CE, 1, State); | 
|  | } else if (FunI == II_reallocf) { | 
|  | State = ReallocMemAux(C, CE, true, State); | 
|  | State = ProcessZeroAllocation(C, CE, 1, State); | 
|  | } else if (FunI == II_calloc) { | 
|  | State = CallocMem(C, CE, State); | 
|  | State = ProcessZeroAllocation(C, CE, 0, State); | 
|  | State = ProcessZeroAllocation(C, CE, 1, State); | 
|  | } else if (FunI == II_free || FunI == II_g_free || FunI == II_kfree) { | 
|  | if (suppressDeallocationsInSuspiciousContexts(CE, C)) | 
|  | return; | 
|  |  | 
|  | State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); | 
|  | } else if (FunI == II_strdup || FunI == II_win_strdup || | 
|  | FunI == II_wcsdup || FunI == II_win_wcsdup) { | 
|  | State = MallocUpdateRefState(C, CE, State); | 
|  | } else if (FunI == II_strndup) { | 
|  | State = MallocUpdateRefState(C, CE, State); | 
|  | } else if (FunI == II_alloca || FunI == II_win_alloca) { | 
|  | if (CE->getNumArgs() < 1) | 
|  | return; | 
|  | 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) { | 
|  | State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, | 
|  | AF_CXXNew); | 
|  | 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 | 
|  | llvm_unreachable("not a new/delete operator"); | 
|  | } else if (FunI == II_if_nameindex) { | 
|  | // Should we model this differently? We can allocate a fixed number of | 
|  | // elements with zeros in the last one. | 
|  | State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, | 
|  | AF_IfNameIndex); | 
|  | } else if (FunI == II_if_freenameindex) { | 
|  | State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); | 
|  | } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) { | 
|  | if (CE->getNumArgs() < 1) | 
|  | return; | 
|  | SValBuilder &svalBuilder = C.getSValBuilder(); | 
|  | SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); | 
|  | State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); | 
|  | State = ProcessZeroAllocation(C, CE, 0, State); | 
|  | } else if (FunI == II_g_memdup) { | 
|  | if (CE->getNumArgs() < 2) | 
|  | return; | 
|  | State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); | 
|  | State = ProcessZeroAllocation(C, CE, 1, State); | 
|  | } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || | 
|  | FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { | 
|  | if (CE->getNumArgs() < 2) | 
|  | return; | 
|  | SVal Init = UndefinedVal(); | 
|  | if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { | 
|  | SValBuilder &SB = C.getSValBuilder(); | 
|  | Init = SB.makeZeroVal(SB.getContext().CharTy); | 
|  | } | 
|  | SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); | 
|  | State = MallocMemAux(C, CE, TotalSize, Init, State); | 
|  | State = ProcessZeroAllocation(C, CE, 0, State); | 
|  | State = ProcessZeroAllocation(C, CE, 1, State); | 
|  | } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) { | 
|  | if (CE->getNumArgs() < 3) | 
|  | return; | 
|  | State = ReallocMemAux(C, CE, false, State, true); | 
|  | State = ProcessZeroAllocation(C, CE, 1, State); | 
|  | State = ProcessZeroAllocation(C, CE, 2, State); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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); | 
|  | break; | 
|  | case OwnershipAttr::Takes: | 
|  | case OwnershipAttr::Holds: | 
|  | State = FreeMemAttr(C, CE, I, State); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | C.addTransition(State); | 
|  | } | 
|  |  | 
|  | // Performs a 0-sized allocations check. | 
|  | 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)) { | 
|  | 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 = C.getSVal(Arg).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) { | 
|  | SymbolRef Sym = RetVal->getAsLocSymbol(); | 
|  | if (!Sym) | 
|  | return State; | 
|  |  | 
|  | const RefState *RS = State->get<RegionState>(Sym); | 
|  | if (RS) { | 
|  | if (RS->isAllocated()) | 
|  | return TrueState->set<RegionState>(Sym, | 
|  | RefState::getAllocatedOfSizeZero(RS)); | 
|  | else | 
|  | return State; | 
|  | } else { | 
|  | // Case of zero-size realloc. Historically 'realloc(ptr, 0)' is treated as | 
|  | // 'free(ptr)' and the returned value from 'realloc(ptr, 0)' is not | 
|  | // tracked. Add zero-reallocated Sym to the state to catch references | 
|  | // to zero-allocated memory. | 
|  | return TrueState->add<ReallocSizeZeroSymbols>(Sym); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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()) { | 
|  | Result = PointeeType; | 
|  | PointeeType = PointeeType->getPointeeType(); | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { | 
|  |  | 
|  | const CXXConstructExpr *ConstructE = NE->getConstructExpr(); | 
|  | if (!ConstructE) | 
|  | return false; | 
|  |  | 
|  | if (!NE->getAllocatedType()->getAsCXXRecordDecl()) | 
|  | return false; | 
|  |  | 
|  | const CXXConstructorDecl *CtorD = ConstructE->getConstructor(); | 
|  |  | 
|  | // Iterate over the constructor parameters. | 
|  | for (const auto *CtorParam : CtorD->parameters()) { | 
|  |  | 
|  | QualType CtorParamPointeeT = CtorParam->getType()->getPointeeType(); | 
|  | if (CtorParamPointeeT.isNull()) | 
|  | continue; | 
|  |  | 
|  | CtorParamPointeeT = getDeepPointeeType(CtorParamPointeeT); | 
|  |  | 
|  | if (CtorParamPointeeT->getAsCXXRecordDecl()) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void MallocChecker::processNewAllocation(const CXXNewExpr *NE, | 
|  | CheckerContext &C, | 
|  | SVal Target) const { | 
|  | if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) | 
|  | return; | 
|  |  | 
|  | const ParentMap &PM = C.getLocationContext()->getParentMap(); | 
|  | if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE)) | 
|  | return; | 
|  |  | 
|  | ProgramStateRef State = C.getState(); | 
|  | // The return value from operator new is bound to a specified initialization | 
|  | // value (if any) and we don't want to loose this value. So we call | 
|  | // MallocUpdateRefState() instead of MallocMemAux() which breaks the | 
|  | // existing binding. | 
|  | State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray | 
|  | : 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, | 
|  | SVal Target) { | 
|  | if (!State) | 
|  | return nullptr; | 
|  | SValBuilder &svalBuilder = C.getSValBuilder(); | 
|  | SVal ElementCount; | 
|  | const SubRegion *Region; | 
|  | if (NE->isArray()) { | 
|  | const Expr *SizeExpr = *NE->getArraySize(); | 
|  | ElementCount = C.getSVal(SizeExpr); | 
|  | // Store the extent size for the (symbolic)region | 
|  | // containing the elements. | 
|  | Region = Target.getAsRegion() | 
|  | ->castAs<SubRegion>() | 
|  | ->StripCasts() | 
|  | ->castAs<SubRegion>(); | 
|  | } else { | 
|  | ElementCount = svalBuilder.makeIntVal(1, true); | 
|  | Region = Target.getAsRegion()->castAs<SubRegion>(); | 
|  | } | 
|  |  | 
|  | // Set the region's extent equal to the Size in Bytes. | 
|  | QualType ElementType = NE->getAllocatedType(); | 
|  | ASTContext &AstContext = C.getASTContext(); | 
|  | CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); | 
|  |  | 
|  | if (ElementCount.getAs<NonLoc>()) { | 
|  | DefinedOrUnknownSVal Extent = Region->getExtent(svalBuilder); | 
|  | // size in Bytes = ElementCount*TypeSize | 
|  | SVal SizeInBytes = svalBuilder.evalBinOpNN( | 
|  | State, BO_Mul, ElementCount.castAs<NonLoc>(), | 
|  | svalBuilder.makeArrayIndex(TypeSize.getQuantity()), | 
|  | svalBuilder.getArrayIndexType()); | 
|  | DefinedOrUnknownSVal extentMatchesSize = svalBuilder.evalEQ( | 
|  | State, Extent, SizeInBytes.castAs<DefinedOrUnknownSVal>()); | 
|  | State = State->assume(extentMatchesSize, true); | 
|  | } | 
|  | return State; | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, | 
|  | CheckerContext &C) const { | 
|  |  | 
|  | if (!ChecksEnabled[CK_NewDeleteChecker]) | 
|  | if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) | 
|  | checkUseAfterFree(Sym, C, DE->getArgument()); | 
|  |  | 
|  | if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext())) | 
|  | return; | 
|  |  | 
|  | ProgramStateRef State = C.getState(); | 
|  | bool ReleasedAllocated; | 
|  | State = FreeMemAux(C, DE->getArgument(), DE, State, | 
|  | /*Hold*/false, ReleasedAllocated); | 
|  |  | 
|  | C.addTransition(State); | 
|  | } | 
|  |  | 
|  | static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { | 
|  | // If the first selector piece is one of the names below, assume that the | 
|  | // object takes ownership of the memory, promising to eventually deallocate it | 
|  | // with free(). | 
|  | // Ex:  [NSData dataWithBytesNoCopy:bytes length:10]; | 
|  | // (...unless a 'freeWhenDone' parameter is false, but that's checked later.) | 
|  | StringRef FirstSlot = Call.getSelector().getNameForSlot(0); | 
|  | return FirstSlot == "dataWithBytesNoCopy" || | 
|  | FirstSlot == "initWithBytesNoCopy" || | 
|  | FirstSlot == "initWithCharactersNoCopy"; | 
|  | } | 
|  |  | 
|  | static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { | 
|  | Selector S = Call.getSelector(); | 
|  |  | 
|  | // FIXME: We should not rely on fully-constrained symbols being folded. | 
|  | for (unsigned i = 1; i < S.getNumArgs(); ++i) | 
|  | if (S.getNameForSlot(i).equals("freeWhenDone")) | 
|  | return !Call.getArgSVal(i).isZeroConstant(); | 
|  |  | 
|  | return None; | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, | 
|  | CheckerContext &C) const { | 
|  | if (C.wasInlined) | 
|  | return; | 
|  |  | 
|  | if (!isKnownDeallocObjCMethodName(Call)) | 
|  | return; | 
|  |  | 
|  | if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) | 
|  | if (!*FreeWhenDone) | 
|  | return; | 
|  |  | 
|  | bool ReleasedAllocatedMemory; | 
|  | ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), | 
|  | Call.getOriginExpr(), C.getState(), | 
|  | /*Hold=*/true, ReleasedAllocatedMemory, | 
|  | /*ReturnsNullOnFailure=*/true); | 
|  |  | 
|  | C.addTransition(State); | 
|  | } | 
|  |  | 
|  | ProgramStateRef | 
|  | MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, | 
|  | 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->getASTIndex()), UndefinedVal(), | 
|  | State); | 
|  | } | 
|  | 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, C.getSVal(SizeEx), Init, State, Family); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, | 
|  | const CallExpr *CE, | 
|  | 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())) | 
|  | return nullptr; | 
|  |  | 
|  | // Bind the return value to the symbolic value from the heap region. | 
|  | // TODO: We could rewrite post visit to eval call; 'malloc' does not have | 
|  | // side effects other than what we model here. | 
|  | unsigned Count = C.blockCount(); | 
|  | SValBuilder &svalBuilder = C.getSValBuilder(); | 
|  | const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); | 
|  | DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count) | 
|  | .castAs<DefinedSVal>(); | 
|  | State = State->BindExpr(CE, C.getLocationContext(), RetVal); | 
|  |  | 
|  | // Fill the region with the initialization value. | 
|  | State = State->bindDefaultInitial(RetVal, Init, LCtx); | 
|  |  | 
|  | // Set the region's extent equal to the Size parameter. | 
|  | const SymbolicRegion *R = | 
|  | dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion()); | 
|  | if (!R) | 
|  | return nullptr; | 
|  | if (Optional<DefinedOrUnknownSVal> DefinedSize = | 
|  | Size.getAs<DefinedOrUnknownSVal>()) { | 
|  | SValBuilder &svalBuilder = C.getSValBuilder(); | 
|  | DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); | 
|  | DefinedOrUnknownSVal extentMatchesSize = | 
|  | svalBuilder.evalEQ(State, Extent, *DefinedSize); | 
|  |  | 
|  | State = State->assume(extentMatchesSize, true); | 
|  | assert(State); | 
|  | } | 
|  |  | 
|  | return MallocUpdateRefState(C, CE, State, Family); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, | 
|  | const Expr *E, | 
|  | ProgramStateRef State, | 
|  | AllocationFamily Family, | 
|  | Optional<SVal> RetVal) { | 
|  | if (!State) | 
|  | return nullptr; | 
|  |  | 
|  | // Get the return value. | 
|  | if (!RetVal) | 
|  | RetVal = C.getSVal(E); | 
|  |  | 
|  | // We expect the malloc functions to return a pointer. | 
|  | if (!RetVal->getAs<Loc>()) | 
|  | return nullptr; | 
|  |  | 
|  | 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. | 
|  | return State->set<RegionState>(Sym, RefState::getAllocated(Family, E)); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, | 
|  | const CallExpr *CE, | 
|  | const OwnershipAttr *Att, | 
|  | ProgramStateRef State) const { | 
|  | if (!State) | 
|  | return nullptr; | 
|  |  | 
|  | if (Att->getModule() != II_malloc) | 
|  | return nullptr; | 
|  |  | 
|  | bool ReleasedAllocated = false; | 
|  |  | 
|  | for (const auto &Arg : Att->args()) { | 
|  | ProgramStateRef StateI = FreeMemAux( | 
|  | C, CE, State, Arg.getASTIndex(), | 
|  | Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated); | 
|  | if (StateI) | 
|  | State = StateI; | 
|  | } | 
|  | return State; | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, | 
|  | const CallExpr *CE, | 
|  | 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, | 
|  | ReleasedAllocated, ReturnsNullOnFailure); | 
|  | } | 
|  |  | 
|  | /// Checks if the previous call to free on the given symbol failed - if free | 
|  | /// failed, returns true. Also, returns the corresponding return value symbol. | 
|  | static bool didPreviousFreeFail(ProgramStateRef State, | 
|  | SymbolRef Sym, SymbolRef &RetStatusSymbol) { | 
|  | const SymbolRef *Ret = State->get<FreeReturnValue>(Sym); | 
|  | if (Ret) { | 
|  | assert(*Ret && "We should not store the null return symbol"); | 
|  | ConstraintManager &CMgr = State->getConstraintManager(); | 
|  | ConditionTruthVal FreeFailed = CMgr.isNull(State, *Ret); | 
|  | RetStatusSymbol = *Ret; | 
|  | return FreeFailed.isConstrainedTrue(); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, | 
|  | const Stmt *S) const { | 
|  | if (!S) | 
|  | return AF_None; | 
|  |  | 
|  | if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { | 
|  | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
|  |  | 
|  | if (!FD) | 
|  | FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); | 
|  |  | 
|  | ASTContext &Ctx = C.getASTContext(); | 
|  |  | 
|  | if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any)) | 
|  | return AF_Malloc; | 
|  |  | 
|  | if (isStandardNewDelete(FD, Ctx)) { | 
|  | OverloadedOperatorKind Kind = FD->getOverloadedOperator(); | 
|  | if (Kind == OO_New || Kind == OO_Delete) | 
|  | return AF_CXXNew; | 
|  | else if (Kind == OO_Array_New || Kind == OO_Array_Delete) | 
|  | return AF_CXXNewArray; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S)) | 
|  | return NE->isArray() ? AF_CXXNewArray : AF_CXXNew; | 
|  |  | 
|  | if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S)) | 
|  | return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew; | 
|  |  | 
|  | if (isa<ObjCMessageExpr>(S)) | 
|  | return AF_Malloc; | 
|  |  | 
|  | return AF_None; | 
|  | } | 
|  |  | 
|  | bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, | 
|  | const Expr *E) const { | 
|  | if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { | 
|  | // FIXME: This doesn't handle indirect calls. | 
|  | const FunctionDecl *FD = CE->getDirectCallee(); | 
|  | if (!FD) | 
|  | return false; | 
|  |  | 
|  | os << *FD; | 
|  | if (!FD->isOverloadedOperator()) | 
|  | os << "()"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { | 
|  | if (Msg->isInstanceMessage()) | 
|  | os << "-"; | 
|  | else | 
|  | os << "+"; | 
|  | Msg->getSelector().print(os); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { | 
|  | os << "'" | 
|  | << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator()) | 
|  | << "'"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) { | 
|  | os << "'" | 
|  | << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator()) | 
|  | << "'"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, | 
|  | const Expr *E) const { | 
|  | AllocationFamily Family = getAllocationFamily(C, E); | 
|  |  | 
|  | switch(Family) { | 
|  | case AF_Malloc: os << "malloc()"; return; | 
|  | 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"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::printExpectedDeallocName(raw_ostream &os, | 
|  | AllocationFamily Family) const { | 
|  | switch(Family) { | 
|  | case AF_Malloc: os << "free()"; return; | 
|  | 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"); | 
|  | } | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, | 
|  | const Expr *ArgExpr, | 
|  | const Expr *ParentExpr, | 
|  | ProgramStateRef State, | 
|  | bool Hold, | 
|  | bool &ReleasedAllocated, | 
|  | bool ReturnsNullOnFailure) const { | 
|  |  | 
|  | if (!State) | 
|  | return nullptr; | 
|  |  | 
|  | SVal ArgVal = C.getSVal(ArgExpr); | 
|  | if (!ArgVal.getAs<DefinedOrUnknownSVal>()) | 
|  | return nullptr; | 
|  | DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>(); | 
|  |  | 
|  | // Check for null dereferences. | 
|  | if (!location.getAs<Loc>()) | 
|  | return nullptr; | 
|  |  | 
|  | // The explicit NULL case, no operation is performed. | 
|  | ProgramStateRef notNullState, nullState; | 
|  | std::tie(notNullState, nullState) = State->assume(location); | 
|  | if (nullState && !notNullState) | 
|  | return nullptr; | 
|  |  | 
|  | // Unknown values could easily be okay | 
|  | // Undefined values are handled elsewhere | 
|  | if (ArgVal.isUnknownOrUndef()) | 
|  | return nullptr; | 
|  |  | 
|  | const MemRegion *R = ArgVal.getAsRegion(); | 
|  |  | 
|  | // Nonlocs can't be freed, of course. | 
|  | // Non-region locations (labels and fixed addresses) also shouldn't be freed. | 
|  | if (!R) { | 
|  | ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | R = R->StripCasts(); | 
|  |  | 
|  | // Blocks might show up as heap data, but should not be free()d | 
|  | if (isa<BlockDataRegion>(R)) { | 
|  | ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const MemSpaceRegion *MS = R->getMemorySpace(); | 
|  |  | 
|  | // 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. | 
|  | // This means that there isn't actually anything from HeapSpaceRegion | 
|  | // that should be freed, even though we allow it here. | 
|  | // 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. | 
|  |  | 
|  | if (isa<AllocaRegion>(R)) | 
|  | ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); | 
|  | else | 
|  | ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const SymbolicRegion *SrBase = dyn_cast<SymbolicRegion>(R->getBaseRegion()); | 
|  | // Various cases could lead to non-symbol values here. | 
|  | // For now, ignore them. | 
|  | if (!SrBase) | 
|  | return nullptr; | 
|  |  | 
|  | SymbolRef SymBase = SrBase->getSymbol(); | 
|  | const RefState *RsBase = State->get<RegionState>(SymBase); | 
|  | SymbolRef PreviousRetStatusSymbol = nullptr; | 
|  |  | 
|  | 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)) { | 
|  | ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), | 
|  | SymBase, PreviousRetStatusSymbol); | 
|  | return nullptr; | 
|  |  | 
|  | // 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->isAllocatedOfSizeZero() || | 
|  | RsBase->isEscaped()) { | 
|  |  | 
|  | // Check if an expected deallocation function matches the real one. | 
|  | bool DeallocMatchesAlloc = | 
|  | RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); | 
|  | if (!DeallocMatchesAlloc) { | 
|  | ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), | 
|  | ParentExpr, RsBase, SymBase, Hold); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Check if the memory location being freed is the actual location | 
|  | // allocated, or an offset. | 
|  | RegionOffset Offset = R->getAsOffset(); | 
|  | if (Offset.isValid() && | 
|  | !Offset.hasSymbolicOffset() && | 
|  | Offset.getOffset() != 0) { | 
|  | const Expr *AllocExpr = cast<Expr>(RsBase->getStmt()); | 
|  | ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, | 
|  | AllocExpr); | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (SymBase->getType()->isFunctionPointerType()) { | 
|  | ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() || | 
|  | RsBase->isAllocatedOfSizeZero()); | 
|  |  | 
|  | // Clean out the info on previous call to free return info. | 
|  | State = State->remove<FreeReturnValue>(SymBase); | 
|  |  | 
|  | // Keep track of the return value. If it is NULL, we will know that free | 
|  | // failed. | 
|  | if (ReturnsNullOnFailure) { | 
|  | SVal RetVal = C.getSVal(ParentExpr); | 
|  | SymbolRef RetStatusSymbol = RetVal.getAsSymbol(); | 
|  | if (RetStatusSymbol) { | 
|  | C.getSymbolManager().addSymbolDependency(SymBase, RetStatusSymbol); | 
|  | State = State->set<FreeReturnValue>(SymBase, RetStatusSymbol); | 
|  | } | 
|  | } | 
|  |  | 
|  | AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() | 
|  | : getAllocationFamily(C, ParentExpr); | 
|  | // Normal free. | 
|  | if (Hold) | 
|  | return State->set<RegionState>(SymBase, | 
|  | RefState::getRelinquished(Family, | 
|  | ParentExpr)); | 
|  |  | 
|  | return State->set<RegionState>(SymBase, | 
|  | RefState::getReleased(Family, ParentExpr)); | 
|  | } | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> | 
|  | MallocChecker::getCheckIfTracked(AllocationFamily Family, | 
|  | bool IsALeakCheck) const { | 
|  | switch (Family) { | 
|  | case AF_Malloc: | 
|  | case AF_Alloca: | 
|  | case AF_IfNameIndex: { | 
|  | if (ChecksEnabled[CK_MallocChecker]) | 
|  | return CK_MallocChecker; | 
|  | return None; | 
|  | } | 
|  | case AF_CXXNew: | 
|  | case AF_CXXNewArray: { | 
|  | if (IsALeakCheck) { | 
|  | if (ChecksEnabled[CK_NewDeleteLeaksChecker]) | 
|  | return CK_NewDeleteLeaksChecker; | 
|  | } | 
|  | else { | 
|  | if (ChecksEnabled[CK_NewDeleteChecker]) | 
|  | return CK_NewDeleteChecker; | 
|  | } | 
|  | return None; | 
|  | } | 
|  | case AF_InnerBuffer: { | 
|  | if (ChecksEnabled[CK_InnerPointerChecker]) | 
|  | return CK_InnerPointerChecker; | 
|  | return None; | 
|  | } | 
|  | case AF_None: { | 
|  | llvm_unreachable("no family"); | 
|  | } | 
|  | } | 
|  | llvm_unreachable("unhandled family"); | 
|  | } | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> | 
|  | MallocChecker::getCheckIfTracked(CheckerContext &C, | 
|  | const Stmt *AllocDeallocStmt, | 
|  | bool IsALeakCheck) const { | 
|  | return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt), | 
|  | IsALeakCheck); | 
|  | } | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> | 
|  | MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, | 
|  | bool IsALeakCheck) const { | 
|  | if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) | 
|  | return CK_MallocChecker; | 
|  |  | 
|  | const RefState *RS = C.getState()->get<RegionState>(Sym); | 
|  | assert(RS); | 
|  | return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck); | 
|  | } | 
|  |  | 
|  | bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { | 
|  | if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>()) | 
|  | os << "an integer (" << IntVal->getValue() << ")"; | 
|  | else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>()) | 
|  | os << "a constant address (" << ConstAddr->getValue() << ")"; | 
|  | else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) | 
|  | os << "the address of the label '" << Label->getLabel()->getName() << "'"; | 
|  | else | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MallocChecker::SummarizeRegion(raw_ostream &os, | 
|  | const MemRegion *MR) { | 
|  | switch (MR->getKind()) { | 
|  | case MemRegion::FunctionCodeRegionKind: { | 
|  | const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl(); | 
|  | if (FD) | 
|  | os << "the address of the function '" << *FD << '\''; | 
|  | else | 
|  | os << "the address of a function"; | 
|  | return true; | 
|  | } | 
|  | case MemRegion::BlockCodeRegionKind: | 
|  | os << "block text"; | 
|  | return true; | 
|  | case MemRegion::BlockDataRegionKind: | 
|  | // FIXME: where the block came from? | 
|  | os << "a block"; | 
|  | return true; | 
|  | default: { | 
|  | const MemSpaceRegion *MS = MR->getMemorySpace(); | 
|  |  | 
|  | if (isa<StackLocalsSpaceRegion>(MS)) { | 
|  | const VarRegion *VR = dyn_cast<VarRegion>(MR); | 
|  | const VarDecl *VD; | 
|  | if (VR) | 
|  | VD = VR->getDecl(); | 
|  | else | 
|  | VD = nullptr; | 
|  |  | 
|  | if (VD) | 
|  | os << "the address of the local variable '" << VD->getName() << "'"; | 
|  | else | 
|  | os << "the address of a local stack variable"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (isa<StackArgumentsSpaceRegion>(MS)) { | 
|  | const VarRegion *VR = dyn_cast<VarRegion>(MR); | 
|  | const VarDecl *VD; | 
|  | if (VR) | 
|  | VD = VR->getDecl(); | 
|  | else | 
|  | VD = nullptr; | 
|  |  | 
|  | if (VD) | 
|  | os << "the address of the parameter '" << VD->getName() << "'"; | 
|  | else | 
|  | os << "the address of a parameter"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (isa<GlobalsSpaceRegion>(MS)) { | 
|  | const VarRegion *VR = dyn_cast<VarRegion>(MR); | 
|  | const VarDecl *VD; | 
|  | if (VR) | 
|  | VD = VR->getDecl(); | 
|  | else | 
|  | VD = nullptr; | 
|  |  | 
|  | if (VD) { | 
|  | if (VD->isStaticLocal()) | 
|  | os << "the address of the static variable '" << VD->getName() << "'"; | 
|  | else | 
|  | os << "the address of the global variable '" << VD->getName() << "'"; | 
|  | } else | 
|  | os << "the address of a global variable"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, | 
|  | SourceRange Range, | 
|  | const Expr *DeallocExpr) const { | 
|  |  | 
|  | if (!ChecksEnabled[CK_MallocChecker] && | 
|  | !ChecksEnabled[CK_NewDeleteChecker]) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> CheckKind = | 
|  | getCheckIfTracked(C, DeallocExpr); | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | if (ExplodedNode *N = C.generateErrorNode()) { | 
|  | if (!BT_BadFree[*CheckKind]) | 
|  | BT_BadFree[*CheckKind].reset(new BugType( | 
|  | CheckNames[*CheckKind], "Bad free", categories::MemoryError)); | 
|  |  | 
|  | SmallString<100> buf; | 
|  | llvm::raw_svector_ostream os(buf); | 
|  |  | 
|  | const MemRegion *MR = ArgVal.getAsRegion(); | 
|  | while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR)) | 
|  | MR = ER->getSuperRegion(); | 
|  |  | 
|  | 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 "; | 
|  |  | 
|  | printExpectedAllocName(os, C, DeallocExpr); | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind], | 
|  | os.str(), N); | 
|  | R->markInteresting(MR); | 
|  | R->addRange(Range); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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.generateErrorNode()) { | 
|  | if (!BT_FreeAlloca[*CheckKind]) | 
|  | BT_FreeAlloca[*CheckKind].reset(new BugType( | 
|  | CheckNames[*CheckKind], "Free alloca()", categories::MemoryError)); | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT_FreeAlloca[*CheckKind], | 
|  | "Memory allocated by alloca() should not be deallocated", N); | 
|  | R->markInteresting(ArgVal.getAsRegion()); | 
|  | R->addRange(Range); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, | 
|  | SourceRange Range, | 
|  | const Expr *DeallocExpr, | 
|  | const RefState *RS, | 
|  | SymbolRef Sym, | 
|  | bool OwnershipTransferred) const { | 
|  |  | 
|  | if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) | 
|  | return; | 
|  |  | 
|  | if (ExplodedNode *N = C.generateErrorNode()) { | 
|  | if (!BT_MismatchedDealloc) | 
|  | BT_MismatchedDealloc.reset( | 
|  | new BugType(CheckNames[CK_MismatchedDeallocatorChecker], | 
|  | "Bad deallocator", categories::MemoryError)); | 
|  |  | 
|  | SmallString<100> buf; | 
|  | llvm::raw_svector_ostream os(buf); | 
|  |  | 
|  | const Expr *AllocExpr = cast<Expr>(RS->getStmt()); | 
|  | SmallString<20> AllocBuf; | 
|  | llvm::raw_svector_ostream AllocOs(AllocBuf); | 
|  | SmallString<20> DeallocBuf; | 
|  | llvm::raw_svector_ostream DeallocOs(DeallocBuf); | 
|  |  | 
|  | if (OwnershipTransferred) { | 
|  | if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) | 
|  | os << DeallocOs.str() << " cannot"; | 
|  | else | 
|  | os << "Cannot"; | 
|  |  | 
|  | os << " take ownership of memory"; | 
|  |  | 
|  | if (printAllocDeallocName(AllocOs, C, AllocExpr)) | 
|  | os << " allocated by " << AllocOs.str(); | 
|  | } else { | 
|  | os << "Memory"; | 
|  | if (printAllocDeallocName(AllocOs, C, AllocExpr)) | 
|  | os << " allocated by " << AllocOs.str(); | 
|  |  | 
|  | os << " should be deallocated by "; | 
|  | printExpectedDeallocName(os, RS->getAllocationFamily()); | 
|  |  | 
|  | if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) | 
|  | os << ", not " << DeallocOs.str(); | 
|  | } | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc, | 
|  | os.str(), N); | 
|  | R->markInteresting(Sym); | 
|  | R->addRange(Range); | 
|  | R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, | 
|  | SourceRange Range, const Expr *DeallocExpr, | 
|  | const Expr *AllocExpr) const { | 
|  |  | 
|  |  | 
|  | if (!ChecksEnabled[CK_MallocChecker] && | 
|  | !ChecksEnabled[CK_NewDeleteChecker]) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> CheckKind = | 
|  | getCheckIfTracked(C, AllocExpr); | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | ExplodedNode *N = C.generateErrorNode(); | 
|  | if (!N) | 
|  | return; | 
|  |  | 
|  | if (!BT_OffsetFree[*CheckKind]) | 
|  | BT_OffsetFree[*CheckKind].reset(new BugType( | 
|  | CheckNames[*CheckKind], "Offset free", categories::MemoryError)); | 
|  |  | 
|  | SmallString<100> buf; | 
|  | llvm::raw_svector_ostream os(buf); | 
|  | SmallString<20> AllocNameBuf; | 
|  | llvm::raw_svector_ostream AllocNameOs(AllocNameBuf); | 
|  |  | 
|  | const MemRegion *MR = ArgVal.getAsRegion(); | 
|  | assert(MR && "Only MemRegion based symbols can have offset free errors"); | 
|  |  | 
|  | RegionOffset Offset = MR->getAsOffset(); | 
|  | assert((Offset.isValid() && | 
|  | !Offset.hasSymbolicOffset() && | 
|  | Offset.getOffset() != 0) && | 
|  | "Only symbols with a valid offset can have offset free errors"); | 
|  |  | 
|  | int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth(); | 
|  |  | 
|  | os << "Argument to "; | 
|  | if (!printAllocDeallocName(os, C, DeallocExpr)) | 
|  | os << "deallocator"; | 
|  | os << " is offset by " | 
|  | << offsetBytes | 
|  | << " " | 
|  | << ((abs(offsetBytes) > 1) ? "bytes" : "byte") | 
|  | << " from the start of "; | 
|  | if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr)) | 
|  | os << "memory allocated by " << AllocNameOs.str(); | 
|  | else | 
|  | os << "allocated memory"; | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>(*BT_OffsetFree[*CheckKind], | 
|  | os.str(), N); | 
|  | R->markInteresting(MR->getBaseRegion()); | 
|  | R->addRange(Range); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, | 
|  | SymbolRef Sym) const { | 
|  |  | 
|  | if (!ChecksEnabled[CK_MallocChecker] && | 
|  | !ChecksEnabled[CK_NewDeleteChecker] && | 
|  | !ChecksEnabled[CK_InnerPointerChecker]) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | if (ExplodedNode *N = C.generateErrorNode()) { | 
|  | if (!BT_UseFree[*CheckKind]) | 
|  | BT_UseFree[*CheckKind].reset(new BugType( | 
|  | CheckNames[*CheckKind], "Use-after-free", categories::MemoryError)); | 
|  |  | 
|  | AllocationFamily AF = | 
|  | C.getState()->get<RegionState>(Sym)->getAllocationFamily(); | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT_UseFree[*CheckKind], | 
|  | AF == AF_InnerBuffer | 
|  | ? "Inner pointer of container used after re/deallocation" | 
|  | : "Use of memory after it is freed", | 
|  | N); | 
|  |  | 
|  | R->markInteresting(Sym); | 
|  | R->addRange(Range); | 
|  | R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); | 
|  |  | 
|  | if (AF == AF_InnerBuffer) | 
|  | R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); | 
|  |  | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, | 
|  | bool Released, SymbolRef Sym, | 
|  | SymbolRef PrevSym) const { | 
|  |  | 
|  | if (!ChecksEnabled[CK_MallocChecker] && | 
|  | !ChecksEnabled[CK_NewDeleteChecker]) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | if (ExplodedNode *N = C.generateErrorNode()) { | 
|  | if (!BT_DoubleFree[*CheckKind]) | 
|  | BT_DoubleFree[*CheckKind].reset(new BugType( | 
|  | CheckNames[*CheckKind], "Double free", categories::MemoryError)); | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT_DoubleFree[*CheckKind], | 
|  | (Released ? "Attempt to free released memory" | 
|  | : "Attempt to free non-owned memory"), | 
|  | N); | 
|  | R->addRange(Range); | 
|  | R->markInteresting(Sym); | 
|  | if (PrevSym) | 
|  | R->markInteresting(PrevSym); | 
|  | R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { | 
|  |  | 
|  | if (!ChecksEnabled[CK_NewDeleteChecker]) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | if (ExplodedNode *N = C.generateErrorNode()) { | 
|  | if (!BT_DoubleDelete) | 
|  | BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker], | 
|  | "Double delete", | 
|  | categories::MemoryError)); | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT_DoubleDelete, "Attempt to delete released memory", N); | 
|  |  | 
|  | R->markInteresting(Sym); | 
|  | R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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.generateErrorNode()) { | 
|  | if (!BT_UseZerroAllocated[*CheckKind]) | 
|  | BT_UseZerroAllocated[*CheckKind].reset( | 
|  | new BugType(CheckNames[*CheckKind], "Use of zero allocated", | 
|  | categories::MemoryError)); | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT_UseZerroAllocated[*CheckKind], "Use of zero-allocated memory", N); | 
|  |  | 
|  | R->addRange(Range); | 
|  | if (Sym) { | 
|  | R->markInteresting(Sym); | 
|  | R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); | 
|  | } | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, | 
|  | SourceRange Range, | 
|  | const Expr *FreeExpr) const { | 
|  | if (!ChecksEnabled[CK_MallocChecker]) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, FreeExpr); | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | if (ExplodedNode *N = C.generateErrorNode()) { | 
|  | if (!BT_BadFree[*CheckKind]) | 
|  | BT_BadFree[*CheckKind].reset(new BugType( | 
|  | CheckNames[*CheckKind], "Bad free", categories::MemoryError)); | 
|  |  | 
|  | SmallString<100> Buf; | 
|  | llvm::raw_svector_ostream Os(Buf); | 
|  |  | 
|  | const MemRegion *MR = ArgVal.getAsRegion(); | 
|  | while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR)) | 
|  | MR = ER->getSuperRegion(); | 
|  |  | 
|  | Os << "Argument to "; | 
|  | if (!printAllocDeallocName(Os, C, FreeExpr)) | 
|  | Os << "deallocator"; | 
|  |  | 
|  | Os << " is a function pointer"; | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind], | 
|  | Os.str(), N); | 
|  | R->markInteresting(MR); | 
|  | R->addRange(Range); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, | 
|  | const CallExpr *CE, | 
|  | bool FreesOnFail, | 
|  | ProgramStateRef State, | 
|  | bool SuffixWithN) const { | 
|  | if (!State) | 
|  | return nullptr; | 
|  |  | 
|  | if (SuffixWithN && CE->getNumArgs() < 3) | 
|  | return nullptr; | 
|  | else if (CE->getNumArgs() < 2) | 
|  | return nullptr; | 
|  |  | 
|  | const Expr *arg0Expr = CE->getArg(0); | 
|  | SVal Arg0Val = C.getSVal(arg0Expr); | 
|  | if (!Arg0Val.getAs<DefinedOrUnknownSVal>()) | 
|  | return nullptr; | 
|  | DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>(); | 
|  |  | 
|  | SValBuilder &svalBuilder = C.getSValBuilder(); | 
|  |  | 
|  | DefinedOrUnknownSVal PtrEQ = | 
|  | svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull()); | 
|  |  | 
|  | // Get the size argument. | 
|  | const Expr *Arg1 = CE->getArg(1); | 
|  |  | 
|  | // Get the value of the size argument. | 
|  | SVal TotalSize = C.getSVal(Arg1); | 
|  | if (SuffixWithN) | 
|  | TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2)); | 
|  | if (!TotalSize.getAs<DefinedOrUnknownSVal>()) | 
|  | return nullptr; | 
|  |  | 
|  | // Compare the size argument to 0. | 
|  | DefinedOrUnknownSVal SizeZero = | 
|  | svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(), | 
|  | svalBuilder.makeIntValWithPtrWidth(0, false)); | 
|  |  | 
|  | ProgramStateRef StatePtrIsNull, StatePtrNotNull; | 
|  | std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ); | 
|  | ProgramStateRef StateSizeIsZero, StateSizeNotZero; | 
|  | 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; | 
|  | bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero; | 
|  |  | 
|  | // If the ptr is NULL and the size is not 0, the call is equivalent to | 
|  | // malloc(size). | 
|  | if (PrtIsNull && !SizeIsZero) { | 
|  | ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize, | 
|  | UndefinedVal(), StatePtrIsNull); | 
|  | return stateMalloc; | 
|  | } | 
|  |  | 
|  | if (PrtIsNull && SizeIsZero) | 
|  | return State; | 
|  |  | 
|  | // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size). | 
|  | assert(!PrtIsNull); | 
|  | SymbolRef FromPtr = arg0Val.getAsSymbol(); | 
|  | SVal RetVal = C.getSVal(CE); | 
|  | SymbolRef ToPtr = RetVal.getAsSymbol(); | 
|  | if (!FromPtr || !ToPtr) | 
|  | return nullptr; | 
|  |  | 
|  | bool ReleasedAllocated = false; | 
|  |  | 
|  | // If the size is 0, free the memory. | 
|  | if (SizeIsZero) | 
|  | if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0, | 
|  | false, ReleasedAllocated)){ | 
|  | // The semantics of the return value are: | 
|  | // If size was equal to 0, either NULL or a pointer suitable to be passed | 
|  | // to free() is returned. We just free the input pointer and do not add | 
|  | // any constrains on the output pointer. | 
|  | return stateFree; | 
|  | } | 
|  |  | 
|  | // Default behavior. | 
|  | if (ProgramStateRef stateFree = | 
|  | FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) { | 
|  |  | 
|  | ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize, | 
|  | UnknownVal(), stateFree); | 
|  | if (!stateRealloc) | 
|  | return nullptr; | 
|  |  | 
|  | ReallocPairKind Kind = RPToBeFreedAfterFailure; | 
|  | if (FreesOnFail) | 
|  | Kind = RPIsFreeOnFailure; | 
|  | else if (!ReleasedAllocated) | 
|  | Kind = RPDoNotTrackAfterFailure; | 
|  |  | 
|  | // Record the info about the reallocated symbol so that we could properly | 
|  | // process failed reallocation. | 
|  | stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr, | 
|  | ReallocPair(FromPtr, Kind)); | 
|  | // The reallocated symbol should stay alive for as long as the new symbol. | 
|  | C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); | 
|  | return stateRealloc; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, | 
|  | ProgramStateRef State) { | 
|  | if (!State) | 
|  | return nullptr; | 
|  |  | 
|  | if (CE->getNumArgs() < 2) | 
|  | return nullptr; | 
|  |  | 
|  | SValBuilder &svalBuilder = C.getSValBuilder(); | 
|  | SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); | 
|  | SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); | 
|  |  | 
|  | return MallocMemAux(C, CE, TotalSize, zeroVal, State); | 
|  | } | 
|  |  | 
|  | LeakInfo | 
|  | MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, | 
|  | CheckerContext &C) const { | 
|  | const LocationContext *LeakContext = N->getLocationContext(); | 
|  | // Walk the ExplodedGraph backwards and find the first node that referred to | 
|  | // the tracked symbol. | 
|  | const ExplodedNode *AllocNode = N; | 
|  | const MemRegion *ReferenceRegion = nullptr; | 
|  |  | 
|  | while (N) { | 
|  | ProgramStateRef State = N->getState(); | 
|  | if (!State->get<RegionState>(Sym)) | 
|  | break; | 
|  |  | 
|  | // Find the most recent expression bound to the symbol in the current | 
|  | // context. | 
|  | if (!ReferenceRegion) { | 
|  | if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { | 
|  | SVal Val = State->getSVal(MR); | 
|  | if (Val.getAsLocSymbol() == Sym) { | 
|  | const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>(); | 
|  | // Do not show local variables belonging to a function other than | 
|  | // where the error is reported. | 
|  | if (!VR || | 
|  | (VR->getStackFrame() == LeakContext->getStackFrame())) | 
|  | ReferenceRegion = MR; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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()); | 
|  | } | 
|  |  | 
|  | return LeakInfo(AllocNode, ReferenceRegion); | 
|  | } | 
|  |  | 
|  | void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, | 
|  | CheckerContext &C) const { | 
|  |  | 
|  | 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(); | 
|  |  | 
|  | if (Family == AF_Alloca) | 
|  | return; | 
|  |  | 
|  | Optional<MallocChecker::CheckKind> | 
|  | CheckKind = getCheckIfTracked(Family, true); | 
|  |  | 
|  | if (!CheckKind.hasValue()) | 
|  | return; | 
|  |  | 
|  | assert(N); | 
|  | if (!BT_Leak[*CheckKind]) { | 
|  | // Leaks should not be reported if they are post-dominated by a sink: | 
|  | // (1) Sinks are higher importance bugs. | 
|  | // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending | 
|  | //     with __noreturn functions such as assert() or exit(). We choose not | 
|  | //     to report leaks on such paths. | 
|  | BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", | 
|  | categories::MemoryError, | 
|  | /*SuppressOnSink=*/true)); | 
|  | } | 
|  |  | 
|  | // Most bug reports are cached at the location where they occurred. | 
|  | // With leaks, we want to unique them by the location where they were | 
|  | // allocated, and only report a single path. | 
|  | PathDiagnosticLocation LocUsedForUniqueing; | 
|  | const ExplodedNode *AllocNode = nullptr; | 
|  | const MemRegion *Region = nullptr; | 
|  | std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C); | 
|  |  | 
|  | const Stmt *AllocationStmt = PathDiagnosticLocation::getStmt(AllocNode); | 
|  | if (AllocationStmt) | 
|  | LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt, | 
|  | C.getSourceManager(), | 
|  | AllocNode->getLocationContext()); | 
|  |  | 
|  | SmallString<200> buf; | 
|  | llvm::raw_svector_ostream os(buf); | 
|  | if (Region && Region->canPrintPretty()) { | 
|  | os << "Potential leak of memory pointed to by "; | 
|  | Region->printPretty(os); | 
|  | } else { | 
|  | os << "Potential memory leak"; | 
|  | } | 
|  |  | 
|  | auto R = std::make_unique<PathSensitiveBugReport>( | 
|  | *BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, | 
|  | AllocNode->getLocationContext()->getDecl()); | 
|  | R->markInteresting(Sym); | 
|  | R->addVisitor(std::make_unique<MallocBugVisitor>(Sym, true)); | 
|  | C.emitReport(std::move(R)); | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, | 
|  | CheckerContext &C) const | 
|  | { | 
|  | ProgramStateRef state = C.getState(); | 
|  | RegionStateTy OldRS = state->get<RegionState>(); | 
|  | RegionStateTy::Factory &F = state->get_context<RegionState>(); | 
|  |  | 
|  | RegionStateTy RS = OldRS; | 
|  | 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() || I->second.isAllocatedOfSizeZero()) | 
|  | Errors.push_back(I->first); | 
|  | // Remove the dead symbol from the map. | 
|  | RS = F.remove(RS, I->first); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (RS == OldRS) { | 
|  | // We shouldn't have touched other maps yet. | 
|  | assert(state->get<ReallocPairs>() == | 
|  | C.getState()->get<ReallocPairs>()); | 
|  | assert(state->get<FreeReturnValue>() == | 
|  | C.getState()->get<FreeReturnValue>()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Cleanup the Realloc Pairs Map. | 
|  | ReallocPairsTy RP = state->get<ReallocPairs>(); | 
|  | for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { | 
|  | if (SymReaper.isDead(I->first) || | 
|  | SymReaper.isDead(I->second.ReallocatedSym)) { | 
|  | state = state->remove<ReallocPairs>(I->first); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Cleanup the FreeReturnValue Map. | 
|  | FreeReturnValueTy FR = state->get<FreeReturnValue>(); | 
|  | for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) { | 
|  | if (SymReaper.isDead(I->first) || | 
|  | SymReaper.isDead(I->second)) { | 
|  | state = state->remove<FreeReturnValue>(I->first); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Generate leak node. | 
|  | ExplodedNode *N = C.getPredecessor(); | 
|  | if (!Errors.empty()) { | 
|  | static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak"); | 
|  | N = C.generateNonFatalErrorNode(C.getState(), &Tag); | 
|  | if (N) { | 
|  | for (SmallVectorImpl<SymbolRef>::iterator | 
|  | I = Errors.begin(), E = Errors.end(); I != E; ++I) { | 
|  | reportLeak(*I, N, C); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | C.addTransition(state->set<RegionState>(RS), N); | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkPreCall(const CallEvent &Call, | 
|  | CheckerContext &C) const { | 
|  |  | 
|  | if (const CXXDestructorCall *DC = dyn_cast<CXXDestructorCall>(&Call)) { | 
|  | SymbolRef Sym = DC->getCXXThisVal().getAsSymbol(); | 
|  | if (!Sym || checkDoubleDelete(Sym, C)) | 
|  | return; | 
|  | } | 
|  |  | 
|  | // We will check for double free in the post visit. | 
|  | if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) { | 
|  | const FunctionDecl *FD = FC->getDecl(); | 
|  | if (!FD) | 
|  | return; | 
|  |  | 
|  | ASTContext &Ctx = C.getASTContext(); | 
|  | if (ChecksEnabled[CK_MallocChecker] && | 
|  | (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || | 
|  | isCMemFunction(FD, Ctx, AF_IfNameIndex, | 
|  | MemoryOperationKind::MOK_Free))) | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check if the callee of a method is deleted. | 
|  | if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { | 
|  | SymbolRef Sym = CC->getCXXThisVal().getAsSymbol(); | 
|  | if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr())) | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check arguments for being used after free. | 
|  | for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) { | 
|  | SVal ArgSVal = Call.getArgSVal(I); | 
|  | if (ArgSVal.getAs<Loc>()) { | 
|  | SymbolRef Sym = ArgSVal.getAsSymbol(); | 
|  | if (!Sym) | 
|  | continue; | 
|  | if (checkUseAfterFree(Sym, C, Call.getArgExpr(I))) | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkPreStmt(const ReturnStmt *S, | 
|  | CheckerContext &C) const { | 
|  | checkEscapeOnReturn(S, C); | 
|  | } | 
|  |  | 
|  | // In the CFG, automatic destructors come after the return statement. | 
|  | // This callback checks for returning memory that is freed by automatic | 
|  | // destructors, as those cannot be reached in checkPreStmt(). | 
|  | void MallocChecker::checkEndFunction(const ReturnStmt *S, | 
|  | CheckerContext &C) const { | 
|  | checkEscapeOnReturn(S, C); | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S, | 
|  | CheckerContext &C) const { | 
|  | if (!S) | 
|  | return; | 
|  |  | 
|  | const Expr *E = S->getRetValue(); | 
|  | if (!E) | 
|  | return; | 
|  |  | 
|  | // Check if we are returning a symbol. | 
|  | ProgramStateRef State = C.getState(); | 
|  | 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, | 
|  | // the callee could still free the memory. | 
|  | // TODO: This logic should be a part of generic symbol escape callback. | 
|  | if (const MemRegion *MR = RetVal.getAsRegion()) | 
|  | if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR)) | 
|  | if (const SymbolicRegion *BMR = | 
|  | dyn_cast<SymbolicRegion>(MR->getBaseRegion())) | 
|  | Sym = BMR->getSymbol(); | 
|  |  | 
|  | // Check if we are returning freed memory. | 
|  | if (Sym) | 
|  | checkUseAfterFree(Sym, C, E); | 
|  | } | 
|  |  | 
|  | // TODO: Blocks should be either inlined or should call invalidate regions | 
|  | // upon invocation. After that's in place, special casing here will not be | 
|  | // needed. | 
|  | void MallocChecker::checkPostStmt(const BlockExpr *BE, | 
|  | CheckerContext &C) const { | 
|  |  | 
|  | // Scan the BlockDecRefExprs for any object the retain count checker | 
|  | // may be tracking. | 
|  | if (!BE->getBlockDecl()->hasCaptures()) | 
|  | return; | 
|  |  | 
|  | ProgramStateRef state = C.getState(); | 
|  | const BlockDataRegion *R = | 
|  | cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); | 
|  |  | 
|  | BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), | 
|  | E = R->referenced_vars_end(); | 
|  |  | 
|  | if (I == E) | 
|  | return; | 
|  |  | 
|  | SmallVector<const MemRegion*, 10> Regions; | 
|  | const LocationContext *LC = C.getLocationContext(); | 
|  | MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); | 
|  |  | 
|  | for ( ; I != E; ++I) { | 
|  | const VarRegion *VR = I.getCapturedRegion(); | 
|  | if (VR->getSuperRegion() == R) { | 
|  | VR = MemMgr.getVarRegion(VR->getDecl(), LC); | 
|  | } | 
|  | Regions.push_back(VR); | 
|  | } | 
|  |  | 
|  | state = | 
|  | state->scanReachableSymbols<StopTrackingCallback>(Regions).getState(); | 
|  | C.addTransition(state); | 
|  | } | 
|  |  | 
|  | bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { | 
|  | assert(Sym); | 
|  | const RefState *RS = C.getState()->get<RegionState>(Sym); | 
|  | return (RS && RS->isReleased()); | 
|  | } | 
|  |  | 
|  | bool MallocChecker::suppressDeallocationsInSuspiciousContexts( | 
|  | const CallExpr *CE, CheckerContext &C) const { | 
|  | if (CE->getNumArgs() == 0) | 
|  | return false; | 
|  |  | 
|  | StringRef FunctionStr = ""; | 
|  | if (const auto *FD = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) | 
|  | if (const Stmt *Body = FD->getBody()) | 
|  | if (Body->getBeginLoc().isValid()) | 
|  | FunctionStr = | 
|  | Lexer::getSourceText(CharSourceRange::getTokenRange( | 
|  | {FD->getBeginLoc(), Body->getBeginLoc()}), | 
|  | C.getSourceManager(), C.getLangOpts()); | 
|  |  | 
|  | // We do not model the Integer Set Library's retain-count based allocation. | 
|  | if (!FunctionStr.contains("__isl_")) | 
|  | return false; | 
|  |  | 
|  | ProgramStateRef State = C.getState(); | 
|  |  | 
|  | for (const Expr *Arg : CE->arguments()) | 
|  | if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol()) | 
|  | if (const RefState *RS = State->get<RegionState>(Sym)) | 
|  | State = State->set<RegionState>(Sym, RefState::getEscaped(RS)); | 
|  |  | 
|  | C.addTransition(State); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, | 
|  | const Stmt *S) const { | 
|  |  | 
|  | if (isReleased(Sym, C)) { | 
|  | ReportUseAfterFree(C, S->getSourceRange(), Sym); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, | 
|  | const Stmt *S) const { | 
|  | assert(Sym); | 
|  |  | 
|  | if (const RefState *RS = C.getState()->get<RegionState>(Sym)) { | 
|  | if (RS->isAllocatedOfSizeZero()) | 
|  | ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym); | 
|  | } | 
|  | else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) { | 
|  | ReportUseZeroAllocated(C, S->getSourceRange(), Sym); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const { | 
|  |  | 
|  | if (isReleased(Sym, C)) { | 
|  | ReportDoubleDelete(C, Sym); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Check if the location is a freed symbolic region. | 
|  | void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, | 
|  | CheckerContext &C) const { | 
|  | SymbolRef Sym = l.getLocSymbolInBase(); | 
|  | if (Sym) { | 
|  | checkUseAfterFree(Sym, C, S); | 
|  | checkUseZeroAllocated(Sym, C, S); | 
|  | } | 
|  | } | 
|  |  | 
|  | // If a symbolic region is assumed to NULL (or another constant), stop tracking | 
|  | // it - assuming that allocation failed on this path. | 
|  | ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, | 
|  | SVal Cond, | 
|  | bool Assumption) const { | 
|  | RegionStateTy RS = state->get<RegionState>(); | 
|  | for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { | 
|  | // If the symbol is assumed to be NULL, remove it from consideration. | 
|  | ConstraintManager &CMgr = state->getConstraintManager(); | 
|  | ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); | 
|  | if (AllocFailed.isConstrainedTrue()) | 
|  | state = state->remove<RegionState>(I.getKey()); | 
|  | } | 
|  |  | 
|  | // Realloc returns 0 when reallocation fails, which means that we should | 
|  | // restore the state of the pointer being reallocated. | 
|  | ReallocPairsTy RP = state->get<ReallocPairs>(); | 
|  | for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { | 
|  | // If the symbol is assumed to be NULL, remove it from consideration. | 
|  | ConstraintManager &CMgr = state->getConstraintManager(); | 
|  | ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); | 
|  | if (!AllocFailed.isConstrainedTrue()) | 
|  | continue; | 
|  |  | 
|  | SymbolRef ReallocSym = I.getData().ReallocatedSym; | 
|  | if (const RefState *RS = state->get<RegionState>(ReallocSym)) { | 
|  | if (RS->isReleased()) { | 
|  | if (I.getData().Kind == RPToBeFreedAfterFailure) | 
|  | state = state->set<RegionState>(ReallocSym, | 
|  | RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt())); | 
|  | else if (I.getData().Kind == RPDoNotTrackAfterFailure) | 
|  | state = state->remove<RegionState>(ReallocSym); | 
|  | else | 
|  | assert(I.getData().Kind == RPIsFreeOnFailure); | 
|  | } | 
|  | } | 
|  | state = state->remove<ReallocPairs>(I.getKey()); | 
|  | } | 
|  |  | 
|  | return state; | 
|  | } | 
|  |  | 
|  | bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( | 
|  | const CallEvent *Call, | 
|  | ProgramStateRef State, | 
|  | SymbolRef &EscapingSymbol) const { | 
|  | assert(Call); | 
|  | EscapingSymbol = nullptr; | 
|  |  | 
|  | // For now, assume that any C++ or block call can free memory. | 
|  | // TODO: If we want to be more optimistic here, we'll need to make sure that | 
|  | // regions escape to C++ containers. They seem to do that even now, but for | 
|  | // mysterious reasons. | 
|  | if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call))) | 
|  | return true; | 
|  |  | 
|  | // Check Objective-C messages by selector name. | 
|  | if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { | 
|  | // If it's not a framework call, or if it takes a callback, assume it | 
|  | // can free memory. | 
|  | if (!Call->isInSystemHeader() || Call->argumentsMayEscape()) | 
|  | return true; | 
|  |  | 
|  | // If it's a method we know about, handle it explicitly post-call. | 
|  | // This should happen before the "freeWhenDone" check below. | 
|  | if (isKnownDeallocObjCMethodName(*Msg)) | 
|  | return false; | 
|  |  | 
|  | // If there's a "freeWhenDone" parameter, but the method isn't one we know | 
|  | // about, we can't be sure that the object will use free() to deallocate the | 
|  | // memory, so we can't model it explicitly. The best we can do is use it to | 
|  | // decide whether the pointer escapes. | 
|  | if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) | 
|  | return *FreeWhenDone; | 
|  |  | 
|  | // If the first selector piece ends with "NoCopy", and there is no | 
|  | // "freeWhenDone" parameter set to zero, we know ownership is being | 
|  | // transferred. Again, though, we can't be sure that the object will use | 
|  | // free() to deallocate the memory, so we can't model it explicitly. | 
|  | StringRef FirstSlot = Msg->getSelector().getNameForSlot(0); | 
|  | if (FirstSlot.endswith("NoCopy")) | 
|  | return true; | 
|  |  | 
|  | // If the first selector starts with addPointer, insertPointer, | 
|  | // or replacePointer, assume we are dealing with NSPointerArray or similar. | 
|  | // This is similar to C++ containers (vector); we still might want to check | 
|  | // that the pointers get freed by following the container itself. | 
|  | if (FirstSlot.startswith("addPointer") || | 
|  | FirstSlot.startswith("insertPointer") || | 
|  | FirstSlot.startswith("replacePointer") || | 
|  | FirstSlot.equals("valueWithPointer")) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // We should escape receiver on call to 'init'. This is especially relevant | 
|  | // to the receiver, as the corresponding symbol is usually not referenced | 
|  | // after the call. | 
|  | if (Msg->getMethodFamily() == OMF_init) { | 
|  | EscapingSymbol = Msg->getReceiverSVal().getAsSymbol(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Otherwise, assume that the method does not free memory. | 
|  | // Most framework methods do not free memory. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // At this point the only thing left to handle is straight function calls. | 
|  | const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl(); | 
|  | if (!FD) | 
|  | return true; | 
|  |  | 
|  | ASTContext &ASTC = State->getStateManager().getContext(); | 
|  |  | 
|  | // If it's one of the allocation functions we can reason about, we model | 
|  | // its behavior explicitly. | 
|  | if (isMemFunction(FD, ASTC)) | 
|  | return false; | 
|  |  | 
|  | // If it's not a system call, assume it frees memory. | 
|  | if (!Call->isInSystemHeader()) | 
|  | return true; | 
|  |  | 
|  | // White list the system functions whose arguments escape. | 
|  | const IdentifierInfo *II = FD->getIdentifier(); | 
|  | if (!II) | 
|  | return true; | 
|  | StringRef FName = II->getName(); | 
|  |  | 
|  | // White list the 'XXXNoCopy' CoreFoundation functions. | 
|  | // We specifically check these before | 
|  | if (FName.endswith("NoCopy")) { | 
|  | // Look for the deallocator argument. We know that the memory ownership | 
|  | // is not transferred only if the deallocator argument is | 
|  | // 'kCFAllocatorNull'. | 
|  | for (unsigned i = 1; i < Call->getNumArgs(); ++i) { | 
|  | const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts(); | 
|  | if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { | 
|  | StringRef DeallocatorName = DE->getFoundDecl()->getName(); | 
|  | if (DeallocatorName == "kCFAllocatorNull") | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Associating streams with malloced buffers. The pointer can escape if | 
|  | // 'closefn' is specified (and if that function does free memory), | 
|  | // but it will not if closefn is not specified. | 
|  | // Currently, we do not inspect the 'closefn' function (PR12101). | 
|  | if (FName == "funopen") | 
|  | if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0)) | 
|  | return false; | 
|  |  | 
|  | // Do not warn on pointers passed to 'setbuf' when used with std streams, | 
|  | // these leaks might be intentional when setting the buffer for stdio. | 
|  | // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer | 
|  | if (FName == "setbuf" || FName =="setbuffer" || | 
|  | FName == "setlinebuf" || FName == "setvbuf") { | 
|  | if (Call->getNumArgs() >= 1) { | 
|  | const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts(); | 
|  | if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE)) | 
|  | if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl())) | 
|  | if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos) | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // A bunch of other functions which either take ownership of a pointer or | 
|  | // wrap the result up in a struct or object, meaning it can be freed later. | 
|  | // (See RetainCountChecker.) Not all the parameters here are invalidated, | 
|  | // but the Malloc checker cannot differentiate between them. The right way | 
|  | // of doing this would be to implement a pointer escapes callback. | 
|  | if (FName == "CGBitmapContextCreate" || | 
|  | FName == "CGBitmapContextCreateWithData" || | 
|  | FName == "CVPixelBufferCreateWithBytes" || | 
|  | FName == "CVPixelBufferCreateWithPlanarBytes" || | 
|  | FName == "OSAtomicEnqueue") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (FName == "postEvent" && | 
|  | FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (FName == "postEvent" && | 
|  | FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (FName == "connectImpl" && | 
|  | FD->getQualifiedNameAsString() == "QObject::connectImpl") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Handle cases where we know a buffer's /address/ can escape. | 
|  | // Note that the above checks handle some special cases where we know that | 
|  | // even though the address escapes, it's still our responsibility to free the | 
|  | // buffer. | 
|  | if (Call->argumentsMayEscape()) | 
|  | return true; | 
|  |  | 
|  | // Otherwise, assume that the function does not free memory. | 
|  | // Most system calls do not free the memory. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool retTrue(const RefState *RS) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool checkIfNewOrNewArrayFamily(const RefState *RS) { | 
|  | return (RS->getAllocationFamily() == AF_CXXNewArray || | 
|  | RS->getAllocationFamily() == AF_CXXNew); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind) const { | 
|  | return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind) const { | 
|  | return checkPointerEscapeAux(State, Escaped, Call, Kind, | 
|  | &checkIfNewOrNewArrayFamily); | 
|  | } | 
|  |  | 
|  | ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, | 
|  | const InvalidatedSymbols &Escaped, | 
|  | const CallEvent *Call, | 
|  | PointerEscapeKind Kind, | 
|  | bool(*CheckRefState)(const RefState*)) const { | 
|  | // If we know that the call does not free memory, or we want to process the | 
|  | // call later, keep tracking the top level arguments. | 
|  | SymbolRef EscapingSymbol = nullptr; | 
|  | if (Kind == PSK_DirectEscapeOnCall && | 
|  | !mayFreeAnyEscapedMemoryOrIsModeledExplicitly(Call, State, | 
|  | EscapingSymbol) && | 
|  | !EscapingSymbol) { | 
|  | return State; | 
|  | } | 
|  |  | 
|  | for (InvalidatedSymbols::const_iterator I = Escaped.begin(), | 
|  | E = Escaped.end(); | 
|  | I != E; ++I) { | 
|  | SymbolRef sym = *I; | 
|  |  | 
|  | if (EscapingSymbol && EscapingSymbol != sym) | 
|  | continue; | 
|  |  | 
|  | if (const RefState *RS = State->get<RegionState>(sym)) { | 
|  | if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && | 
|  | CheckRefState(RS)) { | 
|  | State = State->set<RegionState>(sym, RefState::getEscaped(RS)); | 
|  | } | 
|  | } | 
|  | } | 
|  | return State; | 
|  | } | 
|  |  | 
|  | static SymbolRef findFailedReallocSymbol(ProgramStateRef currState, | 
|  | ProgramStateRef prevState) { | 
|  | ReallocPairsTy currMap = currState->get<ReallocPairs>(); | 
|  | ReallocPairsTy prevMap = prevState->get<ReallocPairs>(); | 
|  |  | 
|  | for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end(); | 
|  | I != E; ++I) { | 
|  | SymbolRef sym = I.getKey(); | 
|  | if (!currMap.lookup(sym)) | 
|  | return sym; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | PathDiagnosticPieceRef | 
|  | MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, | 
|  | BugReporterContext &BRC, | 
|  | PathSensitiveBugReport &BR) { | 
|  |  | 
|  | ProgramStateRef state = N->getState(); | 
|  | ProgramStateRef statePrev = N->getFirstPred()->getState(); | 
|  |  | 
|  | const RefState *RS = state->get<RegionState>(Sym); | 
|  | const RefState *RSPrev = statePrev->get<RegionState>(Sym); | 
|  |  | 
|  | const Stmt *S = PathDiagnosticLocation::getStmt(N); | 
|  | // 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. | 
|  | 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)) { | 
|  | 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"; | 
|  | StackHint = new StackHintGeneratorForSymbol(Sym, | 
|  | "Returning; memory was released"); | 
|  | break; | 
|  | case AF_InnerBuffer: { | 
|  | const MemRegion *ObjRegion = | 
|  | allocation_state::getContainerObjRegion(statePrev, Sym); | 
|  | const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); | 
|  | QualType ObjTy = TypedRegion->getValueType(); | 
|  | OS << "Inner buffer of '" << ObjTy.getAsString() << "' "; | 
|  |  | 
|  | if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { | 
|  | OS << "deallocated by call to destructor"; | 
|  | StackHint = new StackHintGeneratorForSymbol(Sym, | 
|  | "Returning; inner buffer was deallocated"); | 
|  | } else { | 
|  | OS << "reallocated by call to '"; | 
|  | 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(); | 
|  | } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { | 
|  | auto &CEMgr = BRC.getStateManager().getCallEventManager(); | 
|  | CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); | 
|  | const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()); | 
|  | OS << (D ? D->getNameAsString() : "unknown"); | 
|  | } | 
|  | OS << "'"; | 
|  | StackHint = new StackHintGeneratorForSymbol(Sym, | 
|  | "Returning; inner buffer was reallocated"); | 
|  | } | 
|  | Msg = OS.str(); | 
|  | break; | 
|  | } | 
|  | case AF_None: | 
|  | llvm_unreachable("Unhandled allocation family!"); | 
|  | } | 
|  |  | 
|  | // 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, ""); | 
|  | } else if (isReallocFailedCheck(RS, RSPrev, S)) { | 
|  | Mode = ReallocationFailed; | 
|  | Msg = "Reallocation failed"; | 
|  | StackHint = new StackHintGeneratorForReallocationFailed(Sym, | 
|  | "Reallocation failed"); | 
|  |  | 
|  | if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) { | 
|  | // Is it possible to fail two reallocs WITHOUT testing in between? | 
|  | assert((!FailedReallocSymbol || FailedReallocSymbol == sym) && | 
|  | "We only support one failed realloc at a time."); | 
|  | BR.markInteresting(sym); | 
|  | FailedReallocSymbol = sym; | 
|  | } | 
|  | } | 
|  |  | 
|  | // We are in a special mode if a reallocation failed later in the path. | 
|  | } else if (Mode == ReallocationFailed) { | 
|  | assert(FailedReallocSymbol && "No symbol to look for."); | 
|  |  | 
|  | // Is this is the first appearance of the reallocated symbol? | 
|  | if (!statePrev->get<RegionState>(FailedReallocSymbol)) { | 
|  | // We're at the reallocation point. | 
|  | Msg = "Attempt to reallocate memory"; | 
|  | StackHint = new StackHintGeneratorForSymbol(Sym, | 
|  | "Returned reallocated memory"); | 
|  | FailedReallocSymbol = nullptr; | 
|  | Mode = Normal; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (Msg.empty()) { | 
|  | // Silence a memory leak warning by MallocChecker in MallocChecker.cpp :) | 
|  | assert(!StackHint && "Memory leak!"); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | assert(StackHint); | 
|  |  | 
|  | // Generate the extra diagnostic. | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, | 
|  | const char *NL, const char *Sep) const { | 
|  |  | 
|  | RegionStateTy RS = State->get<RegionState>(); | 
|  |  | 
|  | if (!RS.isEmpty()) { | 
|  | Out << Sep << "MallocChecker :" << NL; | 
|  | for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { | 
|  | 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 << " : "; | 
|  | I.getData().dump(Out); | 
|  | if (CheckKind.hasValue()) | 
|  | Out << " (" << CheckNames[*CheckKind].getName() << ")"; | 
|  | Out << NL; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 | 
|  |  | 
|  | // Intended to be used in InnerPointerChecker to register the part of | 
|  | // MallocChecker connected to it. | 
|  | void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { | 
|  | MallocChecker *checker = mgr.getChecker<MallocChecker>(); | 
|  | checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; | 
|  | checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = | 
|  | mgr.getCurrentCheckName(); | 
|  | } | 
|  |  | 
|  | void ento::registerDynamicMemoryModeling(CheckerManager &mgr) { | 
|  | auto *checker = mgr.registerChecker<MallocChecker>(); | 
|  | checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( | 
|  | checker, "Optimistic"); | 
|  | } | 
|  |  | 
|  | bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #define REGISTER_CHECKER(name)                                                 \ | 
|  | void ento::register##name(CheckerManager &mgr) {                             \ | 
|  | MallocChecker *checker = mgr.getChecker<MallocChecker>();                  \ | 
|  | checker->ChecksEnabled[MallocChecker::CK_##name] = true;                   \ | 
|  | checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ | 
|  | }                                                                            \ | 
|  | \ | 
|  | bool ento::shouldRegister##name(const LangOptions &LO) {                     \ | 
|  | return true;                                                               \ | 
|  | } | 
|  |  | 
|  | REGISTER_CHECKER(MallocChecker) | 
|  | REGISTER_CHECKER(NewDeleteChecker) | 
|  | REGISTER_CHECKER(NewDeleteLeaksChecker) | 
|  | REGISTER_CHECKER(MismatchedDeallocatorChecker) |