Skip to content

Commit dcc04e0

Browse files
committed
[Analyzer][MallocChecker] No warning for kfree of ZERO_SIZE_PTR.
Summary: The kernel kmalloc function may return a constant value ZERO_SIZE_PTR if a zero-sized block is allocated. This special value is allowed to be passed to kfree and should produce no warning. This is a simple version but should be no problem. The macro is always detected independent of if this is a kernel source code or any other code. Reviewers: Szelethus, martong Reviewed By: Szelethus, martong Subscribers: rnkovacs, xazax.hun, baloghadamsoftware, szepet, a.sidorin, mikhail.ramalho, Szelethus, donat.nagy, dkrupp, gamesh411, Charusso, martong, ASDenysPetrov, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D76830
1 parent 65c7031 commit dcc04e0

File tree

4 files changed

+56
-8
lines changed

4 files changed

+56
-8
lines changed

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
5959
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
6060
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
61+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
6162
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
6263
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
6364
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
@@ -389,6 +390,13 @@ class MallocChecker
389390
// TODO: Remove mutable by moving the initializtaion to the registry function.
390391
mutable Optional<uint64_t> KernelZeroFlagVal;
391392

393+
using KernelZeroSizePtrValueTy = Optional<int>;
394+
/// Store the value of macro called `ZERO_SIZE_PTR`.
395+
/// The value is initialized at first use, before first use the outer
396+
/// Optional is empty, afterwards it contains another Optional that indicates
397+
/// if the macro value could be determined, and if yes the value itself.
398+
mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;
399+
392400
/// Process C++ operator new()'s allocation, which is the part of C++
393401
/// new-expression that goes before the constructor.
394402
void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
@@ -658,6 +666,10 @@ class MallocChecker
658666
CheckerContext &C);
659667

660668
void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
669+
670+
/// Test if value in ArgVal equals to value in macro `ZERO_SIZE_PTR`.
671+
bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
672+
SVal ArgVal) const;
661673
};
662674

663675
//===----------------------------------------------------------------------===//
@@ -1677,7 +1689,13 @@ ProgramStateRef MallocChecker::FreeMemAux(
16771689
// Nonlocs can't be freed, of course.
16781690
// Non-region locations (labels and fixed addresses) also shouldn't be freed.
16791691
if (!R) {
1680-
ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family);
1692+
// Exception:
1693+
// If the macro ZERO_SIZE_PTR is defined, this could be a kernel source
1694+
// code. In that case, the ZERO_SIZE_PTR defines a special value used for a
1695+
// zero-sized memory block which is allowed to be freed, despite not being a
1696+
// null pointer.
1697+
if (Family != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal))
1698+
ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family);
16811699
return nullptr;
16821700
}
16831701

@@ -3023,6 +3041,18 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
30233041
return State;
30243042
}
30253043

3044+
bool MallocChecker::isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
3045+
SVal ArgVal) const {
3046+
if (!KernelZeroSizePtrValue)
3047+
KernelZeroSizePtrValue =
3048+
tryExpandAsInteger("ZERO_SIZE_PTR", C.getPreprocessor());
3049+
3050+
const llvm::APSInt *ArgValKnown =
3051+
C.getSValBuilder().getKnownValue(State, ArgVal);
3052+
return ArgValKnown && *KernelZeroSizePtrValue &&
3053+
ArgValKnown->getSExtValue() == **KernelZeroSizePtrValue;
3054+
}
3055+
30263056
static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
30273057
ProgramStateRef prevState) {
30283058
ReallocPairsTy currMap = currState->get<ReallocPairs>();

clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,6 @@ llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
126126
if (!T.isOneOf(tok::l_paren, tok::r_paren))
127127
FilteredTokens.push_back(T);
128128

129-
if (FilteredTokens.size() > 2)
130-
return llvm::None;
131-
132129
// Parse an integer at the end of the macro definition.
133130
const Token &T = FilteredTokens.back();
134131
if (!T.isLiteral())
@@ -140,11 +137,10 @@ llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
140137
return llvm::None;
141138

142139
// Parse an optional minus sign.
143-
if (FilteredTokens.size() == 2) {
144-
if (FilteredTokens.front().is(tok::minus))
140+
size_t Size = FilteredTokens.size();
141+
if (Size >= 2) {
142+
if (FilteredTokens[Size - 2].is(tok::minus))
145143
IntValue = -IntValue;
146-
else
147-
return llvm::None;
148144
}
149145

150146
return IntValue.getSExtValue();

clang/test/Analysis/kmalloc-linux.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,17 @@ void test_3arg_malloc_leak(struct malloc_type *mtp, int flags) {
121121
if (list == NULL)
122122
return;
123123
} // expected-warning{{Potential leak of memory pointed to by 'list'}}
124+
125+
// kmalloc can return a constant value defined in ZERO_SIZE_PTR
126+
// if a block of size 0 is requested
127+
#define ZERO_SIZE_PTR ((void *)16)
128+
129+
void test_kfree_ZERO_SIZE_PTR() {
130+
void *ptr = ZERO_SIZE_PTR;
131+
kfree(ptr); // no warning about freeing this value
132+
}
133+
134+
void test_kfree_other_constant_value() {
135+
void *ptr = (void *)1;
136+
kfree(ptr); // expected-warning{{Argument to kfree() is a constant address (1)}}
137+
}

clang/test/Analysis/malloc.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,11 @@ void test(A a) {
164164
(void)a.getName();
165165
}
166166
} // namespace argument_leak
167+
168+
#define ZERO_SIZE_PTR ((void *)16)
169+
170+
void test_delete_ZERO_SIZE_PTR() {
171+
int *Ptr = (int *)ZERO_SIZE_PTR;
172+
// ZERO_SIZE_PTR is specially handled but only for malloc family
173+
delete Ptr; // expected-warning{{Argument to 'delete' is a constant address (16)}}
174+
}

0 commit comments

Comments
 (0)