balazske created this revision.
Herald added subscribers: cfe-commits, ASDenysPetrov, martong, Charusso, 
gamesh411, dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, 
baloghadamsoftware, xazax.hun.
Herald added a reviewer: Szelethus.
Herald added a project: clang.
balazske added a comment.
balazske marked an inline comment as done.

FIXME: There is a test file "kmalloc-linux.c" but it seems to be non-maintained 
and buggy (has no //-verify// option so it passes always but produces multiple 
warnings).



================
Comment at: clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp:146
 
   return IntValue.getSExtValue();
 }
----------------
The function was changed to get the numeric value from the end of the macro in 
any case. This way it recognizes a `(void *)16` as 16 (but maybe `16+16` too as 
16).


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. And it is recognized in any kind of free function, not only kfree.
(These functions are used already intermixed, at least in the tests.)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D76830

Files:
  clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
  clang/test/Analysis/kmalloc-linux-1.c

Index: clang/test/Analysis/kmalloc-linux-1.c
===================================================================
--- /dev/null
+++ 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)}}
+}
Index: clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ 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();
Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ 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<uint64_t> 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<Optional<int>> 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.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to