diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -58,6 +58,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -389,6 +390,11 @@ // TODO: Remove mutable by moving the initializtaion to the registry function. mutable Optional KernelZeroFlagVal; + /// Store value of "ZERO_SIZE_PTR" macro if found. + /// It requires two-level Optional: Was lazy initialization done yet, and if + /// yes was the value obtained or not. + mutable Optional> KernelZeroSizePtrValue; + /// 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, @@ -1672,6 +1678,20 @@ if (ArgVal.isUnknownOrUndef()) return nullptr; + // If there is a macro called ZERO_SIZE_PTR, it could be a kernel source code + // and this value indicates a special value used for a zero-sized memory + // block. It is a constant value that is allowed to be freed. + const llvm::APSInt *ArgValKnown = + C.getSValBuilder().getKnownValue(State, ArgVal); + if (ArgValKnown) { + if (!KernelZeroSizePtrValue) + KernelZeroSizePtrValue = + tryExpandAsInteger("ZERO_SIZE_PTR", C.getPreprocessor()); + if (*KernelZeroSizePtrValue && + ArgValKnown->getSExtValue() == **KernelZeroSizePtrValue) + return nullptr; + } + const MemRegion *R = ArgVal.getAsRegion(); // Nonlocs can't be freed, of course. diff --git a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp --- a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -126,9 +126,6 @@ if (!T.isOneOf(tok::l_paren, tok::r_paren)) FilteredTokens.push_back(T); - if (FilteredTokens.size() > 2) - return llvm::None; - // Parse an integer at the end of the macro definition. const Token &T = FilteredTokens.back(); if (!T.isLiteral()) @@ -140,11 +137,10 @@ return llvm::None; // Parse an optional minus sign. - if (FilteredTokens.size() == 2) { - if (FilteredTokens.front().is(tok::minus)) + size_t Size = FilteredTokens.size(); + if (Size >= 2) { + if (FilteredTokens[Size - 2].is(tok::minus)) IntValue = -IntValue; - else - return llvm::None; } return IntValue.getSExtValue(); diff --git a/clang/test/Analysis/kmalloc-linux-1.c b/clang/test/Analysis/kmalloc-linux-1.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/kmalloc-linux-1.c @@ -0,0 +1,27 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=unix.Malloc -verify %s + +#define ZERO_SIZE_PTR ((void *)16) + +void *__kmalloc(int size, int flags); + +// Linux kmalloc looks like this. +// (simplified) +void *kmalloc(int size, int flags) { + if (size == 0) + return ZERO_SIZE_PTR; + return __kmalloc(size, flags); +} + +// FIXME: malloc checker expects kfree with 2 arguments, is this correct? +// (recent kernel source code contains a `kfree(const void *)`) +void kfree(void *, int); + +void test_kmalloc_zero_sized_block_fixed_value_address() { + void *ptr = kmalloc(0, 0); // kmalloc returns a constant address + kfree(ptr, 0); // no warning about freeing a constant value +} + +void test_kfree_constant_value() { + void *ptr = (void *)1; + kfree(ptr, 0); // expected-warning{{Argument to kfree() is a constant address (1)}} +}