r349344 - Revert rC349281 '[analyzer][MallocChecker][NFC] Document and reorganize some functions'
Kristof Umann via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 17 04:25:48 PST 2018
Author: szelethus
Date: Mon Dec 17 04:25:48 2018
New Revision: 349344
URL: http://llvm.org/viewvc/llvm-project?rev=349344&view=rev
Log:
Revert rC349281 '[analyzer][MallocChecker][NFC] Document and reorganize some functions'
Accidentally commited earlier with the same commit title, but really it
should've been
"Revert rC349283 '[analyzer][MallocChecker] Improve warning messages on double-delete errors'"
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=349344&r1=349343&r2=349344&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Mon Dec 17 04:25:48 2018
@@ -7,41 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a variety of memory management related checkers, such as
-// leak, double free, and use-after-free.
-//
-// The following checkers are defined here:
-//
-// * MallocChecker
-// Despite its name, it models all sorts of memory allocations and
-// de- or reallocation, including but not limited to malloc, free,
-// relloc, new, delete. It also reports on a variety of memory misuse
-// errors.
-// Many other checkers interact very closely with this checker, in fact,
-// most are merely options to this one. Other checkers may register
-// MallocChecker, but do not enable MallocChecker's reports (more details
-// to follow around its field, ChecksEnabled).
-// It also has a boolean "Optimistic" checker option, which if set to true
-// will cause the checker to model user defined memory management related
-// functions annotated via the attribute ownership_takes, ownership_holds
-// and ownership_returns.
-//
-// * NewDeleteChecker
-// Enables the modeling of new, new[], delete, delete[] in MallocChecker,
-// and checks for related double-free and use-after-free errors.
-//
-// * NewDeleteLeaksChecker
-// Checks for leaks related to new, new[], delete, delete[].
-// Depends on NewDeleteChecker.
-//
-// * MismatchedDeallocatorChecker
-// Enables checking whether memory is deallocated with the correspending
-// allocation function in MallocChecker, such as malloc() allocated
-// regions are only freed by free(), new by delete, new[] by delete[].
-//
-// InnerPointerChecker interacts very closely with MallocChecker, but unlike
-// the above checkers, it has it's own file, hence the many InnerPointerChecker
-// related headers and non-static functions.
+// This file defines malloc/free checker, which checks for potential memory
+// leaks, double free, and use-after-free problems.
//
//===----------------------------------------------------------------------===//
@@ -70,10 +37,6 @@
using namespace clang;
using namespace ento;
-//===----------------------------------------------------------------------===//
-// The types of allocation we're modeling.
-//===----------------------------------------------------------------------===//
-
namespace {
// Used to check correspondence between allocators and deallocators.
@@ -87,59 +50,28 @@ enum AllocationFamily {
AF_InnerBuffer
};
-struct MemFunctionInfoTy;
-
-} // end of anonymous namespace
-
-/// Determine family of a deallocation expression.
-static AllocationFamily getAllocationFamily(
- const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, const Stmt *S);
-
-/// Print names of allocators and deallocators.
-///
-/// \returns true on success.
-static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E);
-
-/// Print expected name of an allocator based on the deallocator's
-/// family derived from the DeallocExpr.
-static void printExpectedAllocName(raw_ostream &os,
- const MemFunctionInfoTy &MemFunctionInfo,
- CheckerContext &C, const Expr *E);
-
-/// Print expected name of a deallocator based on the allocator's
-/// family.
-static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family);
-
-//===----------------------------------------------------------------------===//
-// The state of a symbol, in terms of memory management.
-//===----------------------------------------------------------------------===//
-
-namespace {
-
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
+ 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.
- Kind K : 3;
- AllocationFamily Family : 3;
-
- RefState(Kind k, const Stmt *s, AllocationFamily family)
+ RefState(Kind k, const Stmt *s, unsigned family)
: S(s), K(k), Family(family) {
assert(family != AF_None);
}
@@ -150,7 +82,7 @@ public:
bool isRelinquished() const { return K == Relinquished; }
bool isEscaped() const { return K == Escaped; }
AllocationFamily getAllocationFamily() const {
- return Family;
+ return (AllocationFamily)Family;
}
const Stmt *getStmt() const { return S; }
@@ -158,17 +90,17 @@ public:
return K == X.K && S == X.S && Family == X.Family;
}
- static RefState getAllocated(AllocationFamily family, const Stmt *s) {
+ 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(AllocationFamily family, const Stmt *s) {
+ static RefState getReleased(unsigned family, const Stmt *s) {
return RefState(Released, s, family);
}
- static RefState getRelinquished(AllocationFamily family, const Stmt *s) {
+ static RefState getRelinquished(unsigned family, const Stmt *s) {
return RefState(Relinquished, s, family);
}
static RefState getEscaped(const RefState *RS) {
@@ -181,8 +113,8 @@ public:
ID.AddInteger(Family);
}
- LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
- switch (K) {
+ void dump(raw_ostream &OS) const {
+ switch (static_cast<Kind>(K)) {
#define CASE(ID) case ID: OS << #ID; break;
CASE(Allocated)
CASE(AllocatedOfSizeZero)
@@ -195,61 +127,23 @@ public:
LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
};
-} // end of anonymous namespace
-
-REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
-
-/// Check if the memory associated with this symbol was released.
-static bool isReleased(SymbolRef Sym, CheckerContext &C);
-
-/// 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);
-
-//===----------------------------------------------------------------------===//
-// The modeling of memory reallocation.
-//
-// The terminology 'toPtr' and 'fromPtr' will be used:
-// toPtr = realloc(fromPtr, 20);
-//===----------------------------------------------------------------------===//
-
-REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef)
-
-namespace {
-
-/// The state of 'fromPtr' after reallocation is known to have failed.
-enum OwnershipAfterReallocKind {
- // The symbol needs to be freed (e.g.: realloc)
- OAR_ToBeFreedAfterFailure,
- // The symbol has been freed (e.g.: reallocf)
- OAR_FreeOnFailure,
- // The symbol doesn't have to freed (e.g.: we aren't sure if, how and where
- // 'fromPtr' was allocated:
- // void Haha(int *ptr) {
- // ptr = realloc(ptr, 67);
- // // ...
- // }
- // ).
- OAR_DoNotTrackAfterFailure
+enum ReallocPairKind {
+ RPToBeFreedAfterFailure,
+ // The symbol has been freed when reallocation failed.
+ RPIsFreeOnFailure,
+ // The symbol does not need to be freed after reallocation fails.
+ RPDoNotTrackAfterFailure
};
-/// Stores information about the 'fromPtr' symbol after reallocation.
-///
-/// This is important because realloc may fail, and that needs special modeling.
-/// Whether reallocation failed or not will not be known until later, so we'll
-/// store whether upon failure 'fromPtr' will be freed, or needs to be freed
-/// later, etc.
+/// \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 'fromPtr'.
+ // The symbol which realloc reallocated.
SymbolRef ReallocatedSym;
- OwnershipAfterReallocKind Kind;
+ ReallocPairKind Kind;
- ReallocPair(SymbolRef S, OwnershipAfterReallocKind K) :
+ ReallocPair(SymbolRef S, ReallocPairKind K) :
ReallocatedSym(S), Kind(K) {}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(Kind);
@@ -261,75 +155,7 @@ struct ReallocPair {
}
};
-} // end of anonymous namespace
-
-REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
-
-//===----------------------------------------------------------------------===//
-// Kinds of memory operations, information about resource managing functions.
-//===----------------------------------------------------------------------===//
-
-namespace {
-
-enum class MemoryOperationKind {
- MOK_Allocate,
- MOK_Free,
- MOK_Any
-};
-
-struct MemFunctionInfoTy {
- /// The value of the MallocChecker:Optimistic is stored in this variable.
- ///
- /// In pessimistic mode, the checker assumes that it does not know which
- /// functions might free the memory.
- /// In optimistic mode, the checker assumes that all user-defined functions
- /// which might free a pointer are annotated.
- DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
-
- // TODO: Change these to CallDescription, and get rid of lazy initialization.
- mutable IdentifierInfo *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_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;
-
- void initIdentifierInfo(ASTContext &C) 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;
-
- /// Tells if the callee is one of the builtin new/delete operators, including
- /// placement operators and other standard overloads.
- bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
- ///@}
-};
-
-} // end of anonymous namespace
-
-//===----------------------------------------------------------------------===//
-// Definition of the MallocChecker class.
-//===----------------------------------------------------------------------===//
-
-namespace {
+typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo;
class MallocChecker : public Checker<check::DeadSymbols,
check::PointerEscape,
@@ -344,16 +170,26 @@ class MallocChecker : public Checker<che
check::PostStmt<BlockExpr>,
check::PostObjCMessage,
check::Location,
- eval::Assume> {
+ eval::Assume>
+{
public:
- MemFunctionInfoTy MemFunctionInfo;
+ 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_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) {}
- /// Many checkers are essentially built into this one, so enabling them will
- /// make MallocChecker perform additional modeling and reporting.
+ /// In pessimistic mode, the checker assumes that it does not know which
+ /// functions might free the memory.
enum CheckKind {
- /// When a subchecker is enabled but MallocChecker isn't, model memory
- /// management but do not emit warnings emitted with MallocChecker only
- /// enabled.
CK_MallocChecker,
CK_NewDeleteChecker,
CK_NewDeleteLeaksChecker,
@@ -362,7 +198,13 @@ public:
CK_NumCheckKinds
};
- using LeakInfo = std::pair<const ExplodedNode*, const MemRegion*>;
+ enum class MemoryOperationKind {
+ MOK_Allocate,
+ MOK_Free,
+ MOK_Any
+ };
+
+ DefaultBool IsOptimistic;
DefaultBool ChecksEnabled[CK_NumCheckKinds];
CheckName CheckNames[CK_NumCheckKinds];
@@ -405,74 +247,71 @@ private:
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];
-
- // TODO: Remove mutable by moving the initializtaion to the registry function.
+ 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_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.
- ///
- /// \param [in] E The expression that allocates memory.
- /// \param [in] IndexOfSizeArg Index of the argument that specifies the size
- /// of the memory that needs to be allocated. E.g. for malloc, this would be
- /// 0.
- /// \param [in] RetVal Specifies the newly allocated pointer value;
- /// if unspecified, the value of expression \p E is used.
- static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E,
- const unsigned IndexOfSizeArg,
- ProgramStateRef State,
- Optional<SVal> RetVal = None);
+ /// 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;
- /// Model functions with the ownership_returns attribute.
- ///
- /// User-defined function may have the ownership_returns attribute, which
- /// annotates that the function returns with an object that was allocated on
- /// the heap, and passes the ownertship to the callee.
- ///
- /// void __attribute((ownership_returns(malloc, 1))) *my_malloc(size_t);
- ///
- /// It has two parameters:
- /// - first: name of the resource (e.g. 'malloc')
- /// - (OPTIONAL) second: size of the allocated region
- ///
- /// \param [in] CE The expression that allocates memory.
- /// \param [in] Att The ownership_returns attribute.
- /// \param [in] State The \c ProgramState right before allocation.
- /// \returns The ProgramState right after allocation.
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
const OwnershipAttr* Att,
ProgramStateRef State) const;
-
- /// Models memory allocation.
- ///
- /// \param [in] CE The expression that allocates memory.
- /// \param [in] SizeEx Size of the memory that needs to be allocated.
- /// \param [in] Init The value the allocated memory needs to be initialized.
- /// with. For example, \c calloc initializes the allocated memory to 0,
- /// malloc leaves it undefined.
- /// \param [in] State The \c ProgramState right before allocation.
- /// \returns The ProgramState right after allocation.
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
AllocationFamily Family = AF_Malloc);
-
- /// Models memory allocation.
- ///
- /// \param [in] CE The expression that allocates memory.
- /// \param [in] Size Size of the memory that needs to be allocated.
- /// \param [in] Init The value the allocated memory needs to be initialized.
- /// with. For example, \c calloc initializes the allocated memory to 0,
- /// malloc leaves it undefined.
- /// \param [in] State The \c ProgramState right before allocation.
- /// \returns The ProgramState right after allocation.
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- SVal Size, SVal Init,
+ SVal SizeEx, SVal Init,
ProgramStateRef State,
AllocationFamily Family = AF_Malloc);
@@ -485,124 +324,49 @@ private:
performKernelMalloc(const CallExpr *CE, CheckerContext &C,
const ProgramStateRef &State) const;
- /// Model functions with the ownership_takes and ownership_holds attributes.
- ///
- /// User-defined function may have the ownership_takes and/or ownership_holds
- /// attributes, which annotates that the function frees the memory passed as a
- /// parameter.
- ///
- /// void __attribute((ownership_takes(malloc, 1))) my_free(void *);
- /// void __attribute((ownership_holds(malloc, 1))) my_hold(void *);
- ///
- /// They have two parameters:
- /// - first: name of the resource (e.g. 'malloc')
- /// - second: index of the parameter the attribute applies to
- ///
- /// \param [in] CE The expression that frees memory.
- /// \param [in] Att The ownership_takes or ownership_holds attribute.
- /// \param [in] State The \c ProgramState right before allocation.
- /// \returns The ProgramState right after deallocation.
+ /// 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;
-
- /// Models memory deallocation.
- ///
- /// \param [in] CE The expression that frees memory.
- /// \param [in] State The \c ProgramState right before allocation.
- /// \param [in] Num Index of the argument that needs to be freed. This is
- /// normally 0, but for custom free functions it may be different.
- /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds
- /// attribute.
- /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known
- /// to have been allocated, or in other words, the symbol to be freed was
- /// registered as allocated by this checker. In the following case, \c ptr
- /// isn't known to be allocated.
- /// void Haha(int *ptr) {
- /// ptr = realloc(ptr, 67);
- /// // ...
- /// }
- /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
- /// we're modeling returns with Null on failure.
- /// \returns The ProgramState right after deallocation.
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State, unsigned Num,
+ ProgramStateRef state, unsigned Num,
bool Hold,
- bool &IsKnownToBeAllocated,
+ bool &ReleasedAllocated,
bool ReturnsNullOnFailure = false) const;
-
- /// Models memory deallocation.
- ///
- /// \param [in] ArgExpr The variable who's pointee needs to be freed.
- /// \param [in] ParentExpr The expression that frees the memory.
- /// \param [in] State The \c ProgramState right before allocation.
- /// normally 0, but for custom free functions it may be different.
- /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds
- /// attribute.
- /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known
- /// to have been allocated, or in other words, the symbol to be freed was
- /// registered as allocated by this checker. In the following case, \c ptr
- /// isn't known to be allocated.
- /// void Haha(int *ptr) {
- /// ptr = realloc(ptr, 67);
- /// // ...
- /// }
- /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
- /// we're modeling returns with Null on failure.
- /// \returns The ProgramState right after deallocation.
- ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
+ ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
const Expr *ParentExpr,
ProgramStateRef State,
bool Hold,
- bool &IsKnownToBeAllocated,
+ bool &ReleasedAllocated,
bool ReturnsNullOnFailure = false) const;
- // TODO: Needs some refactoring, as all other deallocation modeling
- // functions are suffering from out parameters and messy code due to how
- // realloc is handled.
- //
- /// Models memory reallocation.
- ///
- /// \param [in] CE The expression that reallocated memory
- /// \param [in] FreesMemOnFailure Whether if reallocation fails, the supplied
- /// memory should be freed.
- /// \param [in] State The \c ProgramState right before reallocation.
- /// \param [in] SuffixWithN Whether the reallocation function we're modeling
- /// has an '_n' suffix, such as g_realloc_n.
- /// \returns The ProgramState right after reallocation.
ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
- bool ShouldFreeOnFail,
+ bool FreesMemOnFailure,
ProgramStateRef State,
bool SuffixWithN = false) const;
-
- /// Evaluates the buffer size that needs to be allocated.
- ///
- /// \param [in] Blocks The amount of blocks that needs to be allocated.
- /// \param [in] BlockBytes The size of a block.
- /// \returns The symbolic value of \p Blocks * \p BlockBytes.
static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
const Expr *BlockBytes);
-
- /// Models zero initialized array allocation.
- ///
- /// \param [in] CE The expression that reallocated memory
- /// \param [in] State The \c ProgramState right before reallocation.
- /// \returns The ProgramState right after allocation.
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
ProgramStateRef State);
- /// If in \p S \p Sym is used, check whether \p Sym was already freed.
+ /// Check if the memory associated with this symbol was released.
+ bool isReleased(SymbolRef Sym, CheckerContext &C) const;
+
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
- /// If in \p S \p Sym is used, check whether \p Sym was allocated as a zero
- /// sized memory region.
void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
const Stmt *S) const;
- /// If in \p S \p Sym is being freed, check whether \p Sym was already freed.
bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
- /// Check if the function is known to free memory, or if it is
+ /// 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,
@@ -616,12 +380,12 @@ private:
ProgramStateRef State,
SymbolRef &EscapingSymbol) const;
- /// Implementation of the checkPointerEscape callbacks.
+ // Implementation of the checkPointerEscape callbacks.
ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool IsConstPointerEscape) const;
+ 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;
@@ -640,7 +404,6 @@ private:
///@}
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,
@@ -666,149 +429,143 @@ private:
/// Find the location of the allocation for Sym on the path leading to the
/// exploded node N.
- static LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &C);
-
+ LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
+ CheckerContext &C) const;
void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
-};
-
-} // end anonymous namespace
-
-//===----------------------------------------------------------------------===//
-// Definition of MallocBugVisitor.
-//===----------------------------------------------------------------------===//
-
-/// 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;
+ /// 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;
- // A C++ destructor stack frame in which memory was released. Used for
- // miscellaneous false positive suppression.
- const StackFrameContext *ReleaseDestructorLC;
+ bool IsLeak;
- 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);
- }
-
- /// Did not track -> allocated. Other state (released) -> allocated.
- static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev,
- const Stmt *Stmt) {
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
- (RSCurr && (RSCurr->isAllocated() ||
- RSCurr->isAllocatedOfSizeZero())) &&
- (!RSPrev || !(RSPrev->isAllocated() ||
- RSPrev->isAllocatedOfSizeZero())));
- }
-
- /// Did not track -> released. Other state (allocated) -> released.
- /// The statement associated with the release might be missing.
- static inline bool isReleased(const RefState *RSCurr, const RefState *RSPrev,
- const Stmt *Stmt) {
- bool IsReleased = (RSCurr && RSCurr->isReleased()) &&
- (!RSPrev || !RSPrev->isReleased());
- assert(!IsReleased ||
- (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) ||
- (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer));
- return IsReleased;
- }
-
- /// Did not track -> relinquished. Other state (allocated) -> relinquished.
- static inline bool isRelinquished(const RefState *RSCurr,
- const RefState *RSPrev,
- const Stmt *Stmt) {
- return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
- isa<ObjCPropertyRefExpr>(Stmt)) &&
- (RSCurr && RSCurr->isRelinquished()) &&
- (!RSPrev || !RSPrev->isRelinquished()));
- }
-
- /// 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.
- static inline bool hasReallocFailed(const RefState *RSCurr,
- const RefState *RSPrev,
- const Stmt *Stmt) {
- return ((!Stmt || !isa<CallExpr>(Stmt)) &&
- (RSCurr && (RSCurr->isAllocated() ||
- RSCurr->isAllocatedOfSizeZero())) &&
- (RSPrev && !(RSPrev->isAllocated() ||
- RSPrev->isAllocatedOfSizeZero())));
- }
-
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
-
- std::shared_ptr<PathDiagnosticPiece>
- getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
- BugReport &BR) override {
- if (!IsLeak)
- return nullptr;
-
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode,
- BRC.getSourceManager());
- // 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) {}
+ 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())));
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ std::shared_ptr<PathDiagnosticPiece>
+ getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
+ BugReport &BR) override {
+ if (!IsLeak)
+ return nullptr;
- std::string getMessageForArg(const Expr *ArgE,
- unsigned ArgIndex) override {
- // Printed parameters start at 1, not 0.
- ++ArgIndex;
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createEndOfPath(EndPathNode,
+ BRC.getSourceManager());
+ // 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);
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
- os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
- << " parameter failed";
+ os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
+ << " parameter failed";
- return os.str();
- }
+ return os.str();
+ }
- std::string getMessageForReturn(const CallExpr *CallExpr) override {
- return "Reallocation of returned value failed";
- }
+ 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.
@@ -828,11 +585,7 @@ public:
};
} // end anonymous namespace
-//===----------------------------------------------------------------------===//
-// Methods of MemFunctionInfoTy.
-//===----------------------------------------------------------------------===//
-
-void MemFunctionInfoTy::initIdentifierInfo(ASTContext &Ctx) const {
+void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (II_malloc)
return;
II_alloca = &Ctx.Idents.get("alloca");
@@ -871,8 +624,7 @@ void MemFunctionInfoTy::initIdentifierIn
II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n");
}
-bool MemFunctionInfoTy::isMemFunction(const FunctionDecl *FD,
- ASTContext &C) const {
+bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any))
return true;
@@ -888,10 +640,10 @@ bool MemFunctionInfoTy::isMemFunction(co
return false;
}
-bool MemFunctionInfoTy::isCMemFunction(const FunctionDecl *FD,
- ASTContext &C,
- AllocationFamily Family,
- MemoryOperationKind MemKind) const {
+bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
+ ASTContext &C,
+ AllocationFamily Family,
+ MemoryOperationKind MemKind) const {
if (!FD)
return false;
@@ -944,7 +696,7 @@ bool MemFunctionInfoTy::isCMemFunction(c
if (Family != AF_Malloc)
return false;
- if (ShouldIncludeOwnershipAnnotatedFunctions && FD->hasAttrs()) {
+ if (IsOptimistic && FD->hasAttrs()) {
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) {
@@ -959,7 +711,10 @@ bool MemFunctionInfoTy::isCMemFunction(c
return false;
}
-bool MemFunctionInfoTy::isStandardNewDelete(const FunctionDecl *FD,
+
+// 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;
@@ -976,10 +731,6 @@ bool MemFunctionInfoTy::isStandardNewDel
return !L.isValid() || C.getSourceManager().isInSystemHeader(L);
}
-//===----------------------------------------------------------------------===//
-// Methods of MallocChecker and MallocBugVisitor.
-//===----------------------------------------------------------------------===//
-
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:
@@ -1078,35 +829,28 @@ void MallocChecker::checkPostStmt(const
return;
ProgramStateRef State = C.getState();
- bool IsKnownToBeAllocatedMemory = false;
+ bool ReleasedAllocatedMemory = false;
if (FD->getKind() == Decl::Function) {
- MemFunctionInfo.initIdentifierInfo(C.getASTContext());
+ initIdentifierInfo(C.getASTContext());
IdentifierInfo *FunI = FD->getIdentifier();
- if (FunI == MemFunctionInfo.II_malloc ||
- FunI == MemFunctionInfo.II_g_malloc ||
- FunI == MemFunctionInfo.II_g_try_malloc) {
- switch(CE->getNumArgs()) {
- default:
+ if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) {
+ if (CE->getNumArgs() < 1)
return;
- case 1:
+ if (CE->getNumArgs() < 3) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case 2:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- break;
- case 3:
+ 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);
- break;
}
- } else if (FunI == MemFunctionInfo.II_kmalloc) {
+ } else if (FunI == II_kmalloc) {
if (CE->getNumArgs() < 1)
return;
llvm::Optional<ProgramStateRef> MaybeState =
@@ -1115,112 +859,97 @@ void MallocChecker::checkPostStmt(const
State = MaybeState.getValue();
else
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == MemFunctionInfo.II_valloc) {
+ } else if (FunI == II_valloc) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (FunI == MemFunctionInfo.II_realloc ||
- FunI == MemFunctionInfo.II_g_realloc ||
- FunI == MemFunctionInfo.II_g_try_realloc) {
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/false, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_reallocf) {
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/true, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_calloc) {
+ 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 = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_free ||
- FunI == MemFunctionInfo.II_g_free) {
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
- } else if (FunI == MemFunctionInfo.II_strdup ||
- FunI == MemFunctionInfo.II_win_strdup ||
- FunI == MemFunctionInfo.II_wcsdup ||
- FunI == MemFunctionInfo.II_win_wcsdup) {
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_free || FunI == II_g_free) {
+ 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 == MemFunctionInfo.II_strndup) {
+ } else if (FunI == II_strndup) {
State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == MemFunctionInfo.II_alloca ||
- FunI == MemFunctionInfo.II_win_alloca) {
+ } 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 = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (MemFunctionInfo.isStandardNewDelete(FD, C.getASTContext())) {
+ 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.
- switch(FD->getOverloadedOperator()) {
- case OO_New:
+ OverloadedOperatorKind K = FD->getOverloadedOperator();
+ if (K == OO_New) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNew);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case OO_Array_New:
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
+ else if (K == OO_Array_New) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNewArray);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case OO_Delete:
- case OO_Array_Delete:
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
- break;
- default:
- llvm_unreachable("not a new/delete operator");
+ State = ProcessZeroAllocation(C, CE, 0, State);
}
- } else if (FunI == MemFunctionInfo.II_if_nameindex) {
+ 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 == MemFunctionInfo.II_if_freenameindex) {
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
- } else if (FunI == MemFunctionInfo.II_g_malloc0 ||
- FunI == MemFunctionInfo.II_g_try_malloc0) {
+ } 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 = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (FunI == MemFunctionInfo.II_g_memdup) {
+ 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 = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_g_malloc_n ||
- FunI == MemFunctionInfo.II_g_try_malloc_n ||
- FunI == MemFunctionInfo.II_g_malloc0_n ||
- FunI == MemFunctionInfo.II_g_try_malloc0_n) {
+ 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 == MemFunctionInfo.II_g_malloc0_n ||
- FunI == MemFunctionInfo.II_g_try_malloc0_n) {
+ 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 = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_g_realloc_n ||
- FunI == MemFunctionInfo.II_g_try_realloc_n) {
+ 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, /*ShouldFreeOnFail*/false, State,
- /*SuffixWithN*/true);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- State = ProcessZeroAllocCheck(C, CE, 2, State);
+ State = ReallocMemAux(C, CE, false, State, true);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ State = ProcessZeroAllocation(C, CE, 2, State);
}
}
- if (MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions ||
- ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
+ if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
if (FD->hasAttrs())
@@ -1240,9 +969,9 @@ void MallocChecker::checkPostStmt(const
}
// Performs a 0-sized allocations check.
-ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
- CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg,
- ProgramStateRef State, Optional<SVal> RetVal) {
+ProgramStateRef MallocChecker::ProcessZeroAllocation(
+ CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg,
+ ProgramStateRef State, Optional<SVal> RetVal) const {
if (!State)
return nullptr;
@@ -1252,7 +981,7 @@ ProgramStateRef MallocChecker::ProcessZe
const Expr *Arg = nullptr;
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
- Arg = CE->getArg(IndexOfSizeArg);
+ Arg = CE->getArg(AllocationSizeArg);
}
else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
if (NE->isArray())
@@ -1314,9 +1043,7 @@ static QualType getDeepPointeeType(QualT
return Result;
}
-/// \returns true if the constructor invoked by \p NE has an argument of a
-/// pointer/reference to a record type.
-static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) {
+static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
const CXXConstructExpr *ConstructE = NE->getConstructExpr();
if (!ConstructE)
@@ -1346,17 +1073,11 @@ static bool hasNonTrivialConstructorCall
void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
CheckerContext &C,
SVal Target) const {
- if (!MemFunctionInfo.isStandardNewDelete(NE->getOperatorNew(),
- C.getASTContext()))
+ if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
return;
ParentMap &PM = C.getLocationContext()->getParentMap();
-
- // Non-trivial constructors have a chance to escape 'this', but marking all
- // invocations of trivial constructors as escaped would cause too great of
- // reduction of true positives, so let's just do that for constructors that
- // have an argument of a pointer-to-record type.
- if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE))
+ if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE))
return;
ProgramStateRef State = C.getState();
@@ -1367,7 +1088,7 @@ void MallocChecker::processNewAllocation
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
: AF_CXXNew, Target);
State = addExtentSize(C, NE, State, Target);
- State = ProcessZeroAllocCheck(C, NE, 0, State, Target);
+ State = ProcessZeroAllocation(C, NE, 0, State, Target);
C.addTransition(State);
}
@@ -1436,14 +1157,13 @@ void MallocChecker::checkPreStmt(const C
if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
checkUseAfterFree(Sym, C, DE->getArgument());
- if (!MemFunctionInfo.isStandardNewDelete(DE->getOperatorDelete(),
- C.getASTContext()))
+ if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
return;
ProgramStateRef State = C.getState();
- bool IsKnownToBeAllocated;
+ bool ReleasedAllocated;
State = FreeMemAux(C, DE->getArgument(), DE, State,
- /*Hold*/false, IsKnownToBeAllocated);
+ /*Hold*/false, ReleasedAllocated);
C.addTransition(State);
}
@@ -1483,10 +1203,10 @@ void MallocChecker::checkPostObjCMessage
if (!*FreeWhenDone)
return;
- bool IsKnownToBeAllocatedMemory;
+ bool ReleasedAllocatedMemory;
ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0),
Call.getOriginExpr(), C.getState(),
- /*Hold=*/true, IsKnownToBeAllocatedMemory,
+ /*Hold=*/true, ReleasedAllocatedMemory,
/*RetNullOnFailure=*/true);
C.addTransition(State);
@@ -1499,7 +1219,7 @@ MallocChecker::MallocMemReturnsAttr(Chec
if (!State)
return nullptr;
- if (Att->getModule() != MemFunctionInfo.II_malloc)
+ if (Att->getModule() != II_malloc)
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
@@ -1565,9 +1285,11 @@ ProgramStateRef MallocChecker::MallocMem
return MallocUpdateRefState(C, CE, State, Family);
}
-static ProgramStateRef
-MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
- AllocationFamily Family, Optional<SVal> RetVal) {
+ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
+ const Expr *E,
+ ProgramStateRef State,
+ AllocationFamily Family,
+ Optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -1595,15 +1317,15 @@ ProgramStateRef MallocChecker::FreeMemAt
if (!State)
return nullptr;
- if (Att->getModule() != MemFunctionInfo.II_malloc)
+ if (Att->getModule() != II_malloc)
return nullptr;
- bool IsKnownToBeAllocated = false;
+ bool ReleasedAllocated = false;
for (const auto &Arg : Att->args()) {
ProgramStateRef StateI = FreeMemAux(
C, CE, State, Arg.getASTIndex(),
- Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated);
+ Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);
if (StateI)
State = StateI;
}
@@ -1615,7 +1337,7 @@ ProgramStateRef MallocChecker::FreeMemAu
ProgramStateRef State,
unsigned Num,
bool Hold,
- bool &IsKnownToBeAllocated,
+ bool &ReleasedAllocated,
bool ReturnsNullOnFailure) const {
if (!State)
return nullptr;
@@ -1624,7 +1346,7 @@ ProgramStateRef MallocChecker::FreeMemAu
return nullptr;
return FreeMemAux(C, CE->getArg(Num), CE, State, Hold,
- IsKnownToBeAllocated, ReturnsNullOnFailure);
+ ReleasedAllocated, ReturnsNullOnFailure);
}
/// Checks if the previous call to free on the given symbol failed - if free
@@ -1642,9 +1364,8 @@ static bool didPreviousFreeFail(ProgramS
return false;
}
-static AllocationFamily getAllocationFamily(
- const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, const Stmt *S) {
-
+AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
+ const Stmt *S) const {
if (!S)
return AF_None;
@@ -1656,10 +1377,10 @@ static AllocationFamily getAllocationFam
ASTContext &Ctx = C.getASTContext();
- if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any))
+ if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any))
return AF_Malloc;
- if (MemFunctionInfo.isStandardNewDelete(FD, Ctx)) {
+ if (isStandardNewDelete(FD, Ctx)) {
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
if (Kind == OO_New || Kind == OO_Delete)
return AF_CXXNew;
@@ -1667,12 +1388,10 @@ static AllocationFamily getAllocationFam
return AF_CXXNewArray;
}
- if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex,
- MemoryOperationKind::MOK_Any))
+ if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
return AF_IfNameIndex;
- if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Alloca,
- MemoryOperationKind::MOK_Any))
+ if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any))
return AF_Alloca;
return AF_None;
@@ -1690,8 +1409,8 @@ static AllocationFamily getAllocationFam
return AF_None;
}
-static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) {
+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();
@@ -1730,10 +1449,9 @@ static bool printAllocDeallocName(raw_os
return false;
}
-static void printExpectedAllocName(raw_ostream &os,
- const MemFunctionInfoTy &MemFunctionInfo,
- CheckerContext &C, const Expr *E) {
- AllocationFamily Family = getAllocationFamily(MemFunctionInfo, C, E);
+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;
@@ -1746,7 +1464,8 @@ static void printExpectedAllocName(raw_o
}
}
-static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
+void MallocChecker::printExpectedDeallocName(raw_ostream &os,
+ AllocationFamily Family) const {
switch(Family) {
case AF_Malloc: os << "free()"; return;
case AF_CXXNew: os << "'delete'"; return;
@@ -1763,7 +1482,7 @@ ProgramStateRef MallocChecker::FreeMemAu
const Expr *ParentExpr,
ProgramStateRef State,
bool Hold,
- bool &IsKnownToBeAllocated,
+ bool &ReleasedAllocated,
bool ReturnsNullOnFailure) const {
if (!State)
@@ -1837,9 +1556,6 @@ ProgramStateRef MallocChecker::FreeMemAu
const RefState *RsBase = State->get<RegionState>(SymBase);
SymbolRef PreviousRetStatusSymbol = nullptr;
- IsKnownToBeAllocated = RsBase && (RsBase->isAllocated() ||
- RsBase->isAllocatedOfSizeZero());
-
if (RsBase) {
// Memory returned by alloca() shouldn't be freed.
@@ -1862,8 +1578,7 @@ ProgramStateRef MallocChecker::FreeMemAu
// Check if an expected deallocation function matches the real one.
bool DeallocMatchesAlloc =
- RsBase->getAllocationFamily() ==
- getAllocationFamily(MemFunctionInfo, C, ParentExpr);
+ RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
if (!DeallocMatchesAlloc) {
ReportMismatchedDealloc(C, ArgExpr->getSourceRange(),
ParentExpr, RsBase, SymBase, Hold);
@@ -1889,6 +1604,9 @@ ProgramStateRef MallocChecker::FreeMemAu
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);
@@ -1904,7 +1622,7 @@ ProgramStateRef MallocChecker::FreeMemAu
}
AllocationFamily Family = RsBase ? RsBase->getAllocationFamily()
- : getAllocationFamily(MemFunctionInfo, C, ParentExpr);
+ : getAllocationFamily(C, ParentExpr);
// Normal free.
if (Hold)
return State->set<RegionState>(SymBase,
@@ -1954,8 +1672,8 @@ Optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C,
const Stmt *AllocDeallocStmt,
bool IsALeakCheck) const {
- return getCheckIfTracked(
- getAllocationFamily(MemFunctionInfo, C, AllocDeallocStmt), IsALeakCheck);
+ return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt),
+ IsALeakCheck);
}
Optional<MallocChecker::CheckKind>
@@ -2093,7 +1811,7 @@ void MallocChecker::ReportBadFree(Checke
else
os << "not memory allocated by ";
- printExpectedAllocName(os, MemFunctionInfo, C, DeallocExpr);
+ printExpectedAllocName(os, C, DeallocExpr);
auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], os.str(), N);
R->markInteresting(MR);
@@ -2400,7 +2118,7 @@ void MallocChecker::ReportFunctionPointe
ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
const CallExpr *CE,
- bool ShouldFreeOnFail,
+ bool FreesOnFail,
ProgramStateRef State,
bool SuffixWithN) const {
if (!State)
@@ -2465,32 +2183,33 @@ ProgramStateRef MallocChecker::ReallocMe
if (!FromPtr || !ToPtr)
return nullptr;
- bool IsKnownToBeAllocated = false;
+ bool ReleasedAllocated = false;
// If the size is 0, free the memory.
if (SizeIsZero)
- // 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.
if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0,
- false, IsKnownToBeAllocated))
+ 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, IsKnownToBeAllocated)) {
+ FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize,
UnknownVal(), stateFree);
if (!stateRealloc)
return nullptr;
- OwnershipAfterReallocKind Kind = OAR_ToBeFreedAfterFailure;
- if (ShouldFreeOnFail)
- Kind = OAR_FreeOnFailure;
- else if (!IsKnownToBeAllocated)
- Kind = OAR_DoNotTrackAfterFailure;
+ 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.
@@ -2518,9 +2237,9 @@ ProgramStateRef MallocChecker::CallocMem
return MallocMemAux(C, CE, TotalSize, zeroVal, State);
}
-MallocChecker::LeakInfo
+LeakInfo
MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &C) {
+ CheckerContext &C) const {
const LocationContext *LeakContext = N->getLocationContext();
// Walk the ExplodedGraph backwards and find the first node that referred to
// the tracked symbol.
@@ -2701,10 +2420,9 @@ void MallocChecker::checkPreCall(const C
ASTContext &Ctx = C.getASTContext();
if (ChecksEnabled[CK_MallocChecker] &&
- (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc,
- MemoryOperationKind::MOK_Free) ||
- MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex,
- MemoryOperationKind::MOK_Free)))
+ (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) ||
+ isCMemFunction(FD, Ctx, AF_IfNameIndex,
+ MemoryOperationKind::MOK_Free)))
return;
}
@@ -2807,7 +2525,7 @@ void MallocChecker::checkPostStmt(const
C.addTransition(state);
}
-static bool isReleased(SymbolRef Sym, CheckerContext &C) {
+bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
assert(Sym);
const RefState *RS = C.getState()->get<RegionState>(Sym);
return (RS && RS->isReleased());
@@ -2883,17 +2601,13 @@ ProgramStateRef MallocChecker::evalAssum
SymbolRef ReallocSym = I.getData().ReallocatedSym;
if (const RefState *RS = state->get<RegionState>(ReallocSym)) {
if (RS->isReleased()) {
- switch(I.getData().Kind) {
- case OAR_ToBeFreedAfterFailure:
+ if (I.getData().Kind == RPToBeFreedAfterFailure)
state = state->set<RegionState>(ReallocSym,
RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
- break;
- case OAR_DoNotTrackAfterFailure:
+ else if (I.getData().Kind == RPDoNotTrackAfterFailure)
state = state->remove<RegionState>(ReallocSym);
- break;
- default:
- assert(I.getData().Kind == OAR_FreeOnFailure);
- }
+ else
+ assert(I.getData().Kind == RPIsFreeOnFailure);
}
}
state = state->remove<ReallocPairs>(I.getKey());
@@ -2976,7 +2690,7 @@ bool MallocChecker::mayFreeAnyEscapedMem
// If it's one of the allocation functions we can reason about, we model
// its behavior explicitly.
- if (MemFunctionInfo.isMemFunction(FD, ASTC))
+ if (isMemFunction(FD, ASTC))
return false;
// If it's not a system call, assume it frees memory.
@@ -3068,6 +2782,10 @@ bool MallocChecker::mayFreeAnyEscapedMem
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);
@@ -3077,25 +2795,22 @@ ProgramStateRef MallocChecker::checkPoin
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const {
- return checkPointerEscapeAux(State, Escaped, Call, Kind,
- /*IsConstPointerEscape*/ false);
+ return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue);
}
ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const {
- // If a const pointer escapes, it may not be freed(), but it could be deleted.
return checkPointerEscapeAux(State, Escaped, Call, Kind,
- /*IsConstPointerEscape*/ true);
+ &checkIfNewOrNewArrayFamily);
}
-ProgramStateRef MallocChecker::checkPointerEscapeAux(
- ProgramStateRef State,
+ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind,
- bool IsConstPointerEscape) const {
+ 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;
@@ -3115,11 +2830,10 @@ ProgramStateRef MallocChecker::checkPoin
continue;
if (const RefState *RS = State->get<RegionState>(sym)) {
- if ((RS->isAllocated() || RS->isAllocatedOfSizeZero())) {
- if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS)) {
- State = State->remove<RegionState>(sym);
- State = State->set<RegionState>(sym, RefState::getEscaped(RS));
- }
+ if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
+ CheckRefState(RS)) {
+ State = State->remove<RegionState>(sym);
+ State = State->set<RegionState>(sym, RefState::getEscaped(RS));
}
}
}
@@ -3131,8 +2845,9 @@ static SymbolRef findFailedReallocSymbol
ReallocPairsTy currMap = currState->get<ReallocPairs>();
ReallocPairsTy prevMap = prevState->get<ReallocPairs>();
- for (const ReallocPairsTy::value_type &Pair : prevMap) {
- SymbolRef sym = Pair.first;
+ for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end();
+ I != E; ++I) {
+ SymbolRef sym = I.getKey();
if (!currMap.lookup(sym))
return sym;
}
@@ -3153,19 +2868,19 @@ static bool isReferenceCountingPointerDe
return false;
}
-std::shared_ptr<PathDiagnosticPiece> MallocBugVisitor::VisitNode(
+std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
ProgramStateRef state = N->getState();
ProgramStateRef statePrev = N->getFirstPred()->getState();
- const RefState *RSCurr = state->get<RegionState>(Sym);
+ 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 && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer))
+ if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))
return nullptr;
const LocationContext *CurrentLC = N->getLocationContext();
@@ -3200,12 +2915,12 @@ std::shared_ptr<PathDiagnosticPiece> Mal
llvm::raw_svector_ostream OS(Buf);
if (Mode == Normal) {
- if (isAllocated(RSCurr, RSPrev, S)) {
+ if (isAllocated(RS, RSPrev, S)) {
Msg = "Memory is allocated";
StackHint = new StackHintGeneratorForSymbol(Sym,
"Returned allocated memory");
- } else if (isReleased(RSCurr, RSPrev, S)) {
- const auto Family = RSCurr->getAllocationFamily();
+ } else if (isReleased(RS, RSPrev, S)) {
+ const auto Family = RS->getAllocationFamily();
switch (Family) {
case AF_Alloca:
case AF_Malloc:
@@ -3229,7 +2944,7 @@ std::shared_ptr<PathDiagnosticPiece> Mal
"Returning; inner buffer was deallocated");
} else {
OS << "reallocated by call to '";
- const Stmt *S = RSCurr->getStmt();
+ 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)) {
@@ -3280,10 +2995,10 @@ std::shared_ptr<PathDiagnosticPiece> Mal
}
}
}
- } else if (isRelinquished(RSCurr, RSPrev, S)) {
+ } else if (isRelinquished(RS, RSPrev, S)) {
Msg = "Memory ownership is transferred";
StackHint = new StackHintGeneratorForSymbol(Sym, "");
- } else if (hasReallocFailed(RSCurr, RSPrev, S)) {
+ } else if (isReallocFailedCheck(RS, RSPrev, S)) {
Mode = ReallocationFailed;
Msg = "Reallocation failed";
StackHint = new StackHintGeneratorForReallocationFailed(Sym,
@@ -3320,7 +3035,7 @@ std::shared_ptr<PathDiagnosticPiece> Mal
// Generate the extra diagnostic.
PathDiagnosticLocation Pos;
if (!S) {
- assert(RSCurr->getAllocationFamily() == AF_InnerBuffer);
+ assert(RS->getAllocationFamily() == AF_InnerBuffer);
auto PostImplCall = N->getLocation().getAs<PostImplicitCall>();
if (!PostImplCall)
return nullptr;
@@ -3375,11 +3090,8 @@ markReleased(ProgramStateRef State, Symb
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
registerCStringCheckerBasic(mgr);
MallocChecker *checker = mgr.registerChecker<MallocChecker>();
-
- checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(
- "Optimistic", false, checker);
-
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ "Optimistic", false, checker);
checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
mgr.getCurrentCheckName();
@@ -3397,25 +3109,21 @@ void ento::registerNewDeleteLeaksChecker
// Intended to be used in InnerPointerChecker to register the part of
// MallocChecker connected to it.
void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
- registerCStringCheckerBasic(mgr);
- MallocChecker *checker = mgr.registerChecker<MallocChecker>();
-
- checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(
- "Optimistic", false, checker);
-
- checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
- checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
- mgr.getCurrentCheckName();
+ registerCStringCheckerBasic(mgr);
+ MallocChecker *checker = mgr.registerChecker<MallocChecker>();
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ "Optimistic", false, checker);
+ checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
+ checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
+ mgr.getCurrentCheckName();
}
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
registerCStringCheckerBasic(mgr); \
MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
- checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions = \
- mgr.getAnalyzerOptions().getCheckerBooleanOption( \
- "Optimistic", false, checker);\
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \
+ "Optimistic", false, checker); \
checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
}
More information about the cfe-commits
mailing list