r204832 - [analyzer] Handle the M_ZERO and __GFP_ZERO flags in kernel mallocs.
Jordan Rose
jordan_rose at apple.com
Wed Mar 26 10:05:47 PDT 2014
Author: jrose
Date: Wed Mar 26 12:05:46 2014
New Revision: 204832
URL: http://llvm.org/viewvc/llvm-project?rev=204832&view=rev
Log:
[analyzer] Handle the M_ZERO and __GFP_ZERO flags in kernel mallocs.
Add M_ZERO awareness to malloc() static analysis in Clang for FreeBSD,
NetBSD, and OpenBSD in a similar fashion to O_CREAT for open(2).
These systems have a three-argument malloc() in the kernel where the
third argument contains flags; the M_ZERO flag will zero-initialize the
allocated buffer.
This should reduce the number of false positives when running static
analysis on BSD kernels.
Additionally, add kmalloc() (Linux kernel malloc()) and treat __GFP_ZERO
like M_ZERO on Linux.
Future work involves a better method of checking for named flags without
hardcoding values.
Patch by Conrad Meyer, with minor modifications by me.
Added:
cfe/trunk/test/Analysis/kmalloc-linux.c
cfe/trunk/test/Analysis/malloc-three-arg.c
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=204832&r1=204831&r2=204832&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Wed Mar 26 12:05:46 2014
@@ -16,6 +16,7 @@
#include "InterCheckerAPI.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -157,7 +158,8 @@ class MallocChecker : public Checker<che
{
public:
MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0),
- II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {}
+ II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0),
+ II_kmalloc(0) {}
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
@@ -207,7 +209,9 @@ private:
mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
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_valloc, *II_reallocf, *II_strndup, *II_strdup,
+ *II_kmalloc;
+ mutable Optional<uint64_t> KernelZeroFlagVal;
void initIdentifierInfo(ASTContext &C) const;
@@ -253,6 +257,12 @@ private:
ProgramStateRef State,
AllocationFamily Family = AF_Malloc);
+ // Check if this malloc() for special flags. At present that means M_ZERO or
+ // __GFP_ZERO (in which case, treat it like calloc).
+ llvm::Optional<ProgramStateRef>
+ performKernelMalloc(const CallExpr *CE, CheckerContext &C,
+ const ProgramStateRef &State) const;
+
/// Update the RefState to reflect the new memory allocation.
static ProgramStateRef
MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
@@ -482,6 +492,7 @@ void MallocChecker::initIdentifierInfo(A
II_valloc = &Ctx.Idents.get("valloc");
II_strdup = &Ctx.Idents.get("strdup");
II_strndup = &Ctx.Idents.get("strndup");
+ II_kmalloc = &Ctx.Idents.get("kmalloc");
}
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
@@ -508,7 +519,7 @@ bool MallocChecker::isAllocationFunction
if (FunI == II_malloc || FunI == II_realloc ||
FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
- FunI == II_strdup || FunI == II_strndup)
+ FunI == II_strdup || FunI == II_strndup || FunI == II_kmalloc)
return true;
}
@@ -572,10 +583,88 @@ bool MallocChecker::isStandardNewDelete(
return true;
}
+llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
+ const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
+ // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
+ //
+ // void *malloc(unsigned long size, struct malloc_type *mtp, int flags);
+ //
+ // One of the possible flags is M_ZERO, which means 'give me back an
+ // allocation which is already zeroed', like calloc.
+
+ // 2-argument kmalloc(), as used in the Linux kernel:
+ //
+ // void *kmalloc(size_t size, gfp_t flags);
+ //
+ // Has the similar flag value __GFP_ZERO.
+
+ // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some
+ // code could be shared.
+
+ ASTContext &Ctx = C.getASTContext();
+ llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS();
+
+ if (!KernelZeroFlagVal.hasValue()) {
+ if (OS == llvm::Triple::FreeBSD)
+ KernelZeroFlagVal = 0x0100;
+ else if (OS == llvm::Triple::NetBSD)
+ KernelZeroFlagVal = 0x0002;
+ else if (OS == llvm::Triple::OpenBSD)
+ KernelZeroFlagVal = 0x0008;
+ else if (OS == llvm::Triple::Linux)
+ // __GFP_ZERO
+ KernelZeroFlagVal = 0x8000;
+ else
+ // FIXME: We need a more general way of getting the M_ZERO value.
+ // See also: O_CREAT in UnixAPIChecker.cpp.
+
+ // Fall back to normal malloc behavior on platforms where we don't
+ // know M_ZERO.
+ return None;
+ }
+
+ // We treat the last argument as the flags argument, and callers fall-back to
+ // normal malloc on a None return. This works for the FreeBSD kernel malloc
+ // as well as Linux kmalloc.
+ if (CE->getNumArgs() < 2)
+ return None;
+
+ const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
+ const SVal V = State->getSVal(FlagsEx, C.getLocationContext());
+ if (!V.getAs<NonLoc>()) {
+ // The case where 'V' can be a location can only be due to a bad header,
+ // so in this case bail out.
+ return None;
+ }
+
+ NonLoc Flags = V.castAs<NonLoc>();
+ NonLoc ZeroFlag = C.getSValBuilder()
+ .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType())
+ .castAs<NonLoc>();
+ SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
+ Flags, ZeroFlag,
+ FlagsEx->getType());
+ if (MaskedFlagsUC.isUnknownOrUndef())
+ return None;
+ DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
+
+ // Check if maskedFlags is non-zero.
+ ProgramStateRef TrueState, FalseState;
+ std::tie(TrueState, FalseState) = State->assume(MaskedFlags);
+
+ // If M_ZERO is set, treat this like calloc (initialized).
+ if (TrueState && !FalseState) {
+ SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
+ return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState);
+ }
+
+ return None;
+}
+
void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (C.wasInlined)
return;
-
+
const FunctionDecl *FD = C.getCalleeDecl(CE);
if (!FD)
return;
@@ -587,7 +676,27 @@ void MallocChecker::checkPostStmt(const
initIdentifierInfo(C.getASTContext());
IdentifierInfo *FunI = FD->getIdentifier();
- if (FunI == II_malloc || FunI == II_valloc) {
+ if (FunI == II_malloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ if (CE->getNumArgs() < 3) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (CE->getNumArgs() == 3) {
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ }
+ } else if (FunI == II_kmalloc) {
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (FunI == II_valloc) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp?rev=204832&r1=204831&r2=204832&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp Wed Mar 26 12:05:46 2014
@@ -80,6 +80,7 @@ void UnixAPIChecker::CheckOpen(CheckerCo
// FIXME: We need a more general way of getting the O_CREAT value.
// We could possibly grovel through the preprocessor state, but
// that would require passing the Preprocessor object to the ExprEngine.
+ // See also: MallocChecker.cpp / M_ZERO.
return;
}
}
Added: cfe/trunk/test/Analysis/kmalloc-linux.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/kmalloc-linux.c?rev=204832&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/kmalloc-linux.c (added)
+++ cfe/trunk/test/Analysis/kmalloc-linux.c Wed Mar 26 12:05:46 2014
@@ -0,0 +1,58 @@
+// RUN: %clang -target x86_64-unknown-linux --analyze %s
+
+#include "Inputs/system-header-simulator.h"
+
+#define __GFP_ZERO 0x8000
+#define NULL ((void *)0)
+
+void *kmalloc(size_t, int);
+
+struct test {
+};
+
+void foo(struct test *);
+
+void test_zeroed() {
+ struct test **list, *t;
+ int i;
+
+ list = kmalloc(sizeof(*list) * 10, __GFP_ZERO);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ t = list[i];
+ foo(t);
+ }
+ free(list); // no-warning
+}
+
+void test_nonzero() {
+ struct test **list, *t;
+ int i;
+
+ list = kmalloc(sizeof(*list) * 10, 0);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ t = list[i]; // expected-warning{{undefined}}
+ foo(t);
+ }
+ free(list);
+}
+
+void test_indeterminate(int flags) {
+ struct test **list, *t;
+ int i;
+
+ list = kmalloc(sizeof(*list) * 10, flags);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ t = list[i]; // expected-warning{{undefined}}
+ foo(t);
+ }
+ free(list);
+}
Added: cfe/trunk/test/Analysis/malloc-three-arg.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/malloc-three-arg.c?rev=204832&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/malloc-three-arg.c (added)
+++ cfe/trunk/test/Analysis/malloc-three-arg.c Wed Mar 26 12:05:46 2014
@@ -0,0 +1,58 @@
+// RUN: %clang -target x86_64-unknown-freebsd --analyze %s
+
+#include "Inputs/system-header-simulator.h"
+
+#define M_ZERO 0x0100
+#define NULL ((void *)0)
+
+void *malloc(size_t, void *, int);
+
+struct test {
+};
+
+void foo(struct test *);
+
+void test_zeroed() {
+ struct test **list, *t;
+ int i;
+
+ list = malloc(sizeof(*list) * 10, NULL, M_ZERO);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ t = list[i];
+ foo(t);
+ }
+ free(list); // no-warning
+}
+
+void test_nonzero() {
+ struct test **list, *t;
+ int i;
+
+ list = malloc(sizeof(*list) * 10, NULL, 0);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ t = list[i]; // expected-warning{{undefined}}
+ foo(t);
+ }
+ free(list);
+}
+
+void test_indeterminate(int flags) {
+ struct test **list, *t;
+ int i;
+
+ list = malloc(sizeof(*list) * 10, NULL, flags);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ t = list[i]; // expected-warning{{undefined}}
+ foo(t);
+ }
+ free(list);
+}
More information about the cfe-commits
mailing list