[clang] [analyzer] [MallocChecker] Fix Store modification in `checkPreCall` (PR #109337)

via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 19 14:36:15 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Pavel Skripkin (pskrgag)

<details>
<summary>Changes</summary>

This is small follow-up for #<!-- -->106081

While trying to add sanity check for `Enviroment` and `Store` being consistent during `checkPostCall` and `checkPreCall` I found out that `MallocChecker` still violates that rule. 

The problem lies in `FreeMemAux`, which invalidates freed region while being called from `preGetdelim`. This invalidation was added to prevent "use of uninitialized memory" for malloc and friends while `unix.Malloc` is disabled.

Fix adds another bool flag to `FreeMemAux` to control invalidation from call-site and set it to false in `preGetdelim`



---
Full diff: https://github.com/llvm/llvm-project/pull/109337.diff


2 Files Affected:

- (modified) clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (+30-25) 
- (modified) clang/test/Analysis/NewDelete-intersections.mm (+9) 


``````````diff
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 81ec8e1b516986..7a265d3bbcda47 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -668,7 +668,8 @@ class MallocChecker
   [[nodiscard]] ProgramStateRef
   FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State,
              unsigned Num, bool Hold, bool &IsKnownToBeAllocated,
-             AllocationFamily Family, bool ReturnsNullOnFailure = false) const;
+             AllocationFamily Family, bool Invalidate = true,
+             bool ReturnsNullOnFailure = false) const;
 
   /// Models memory deallocation.
   ///
@@ -694,7 +695,8 @@ class MallocChecker
   [[nodiscard]] ProgramStateRef
   FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
              ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
-             AllocationFamily Family, bool ReturnsNullOnFailure = false,
+             AllocationFamily Family, bool Invalidate = true,
+             bool ReturnsNullOnFailure = false,
              std::optional<SVal> ArgValOpt = {}) const;
 
   // TODO: Needs some refactoring, as all other deallocation modeling
@@ -1474,9 +1476,10 @@ void MallocChecker::preGetdelim(ProgramStateRef State, const CallEvent &Call,
   // We do not need this value here, as FreeMemAux will take care
   // of reporting any violation of the preconditions.
   bool IsKnownToBeAllocated = false;
-  State = FreeMemAux(C, Call.getArgExpr(0), Call, State, false,
-                     IsKnownToBeAllocated, AllocationFamily(AF_Malloc), false,
-                     LinePtr);
+  State =
+      FreeMemAux(C, Call.getArgExpr(0), Call, State, false,
+                 IsKnownToBeAllocated, AllocationFamily(AF_Malloc),
+                 /*Invalidate=*/false, /*ReturnsNullOnFailure=*/false, LinePtr);
   if (State)
     C.addTransition(State);
 }
@@ -1793,6 +1796,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
   ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
                                      /*Hold=*/true, IsKnownToBeAllocatedMemory,
                                      AllocationFamily(AF_Malloc),
+                                     /*Invalidate=*/false,
                                      /*ReturnsNullOnFailure=*/true);
 
   C.addTransition(State);
@@ -1986,12 +1990,11 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
   return State;
 }
 
-ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
-                                          const CallEvent &Call,
-                                          ProgramStateRef State, unsigned Num,
-                                          bool Hold, bool &IsKnownToBeAllocated,
-                                          AllocationFamily Family,
-                                          bool ReturnsNullOnFailure) const {
+ProgramStateRef
+MallocChecker::FreeMemAux(CheckerContext &C, const CallEvent &Call,
+                          ProgramStateRef State, unsigned Num, bool Hold,
+                          bool &IsKnownToBeAllocated, AllocationFamily Family,
+                          bool Invalidate, bool ReturnsNullOnFailure) const {
   if (!State)
     return nullptr;
 
@@ -1999,7 +2002,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
     return nullptr;
 
   return FreeMemAux(C, Call.getArgExpr(Num), Call, State, Hold,
-                    IsKnownToBeAllocated, Family, ReturnsNullOnFailure);
+                    IsKnownToBeAllocated, Family, Invalidate,
+                    ReturnsNullOnFailure);
 }
 
 /// Checks if the previous call to free on the given symbol failed - if free
@@ -2134,12 +2138,11 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
   }
 }
 
-ProgramStateRef
-MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
-                          const CallEvent &Call, ProgramStateRef State,
-                          bool Hold, bool &IsKnownToBeAllocated,
-                          AllocationFamily Family, bool ReturnsNullOnFailure,
-                          std::optional<SVal> ArgValOpt) const {
+ProgramStateRef MallocChecker::FreeMemAux(
+    CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
+    ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
+    AllocationFamily Family, bool Invalidate, bool ReturnsNullOnFailure,
+    std::optional<SVal> ArgValOpt) const {
 
   if (!State)
     return nullptr;
@@ -2299,13 +2302,15 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
   // that.
   assert(!RsBase || (RsBase && RsBase->getAllocationFamily() == Family));
 
-  // Assume that after memory is freed, it contains unknown values. This
-  // conforts languages standards, since reading from freed memory is considered
-  // UB and may result in arbitrary value.
-  State = State->invalidateRegions({location}, Call.getOriginExpr(),
-                                   C.blockCount(), C.getLocationContext(),
-                                   /*CausesPointerEscape=*/false,
-                                   /*InvalidatedSymbols=*/nullptr);
+  if (Invalidate) {
+    // Assume that after memory is freed, it contains unknown values. This
+    // conforts languages standards, since reading from freed memory is
+    // considered UB and may result in arbitrary value.
+    State = State->invalidateRegions({location}, Call.getOriginExpr(),
+                                     C.blockCount(), C.getLocationContext(),
+                                     /*CausesPointerEscape=*/false,
+                                     /*InvalidatedSymbols=*/nullptr);
+  }
 
   // Normal free.
   if (Hold)
diff --git a/clang/test/Analysis/NewDelete-intersections.mm b/clang/test/Analysis/NewDelete-intersections.mm
index e897f48b839620..29f4d35d868b2e 100644
--- a/clang/test/Analysis/NewDelete-intersections.mm
+++ b/clang/test/Analysis/NewDelete-intersections.mm
@@ -17,6 +17,7 @@
 
 #include "Inputs/system-header-simulator-cxx.h"
 #include "Inputs/system-header-simulator-objc.h"
+#include "Inputs/system-header-simulator.h"
 
 typedef __typeof__(sizeof(int)) size_t;
 extern "C" void *malloc(size_t);
@@ -86,3 +87,11 @@ void testStandardPlacementNewAfterDelete() {
   delete p;
   p = new (p) int; // newdelete-warning{{Use of memory after it is freed}}
 }
+
+void testGetLine(FILE *f) {
+  char *buffer = (char *)malloc(10);
+  char *old = buffer;
+  size_t n = 0;
+  getline(&buffer, &n, f);
+  int a = *old; // no-warn
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/109337


More information about the cfe-commits mailing list