Hi,
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.
Any comments / feedback?
Best regards,
Daniel Fahlgren
Index: test/Analysis/ifnameindex.c
===================================================================
--- test/Analysis/ifnameindex.c (revision 0)
+++ test/Analysis/ifnameindex.c (revision 0)
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator -analyzer-store=region -verify %s
+
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void *valloc(size_t);
+void free(void *);
+void *realloc(void *ptr, size_t size);
+void *reallocf(void *ptr, size_t size);
+void *calloc(size_t nmemb, size_t size);
+char *strdup(const char *s);
+char *strndup(const char *s, size_t n);
+
+struct if_nameindex;
+struct if_nameindex *if_nameindex(void);
+void if_freenameindex(struct if_nameindex *ptr);
+
+void f1() {
+ struct if_nameindex *p;
+ p = if_nameindex();
+ if (p)
+ if_freenameindex(p); // no-warning
+}
+
+void f2() {
+ struct if_nameindex *p;
+ p = if_nameindex();
+ (void) p;
+ return; // expected-warning{{Potential leak of memory pointed to by 'p'}}
+}
+
+void f3() {
+ struct if_nameindex *p;
+ p = if_nameindex();
+ (void) p;
+ p = if_nameindex();
+ if (p)
+ if_freenameindex(p);
+ return; // expected-warning{{Potential leak of memory pointed to by 'p'}}
+}
+
+void f4() {
+ struct if_nameindex *p;
+ p = if_nameindex();
+ if (p) {
+ if_freenameindex(p);
+ if_freenameindex(p); // expected-warning{{Attempt to free released memory}}
+ }
+}
+
+void bar(const struct if_nameindex *p);
+
+void f5() {
+ struct if_nameindex *p;
+ p = if_nameindex();
+ if (p) {
+ if_freenameindex(p);
+ bar(p); // expected-warning{{Use of memory after it is freed}}
+ }
+}
+
+void f6() {
+ struct if_nameindex *p;
+ p = if_nameindex();
+ if (p)
+ free(p); // expected-warning{{Memory allocated by if_nameindex() should be deallocated by 'if_freenameindex()', not free()}}
+}
+
+void f7() {
+ struct if_nameindex *p;
+ void *p2;
+ p = if_nameindex();
+ if (p)
+ p2 = realloc(p, 1); // expected-warning{{Memory allocated by if_nameindex() should be deallocated by 'if_freenameindex()', not realloc()}}
+ (void) p2;
+}
+
+void f8() {
+ struct if_nameindex *p;
+ void *p2;
+ p = if_nameindex();
+ if (p)
+ p2 = reallocf(p, 1); // expected-warning{{Memory allocated by if_nameindex() should be deallocated by 'if_freenameindex()', not reallocf()}}
+ (void) p2;
+}
+
+void f9() {
+ struct if_nameindex *p;
+ p = malloc(100);
+ if (p)
+ if_freenameindex(p); // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not if_freenameindex()}}
+}
+
+void f10() {
+ struct if_nameindex *p;
+ p = valloc(100);
+ if (p)
+ if_freenameindex(p); // expected-warning{{Memory allocated by valloc() should be deallocated by free(), not if_freenameindex()}}
+}
+
+void f11() {
+ struct if_nameindex *p;
+ p = calloc(1, 100);
+ if (p)
+ if_freenameindex(p); // expected-warning{{Memory allocated by calloc() should be deallocated by free(), not if_freenameindex()}}
+}
+
+void f12(void *p) {
+ void *p2;
+ p2 = realloc(p, 1);
+ if (p2)
+ if_freenameindex(p2); // expected-warning{{Memory allocated by realloc() should be deallocated by free(), not if_freenameindex()}}
+}
+
+void f13(void *p) {
+ void *p2;
+ p2 = reallocf(p, 1);
+ if (p2)
+ if_freenameindex(p2); // expected-warning{{Memory allocated by reallocf() should be deallocated by free(), not if_freenameindex()}}
+}
+
+void f14(const char *str) {
+ struct if_nameindex *p;
+ p = (struct if_nameindex *) strdup(str);
+ if (p)
+ if_freenameindex(p); // expected-warning{{Memory allocated by strdup() should be deallocated by free(), not if_freenameindex()}}
+}
+
+void f15(const char *str) {
+ struct if_nameindex *p;
+ p = (struct if_nameindex *) strndup(str, 1);
+ if (p)
+ if_freenameindex(p); //expected-warning{{Memory allocated by strndup() should be deallocated by free(), not if_freenameindex()}}
+}
Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp (revision 216319)
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp (working copy)
@@ -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.
@@ -212,7 +214,7 @@ private:
mutable std::unique_ptr<BugType> 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<uint64_t> KernelZeroFlagVal;
void initIdentifierInfo(ASTContext &C) const;
@@ -241,6 +243,8 @@ private:
bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const;
bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
+ bool isIfNameFunction(const FunctionDecl *FD, ASTContext &C) const;
+ bool isIfFreeNameFunction(const FunctionDecl *FD, ASTContext &C) const;
///@}
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
@@ -495,6 +499,8 @@ void MallocChecker::initIdentifierInfo(A
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 {
@@ -507,6 +513,12 @@ bool MallocChecker::isMemFunction(const
if (isStandardNewDelete(FD, C))
return true;
+ if (isIfNameFunction(FD, C))
+ return true;
+
+ if (isIfFreeNameFunction(FD, C))
+ return true;
+
return false;
}
@@ -585,6 +597,37 @@ bool MallocChecker::isStandardNewDelete(
return true;
}
+bool MallocChecker::isIfNameFunction(const FunctionDecl *FD,
+ ASTContext &C) const {
+ if (!FD)
+ return false;
+
+ if (FD->getKind() == Decl::Function) {
+ IdentifierInfo *FunI = FD->getIdentifier();
+ initIdentifierInfo(C);
+
+ if (FunI == II_if_nameindex)
+ return true;
+ }
+
+ return false;
+}
+
+bool MallocChecker::isIfFreeNameFunction(const FunctionDecl *FD, ASTContext &C) const {
+ if (!FD)
+ return false;
+
+ if (FD->getKind() == Decl::Function) {
+ IdentifierInfo *FunI = FD->getIdentifier();
+ initIdentifierInfo(C);
+
+ if (FunI == II_if_freenameindex)
+ return true;
+ }
+
+ return false;
+}
+
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:
@@ -731,6 +774,13 @@ void MallocChecker::checkPostStmt(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);
}
}
@@ -1026,6 +1076,9 @@ AllocationFamily MallocChecker::getAlloc
return AF_CXXNewArray;
}
+ if (isIfNameFunction(FD, Ctx) || isIfFreeNameFunction(FD, Ctx))
+ return AF_IfNameIndex;
+
return AF_None;
}
@@ -1089,6 +1142,7 @@ void MallocChecker::printExpectedAllocNa
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");
}
}
@@ -1099,6 +1153,7 @@ void MallocChecker::printExpectedDealloc
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");
}
}
@@ -1242,7 +1297,8 @@ ProgramStateRef MallocChecker::FreeMemAu
Optional<MallocChecker::CheckKind>
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]) {
@@ -1908,7 +1964,8 @@ void MallocChecker::checkPreCall(const C
if ((ChecksEnabled[CK_MallocOptimistic] ||
ChecksEnabled[CK_MallocPessimistic]) &&
- isFreeFunction(FD, C.getASTContext()))
+ (isFreeFunction(FD, C.getASTContext()) ||
+ isIfFreeNameFunction(FD, C.getASTContext())))
return;
if (ChecksEnabled[CK_NewDeleteChecker] &&
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits