From 5a9b42fd92e7e412c396cf2aa584484211fee8aa Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Fri, 3 Oct 2014 21:48:59 +0000 Subject: [PATCH] [analyzer] Make Malloc Checker track memory allocated by if_nameindex The MallocChecker does currently not track the memory allocated by if_nameindex. That memory is dynamically allocated and should be freed by calling if_freenameindex. The attached patch teaches the checker about these functions. Memory allocated by if_nameindex is treated as a separate allocation "family". That way the checker can verify it is freed by the correct function. A patch by Daniel Fahlgren! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@219025 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 118 ++++++++++++------ 1 file changed, 81 insertions(+), 37 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 31c30dcf28..435a253a6b 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -42,7 +42,8 @@ enum AllocationFamily { AF_None, AF_Malloc, AF_CXXNew, - AF_CXXNewArray + AF_CXXNewArray, + AF_IfNameIndex }; class RefState { @@ -161,7 +162,8 @@ public: MallocChecker() : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr), - II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr) {} + II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr), + II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -174,6 +176,12 @@ public: CK_NumCheckKinds }; + enum class MemoryOperationKind { + MOK_Allocate, + MOK_Free, + MOK_Any + }; + DefaultBool ChecksEnabled[CK_NumCheckKinds]; CheckName CheckNames[CK_NumCheckKinds]; @@ -212,7 +220,7 @@ private: mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup, - *II_kmalloc; + *II_kmalloc, *II_if_nameindex, *II_if_freenameindex; mutable Optional KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -238,8 +246,10 @@ private: /// 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 isFreeFunction(const FunctionDecl *FD, ASTContext &C) const; - bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isCMemFunction(const FunctionDecl *FD, + ASTContext &C, + AllocationFamily Family, + enum MemoryOperationKind) const; bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; ///@} ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, @@ -496,13 +506,15 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_strdup = &Ctx.Idents.get("strdup"); II_strndup = &Ctx.Idents.get("strndup"); II_kmalloc = &Ctx.Idents.get("kmalloc"); + II_if_nameindex = &Ctx.Idents.get("if_nameindex"); + II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { - if (isFreeFunction(FD, C)) + if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any)) return true; - if (isAllocationFunction(FD, C)) + if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) return true; if (isStandardNewDelete(FD, C)) @@ -511,45 +523,61 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { return false; } -bool MallocChecker::isAllocationFunction(const FunctionDecl *FD, - ASTContext &C) const { +bool MallocChecker::isCMemFunction(const FunctionDecl *FD, + ASTContext &C, + AllocationFamily Family, + enum MemoryOperationKind MemKind) const { if (!FD) return false; + bool CheckFree = (MemKind == MemoryOperationKind::MOK_Any || + MemKind == MemoryOperationKind::MOK_Free); + bool CheckAlloc = (MemKind == MemoryOperationKind::MOK_Any || + MemKind == MemoryOperationKind::MOK_Allocate); + if (FD->getKind() == Decl::Function) { - IdentifierInfo *FunI = FD->getIdentifier(); + const IdentifierInfo *FunI = FD->getIdentifier(); initIdentifierInfo(C); - if (FunI == II_malloc || FunI == II_realloc || - FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || - FunI == II_strdup || FunI == II_strndup || FunI == II_kmalloc) - return true; - } + if (Family == AF_Malloc && CheckFree) { + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) + return true; + } - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) - for (const auto *I : FD->specific_attrs()) - if (I->getOwnKind() == OwnershipAttr::Returns) + if (Family == AF_Malloc && CheckAlloc) { + if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || + FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || + FunI == II_strndup || FunI == II_kmalloc) return true; - return false; -} + } -bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const { - if (!FD) - return false; + if (Family == AF_IfNameIndex && CheckFree) { + if (FunI == II_if_freenameindex) + return true; + } - if (FD->getKind() == Decl::Function) { - IdentifierInfo *FunI = FD->getIdentifier(); - initIdentifierInfo(C); + if (Family == AF_IfNameIndex && CheckAlloc) { + if (FunI == II_if_nameindex) + return true; + } + } - if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) - return true; + if (Family != AF_Malloc) + return false; + + if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) { + for (const auto *I : FD->specific_attrs()) { + OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); + if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { + if (CheckFree) + return true; + } else if (OwnKind == OwnershipAttr::Returns) { + if (CheckAlloc) + return true; + } + } } - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) - for (const auto *I : FD->specific_attrs()) - if (I->getOwnKind() == OwnershipAttr::Takes || - I->getOwnKind() == OwnershipAttr::Holds) - return true; return false; } @@ -732,6 +760,13 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); else llvm_unreachable("not a new/delete operator"); + } else if (FunI == II_if_nameindex) { + // Should we model this differently? We can allocate a fixed number of + // elements with zeros in the last one. + State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, + AF_IfNameIndex); + } else if (FunI == II_if_freenameindex) { + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } } @@ -1016,7 +1051,7 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, ASTContext &Ctx = C.getASTContext(); - if (isAllocationFunction(FD, Ctx) || isFreeFunction(FD, Ctx)) + if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any)) return AF_Malloc; if (isStandardNewDelete(FD, Ctx)) { @@ -1027,6 +1062,9 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_CXXNewArray; } + if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) + return AF_IfNameIndex; + return AF_None; } @@ -1090,6 +1128,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, case AF_Malloc: os << "malloc()"; return; case AF_CXXNew: os << "'new'"; return; case AF_CXXNewArray: os << "'new[]'"; return; + case AF_IfNameIndex: os << "'if_nameindex()'"; return; case AF_None: llvm_unreachable("not a deallocation expression"); } } @@ -1100,6 +1139,7 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os, case AF_Malloc: os << "free()"; return; case AF_CXXNew: os << "'delete'"; return; case AF_CXXNewArray: os << "'delete[]'"; return; + case AF_IfNameIndex: os << "'if_freenameindex()'"; return; case AF_None: llvm_unreachable("suspicious AF_None argument"); } } @@ -1243,7 +1283,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, Optional MallocChecker::getCheckIfTracked(AllocationFamily Family) const { switch (Family) { - case AF_Malloc: { + case AF_Malloc: + case AF_IfNameIndex: { if (ChecksEnabled[CK_MallocOptimistic]) { return CK_MallocOptimistic; } else if (ChecksEnabled[CK_MallocPessimistic]) { @@ -1907,13 +1948,16 @@ void MallocChecker::checkPreCall(const CallEvent &Call, if (!FD) return; + ASTContext &Ctx = C.getASTContext(); if ((ChecksEnabled[CK_MallocOptimistic] || ChecksEnabled[CK_MallocPessimistic]) && - isFreeFunction(FD, C.getASTContext())) + (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || + isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Free))) return; if (ChecksEnabled[CK_NewDeleteChecker] && - isStandardNewDelete(FD, C.getASTContext())) + isStandardNewDelete(FD, Ctx)) return; } -- 2.40.0