[clang] 703a1b8 - [analyzer][MallocChecker][NFC] Split checkPostCall up, deploy CallDescriptionMap
Kirstóf Umann via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 30 07:02:11 PDT 2020
Author: Louis Dionne
Date: 2020-03-30T16:01:58+02:00
New Revision: 703a1b8caf09a5262a45c2179b8131922f71cf25
URL: https://github.com/llvm/llvm-project/commit/703a1b8caf09a5262a45c2179b8131922f71cf25
DIFF: https://github.com/llvm/llvm-project/commit/703a1b8caf09a5262a45c2179b8131922f71cf25.diff
LOG: [analyzer][MallocChecker][NFC] Split checkPostCall up, deploy CallDescriptionMap
Since its important to know whether a function frees memory (even if its a
reallocating function!), I used two CallDescriptionMaps to merge all
CallDescriptions into it. MemFunctionInfoTy no longer makes sense, it may never
have, but for now, it would be more of a distraction then anything else.
Differential Revision: https://reviews.llvm.org/D68165
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 32a500ffd102..52786bcaf072 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -62,16 +62,19 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include <climits>
+#include <functional>
#include <utility>
using namespace clang;
using namespace ento;
+using namespace std::placeholders;
//===----------------------------------------------------------------------===//
// The types of allocation we're modeling. This is used to check whether a
@@ -93,8 +96,6 @@ enum AllocationFamily {
AF_InnerBuffer
};
-struct MemFunctionInfoTy;
-
} // end of anonymous namespace
/// Print names of allocators and deallocators.
@@ -263,47 +264,6 @@ struct ReallocPair {
REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
-//===----------------------------------------------------------------------===//
-// Kinds of memory operations, information about resource managing functions.
-//===----------------------------------------------------------------------===//
-
-namespace {
-
-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;
-
- CallDescription CD_alloca{{"alloca"}, 1}, CD_win_alloca{{"_alloca"}, 1},
- CD_malloc{{"malloc"}, 1}, CD_BSD_malloc{{"malloc"}, 3},
- CD_free{{"free"}, 1}, CD_realloc{{"realloc"}, 2},
- CD_calloc{{"calloc"}, 2}, CD_valloc{{"valloc"}, 1},
- CD_reallocf{{"reallocf"}, 2}, CD_strndup{{"strndup"}, 2},
- CD_strdup{{"strdup"}, 1}, CD_win_strdup{{"_strdup"}, 1},
- CD_kmalloc{{"kmalloc"}, 2}, CD_if_nameindex{{"if_nameindex"}, 1},
- CD_if_freenameindex{{"if_freenameindex"}, 1}, CD_wcsdup{{"wcsdup"}, 1},
- CD_win_wcsdup{{"_wcsdup"}, 1}, CD_kfree{{"kfree"}, 1},
- CD_g_malloc{{"g_malloc"}, 1}, CD_g_malloc0{{"g_malloc0"}, 1},
- CD_g_realloc{{"g_realloc"}, 2}, CD_g_try_malloc{{"g_try_malloc"}, 1},
- CD_g_try_malloc0{{"g_try_malloc0"}, 1},
- CD_g_try_realloc{{"g_try_realloc"}, 2}, CD_g_free{{"g_free"}, 1},
- CD_g_memdup{{"g_memdup"}, 2}, CD_g_malloc_n{{"g_malloc_n"}, 2},
- CD_g_malloc0_n{{"g_malloc0_n"}, 2}, CD_g_realloc_n{{"g_realloc_n"}, 3},
- CD_g_try_malloc_n{{"g_try_malloc_n"}, 2},
- CD_g_try_malloc0_n{{"g_try_malloc0_n"}, 2},
- CD_g_try_realloc_n{{"g_try_realloc_n"}, 3};
-
- bool isMemFunction(const CallEvent &Call) const;
- bool isCMemFunction(const CallEvent &Call) const;
- bool isCMemFreeFunction(const CallEvent &Call) const;
- bool isCMemAllocFunction(const CallEvent &Call) const;
-};
-} // end of anonymous namespace
-
/// Tells if the callee is one of the builtin new/delete operators, including
/// placement operators and other standard overloads.
static bool isStandardNewDelete(const FunctionDecl *FD);
@@ -327,7 +287,11 @@ class MallocChecker
check::PreStmt<CXXDeleteExpr>, check::PostStmt<BlockExpr>,
check::PostObjCMessage, check::Location, eval::Assume> {
public:
- MemFunctionInfoTy MemFunctionInfo;
+ /// 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;
/// Many checkers are essentially built into this one, so enabling them will
/// make MallocChecker perform additional modeling and reporting.
@@ -387,6 +351,81 @@ class MallocChecker
mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
+#define CHECK_FN(NAME) \
+ void NAME(CheckerContext &C, const CallExpr *CE, ProgramStateRef State) const;
+
+ CHECK_FN(checkFree)
+ CHECK_FN(checkIfNameIndex)
+ CHECK_FN(checkBasicAlloc)
+ CHECK_FN(checkKernelMalloc)
+ CHECK_FN(checkCalloc)
+ CHECK_FN(checkAlloca)
+ CHECK_FN(checkStrdup)
+ CHECK_FN(checkIfFreeNameIndex)
+ CHECK_FN(checkCXXNewOrCXXDelete)
+ CHECK_FN(checkGMalloc0)
+ CHECK_FN(checkGMemdup)
+ CHECK_FN(checkGMallocN)
+ CHECK_FN(checkGMallocN0)
+ CHECK_FN(checkReallocN)
+ CHECK_FN(checkOwnershipAttr)
+
+ void checkRealloc(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State, bool ShouldFreeOnFail) const;
+
+ using CheckFn =
+ std::function<void(const MallocChecker *, CheckerContext &C,
+ const CallExpr *CE, ProgramStateRef State)>;
+
+ const CallDescriptionMap<CheckFn> FreeingMemFnMap{
+ {{"free", 1}, &MallocChecker::checkFree},
+ {{"if_freenameindex", 1}, &MallocChecker::checkIfFreeNameIndex},
+ {{"kfree", 1}, &MallocChecker::checkFree},
+ {{"g_free", 1}, &MallocChecker::checkFree},
+ };
+
+ bool isFreeingCall(const CallEvent &Call) const;
+
+ CallDescriptionMap<CheckFn> AllocatingMemFnMap{
+ {{"alloca", 1}, &MallocChecker::checkAlloca},
+ {{"_alloca", 1}, &MallocChecker::checkAlloca},
+ {{"malloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{"malloc", 3}, &MallocChecker::checkKernelMalloc},
+ {{"calloc", 2}, &MallocChecker::checkCalloc},
+ {{"valloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{CDF_MaybeBuiltin, "strndup", 2}, &MallocChecker::checkStrdup},
+ {{CDF_MaybeBuiltin, "strdup", 1}, &MallocChecker::checkStrdup},
+ {{"_strdup", 1}, &MallocChecker::checkStrdup},
+ {{"kmalloc", 2}, &MallocChecker::checkKernelMalloc},
+ {{"if_nameindex", 1}, &MallocChecker::checkIfNameIndex},
+ {{CDF_MaybeBuiltin, "wcsdup", 1}, &MallocChecker::checkStrdup},
+ {{CDF_MaybeBuiltin, "_wcsdup", 1}, &MallocChecker::checkStrdup},
+ {{"g_malloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{"g_malloc0", 1}, &MallocChecker::checkGMalloc0},
+ {{"g_try_malloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{"g_try_malloc0", 1}, &MallocChecker::checkGMalloc0},
+ {{"g_memdup", 2}, &MallocChecker::checkGMemdup},
+ {{"g_malloc_n", 2}, &MallocChecker::checkGMallocN},
+ {{"g_malloc0_n", 2}, &MallocChecker::checkGMallocN0},
+ {{"g_try_malloc_n", 2}, &MallocChecker::checkGMallocN},
+ {{"g_try_malloc0_n", 2}, &MallocChecker::checkGMallocN0},
+ };
+
+ CallDescriptionMap<CheckFn> ReallocatingMemFnMap{
+ {{"realloc", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
+ {{"reallocf", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, true)},
+ {{"g_realloc", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
+ {{"g_try_realloc", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
+ {{"g_realloc_n", 3}, &MallocChecker::checkReallocN},
+ {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN},
+ };
+
+ bool isMemCall(const CallEvent &Call) const;
+
// TODO: Remove mutable by moving the initializtaion to the registry function.
mutable Optional<uint64_t> KernelZeroFlagVal;
@@ -822,28 +861,31 @@ class StopTrackingCallback final : public SymbolVisitor {
};
} // end anonymous namespace
-//===----------------------------------------------------------------------===//
-// Methods of MemFunctionInfoTy.
-//===----------------------------------------------------------------------===//
+static bool isStandardNewDelete(const FunctionDecl *FD) {
+ if (!FD)
+ return false;
-bool MemFunctionInfoTy::isMemFunction(const CallEvent &Call) const {
- return isCMemFunction(Call) || isStandardNewDelete(Call);
-}
+ OverloadedOperatorKind Kind = FD->getOverloadedOperator();
+ if (Kind != OO_New && Kind != OO_Array_New && Kind != OO_Delete &&
+ Kind != OO_Array_Delete)
+ return false;
-bool MemFunctionInfoTy::isCMemFunction(const CallEvent &Call) const {
- return isCMemFreeFunction(Call) || isCMemAllocFunction(Call);
+ // 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() ||
+ FD->getASTContext().getSourceManager().isInSystemHeader(L);
}
-bool MemFunctionInfoTy::isCMemFreeFunction(const CallEvent &Call) const {
- if (Call.isCalled(CD_free, CD_realloc, CD_reallocf, CD_g_free, CD_kfree))
- return true;
+//===----------------------------------------------------------------------===//
+// Methods of MallocChecker and MallocBugVisitor.
+//===----------------------------------------------------------------------===//
- if (Call.isCalled(CD_if_freenameindex))
+bool MallocChecker::isFreeingCall(const CallEvent &Call) const {
+ if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))
return true;
- if (!ShouldIncludeOwnershipAnnotatedFunctions)
- return false;
-
const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());
if (Func && Func->hasAttrs()) {
for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {
@@ -855,60 +897,21 @@ bool MemFunctionInfoTy::isCMemFreeFunction(const CallEvent &Call) const {
return false;
}
-bool MemFunctionInfoTy::isCMemAllocFunction(const CallEvent &Call) const {
- if (Call.isCalled(CD_malloc, CD_realloc, CD_reallocf, CD_calloc, CD_valloc,
- CD_strdup, CD_win_strdup, CD_strndup, CD_wcsdup,
- CD_win_wcsdup, CD_kmalloc, CD_g_malloc, CD_g_malloc0,
- CD_g_realloc, CD_g_try_malloc, CD_g_try_malloc0,
- CD_g_try_realloc, CD_g_memdup, CD_g_malloc_n,
- CD_g_malloc0_n, CD_g_realloc_n, CD_g_try_malloc_n,
- CD_g_try_malloc0_n, CD_g_try_realloc_n))
- return true;
-
- if (Call.isCalled(CD_if_nameindex))
- return true;
-
- if (Call.isCalled(CD_alloca, CD_win_alloca))
+bool MallocChecker::isMemCall(const CallEvent &Call) const {
+ if (FreeingMemFnMap.lookup(Call) || AllocatingMemFnMap.lookup(Call) ||
+ ReallocatingMemFnMap.lookup(Call))
return true;
if (!ShouldIncludeOwnershipAnnotatedFunctions)
return false;
const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());
- if (Func && Func->hasAttrs()) {
- for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {
- OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
- if (OwnKind == OwnershipAttr::Returns)
- return true;
- }
- }
-
- return false;
-}
-
-static bool isStandardNewDelete(const FunctionDecl *FD) {
- 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() ||
- FD->getASTContext().getSourceManager().isInSystemHeader(L);
+ return Func && Func->hasAttr<OwnershipAttr>();
}
-//===----------------------------------------------------------------------===//
-// Methods of MallocChecker and MallocBugVisitor.
-//===----------------------------------------------------------------------===//
-
-llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
- const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
+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);
@@ -996,173 +999,169 @@ SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
return TotalSize;
}
-void MallocChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- if (C.wasInlined)
- return;
+void MallocChecker::checkBasicAlloc(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ C.addTransition(State);
+}
- const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return;
+void MallocChecker::checkKernelMalloc(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State =
+ MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc);
+ C.addTransition(State);
+}
- const FunctionDecl *FD = C.getCalleeDecl(CE);
- if (!FD)
+void MallocChecker::checkRealloc(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State,
+ bool ShouldFreeOnFail) const {
+ State = ReallocMemAux(C, CE, ShouldFreeOnFail, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkCalloc(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ State = CallocMem(C, CE, State);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkFree(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ bool IsKnownToBeAllocatedMemory = false;
+ if (suppressDeallocationsInSuspiciousContexts(CE, C))
return;
+ State =
+ FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory, AF_Malloc);
+ C.addTransition(State);
+}
- ProgramStateRef State = C.getState();
+void MallocChecker::checkAlloca(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Alloca);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkStrdup(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ State = MallocUpdateRefState(C, CE, State, AF_Malloc);
+
+ C.addTransition(State);
+}
+
+void MallocChecker::checkIfNameIndex(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ // Should we model this
diff erently? We can allocate a fixed number of
+ // elements with zeros in the last one.
+ State =
+ MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, AF_IfNameIndex);
+
+ C.addTransition(State);
+}
+
+void MallocChecker::checkIfFreeNameIndex(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
bool IsKnownToBeAllocatedMemory = false;
+ State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_IfNameIndex);
+ C.addTransition(State);
+}
- if (FD->getKind() == Decl::Function) {
- if (Call.isCalled(MemFunctionInfo.CD_malloc, MemFunctionInfo.CD_BSD_malloc,
- MemFunctionInfo.CD_g_malloc,
- MemFunctionInfo.CD_g_try_malloc)) {
- switch (CE->getNumArgs()) {
- default:
- return;
- case 1:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case 2:
- llvm_unreachable("There shouldn't be a 2-argument malloc!");
- break;
- case 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,
- AF_Malloc);
- break;
- }
- } else if (Call.isCalled(MemFunctionInfo.CD_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,
- AF_Malloc);
- } else if (Call.isCalled(MemFunctionInfo.CD_valloc)) {
- if (CE->getNumArgs() < 1)
- return;
- State =
- MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_realloc,
- MemFunctionInfo.CD_g_realloc,
- MemFunctionInfo.CD_g_try_realloc)) {
- State =
- ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_reallocf)) {
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_calloc)) {
- State = CallocMem(C, CE, State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_free, MemFunctionInfo.CD_g_free,
- MemFunctionInfo.CD_kfree)) {
- if (suppressDeallocationsInSuspiciousContexts(CE, C))
- return;
+void MallocChecker::checkCXXNewOrCXXDelete(CheckerContext &C,
+ const CallExpr *CE,
+ ProgramStateRef State) const {
+ bool IsKnownToBeAllocatedMemory = false;
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_Malloc);
- } else if (Call.isCalled(
- MemFunctionInfo.CD_strdup, MemFunctionInfo.CD_win_strdup,
- MemFunctionInfo.CD_wcsdup, MemFunctionInfo.CD_win_wcsdup)) {
- State = MallocUpdateRefState(C, CE, State, AF_Malloc);
- } else if (Call.isCalled(MemFunctionInfo.CD_strndup)) {
- State = MallocUpdateRefState(C, CE, State, AF_Malloc);
- } else if (Call.isCalled(MemFunctionInfo.CD_alloca,
- MemFunctionInfo.CD_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 (isStandardNewDelete(FD)) {
- // 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:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNew);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case OO_Array_New:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNewArray);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case OO_Delete:
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_CXXNew);
- break;
- case OO_Array_Delete:
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_CXXNewArray);
- break;
- default:
- llvm_unreachable("not a new/delete operator");
- }
- } else if (Call.isCalled(MemFunctionInfo.CD_if_nameindex)) {
- // Should we model this
diff erently? 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 (Call.isCalled(MemFunctionInfo.CD_if_freenameindex)) {
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
- AF_IfNameIndex);
- } else if (Call.isCalled(MemFunctionInfo.CD_g_malloc0,
- MemFunctionInfo.CD_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, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_g_memdup)) {
- if (CE->getNumArgs() < 2)
- return;
- State =
- MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_g_malloc_n,
- MemFunctionInfo.CD_g_try_malloc_n,
- MemFunctionInfo.CD_g_malloc0_n,
- MemFunctionInfo.CD_g_try_malloc0_n)) {
- if (CE->getNumArgs() < 2)
- return;
- SVal Init = UndefinedVal();
- if (Call.isCalled(MemFunctionInfo.CD_g_malloc0_n,
- MemFunctionInfo.CD_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, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (Call.isCalled(MemFunctionInfo.CD_g_realloc_n,
- MemFunctionInfo.CD_g_try_realloc_n)) {
- if (CE->getNumArgs() < 3)
- return;
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State, AF_Malloc,
- /*SuffixWithN*/ true);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- State = ProcessZeroAllocCheck(C, CE, 2, State);
- }
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ // 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:
+ State =
+ MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNew);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ break;
+ case OO_Array_New:
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_CXXNewArray);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ break;
+ case OO_Delete:
+ State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_CXXNew);
+ break;
+ case OO_Array_Delete:
+ State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_CXXNewArray);
+ break;
+ default:
+ llvm_unreachable("not a new/delete operator");
}
- if (MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions ||
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMalloc0(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
+ State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMemdup(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State, AF_Malloc);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMallocN(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ SVal Init = UndefinedVal();
+ SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+ State = MallocMemAux(C, CE, TotalSize, Init, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMallocN0(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ SValBuilder &SB = C.getSValBuilder();
+ SVal Init = SB.makeZeroVal(SB.getContext().CharTy);
+ SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+ State = MallocMemAux(C, CE, TotalSize, Init, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkReallocN(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ State = ReallocMemAux(C, CE, /*ShouldFreeOnFail=*/false, State, AF_Malloc,
+ /*SuffixWithN=*/true);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ State = ProcessZeroAllocCheck(C, CE, 2, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkOwnershipAttr(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) const {
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (ShouldIncludeOwnershipAnnotatedFunctions ||
ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
@@ -1182,6 +1181,44 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
C.addTransition(State);
}
+void MallocChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (C.wasInlined)
+ return;
+
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (const CheckFn *Callback = FreeingMemFnMap.lookup(Call)) {
+ (*Callback)(this, C, CE, State);
+ return;
+ }
+
+ if (const CheckFn *Callback = AllocatingMemFnMap.lookup(Call)) {
+ (*Callback)(this, C, CE, State);
+ return;
+ }
+
+ if (const CheckFn *Callback = ReallocatingMemFnMap.lookup(Call)) {
+ (*Callback)(this, C, CE, State);
+ return;
+ }
+
+ if (isStandardNewDelete(Call)) {
+ checkCXXNewOrCXXDelete(C, CE, State);
+ return;
+ }
+
+ checkOwnershipAttr(C, CE, State);
+}
+
// Performs a 0-sized allocations check.
ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg,
@@ -1448,8 +1485,7 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
if (!State)
return nullptr;
- if (Att->getModule()->getName() !=
- MemFunctionInfo.CD_malloc.getFunctionName())
+ if (Att->getModule()->getName() != "malloc")
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
@@ -1545,8 +1581,7 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
if (!State)
return nullptr;
- if (Att->getModule()->getName() !=
- MemFunctionInfo.CD_malloc.getFunctionName())
+ if (Att->getModule()->getName() != "malloc")
return nullptr;
bool IsKnownToBeAllocated = false;
@@ -2597,8 +2632,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
if (!FD)
return;
- if (ChecksEnabled[CK_MallocChecker] &&
- (MemFunctionInfo.isCMemFreeFunction(Call)))
+ if (ChecksEnabled[CK_MallocChecker] && isFreeingCall(Call))
return;
}
@@ -2897,7 +2931,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// If it's one of the allocation functions we can reason about, we model
// its behavior explicitly.
- if (MemFunctionInfo.isMemFunction(*Call))
+ if (isMemCall(*Call))
return false;
// If it's not a system call, assume it frees memory.
@@ -3315,7 +3349,7 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
auto *checker = mgr.registerChecker<MallocChecker>();
- checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions =
+ checker->ShouldIncludeOwnershipAnnotatedFunctions =
mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
}
More information about the cfe-commits
mailing list