https://github.com/benedekaibas updated 
https://github.com/llvm/llvm-project/pull/225489

>From f2d72cf338ff9c26dbffe6a34fc741d7aa05a80f Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 22 Sep 2026 19:57:05 +0200
Subject: [PATCH 1/4] [analyzer] Don't invalidate pointee of pointer-to-const
 on callback arguments

---
 .../StaticAnalyzer/Checkers/MallocChecker.cpp |   2 +-
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp   | 124 ++++++++++--------
 clang/test/Analysis/call-invalidation.cpp     |  25 ++++
 3 files changed, 92 insertions(+), 59 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 140ea90eafc4bd..7db15838bffa96 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3940,7 +3940,7 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
 
     if (const RefState *RS = State->get<RegionState>(sym))
       if (RS->isAllocated() || RS->isAllocatedOfSizeZero())
-        if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS))
+        if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS) || (Call 
&& Call->argumentsMayEscape()))
           State = State->set<RegionState>(sym, RefState::getEscaped(RS));
   }
   return State;
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp 
b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 2338c06d5f9925..0eea0d686be2ea 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -236,6 +236,69 @@ static const MemRegion *getThisRegionBaseOrNull(const 
CallEvent &Call) {
   return nullptr;
 }
 
+// Returns true if the Callee is one of the APIs that can let an
+// argument escape even through a pointer to const paramter.
+static bool escapingAPIs(const CallEvent &Call) {
+  const Decl *D = Call.getDecl();
+  if (!D)
+    return false;
+
+  const auto *FD = dyn_cast<FunctionDecl>(D);
+  if (!FD)
+    return false;
+
+  const IdentifierInfo *II = FD->getIdentifier();
+  if (!II)
+    return false;
+
+  // This set of "escaping" APIs is
+
+  // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
+  //   value into thread local storage. The value can later be retrieved with
+  //   'void *ptheread_getspecific(pthread_key)'. So even thought the
+  //   parameter is 'const void *', the region escapes through the call.
+  if (II->isStr("pthread_setspecific"))
+    return true;
+
+  // - xpc_connection_set_context stores a value which can be retrieved later
+  //   with xpc_connection_get_context.
+  if (II->isStr("xpc_connection_set_context"))
+    return true;
+
+  // - funopen - sets a buffer for future IO calls.
+  if (II->isStr("funopen"))
+    return true;
+
+  // - __cxa_demangle - can reallocate memory and can return the pointer to
+  // the input buffer.
+  if (II->isStr("__cxa_demangle"))
+    return true;
+
+  StringRef FName = II->getName();
+
+  // - CoreFoundation functions that end with "NoCopy" can free a passed-in
+  //   buffer even if it is const.
+  if (FName.ends_with("NoCopy"))
+    return true;
+
+  // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+  //   be deallocated by NSMapRemove.
+  if (FName.starts_with("NS") && FName.contains("Insert"))
+    return true;
+
+  // - Many CF containers allow objects to escape through custom
+  //   allocators/deallocators upon container construction. (PR12101)
+  if (FName.starts_with("CF") || FName.starts_with("CG")) {
+    return FName.contains_insensitive("InsertValue") ||
+           FName.contains_insensitive("AddValue") ||
+           FName.contains_insensitive("SetValue") ||
+           FName.contains_insensitive("WithData") ||
+           FName.contains_insensitive("AppendValue") ||
+           FName.contains_insensitive("SetAttribute");
+  }
+  return false;
+}
+
 ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
                                              ProgramStateRef State) const {
   // Don't invalidate anything if the callee is marked pure/const.
@@ -250,7 +313,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned 
BlockCount,
 
   // Indexes of arguments whose values will be preserved by the call.
   llvm::SmallSet<unsigned, 4> PreserveArgs;
-  if (!argumentsMayEscape())
+  if (!escapingAPIs(*this) && !hasVoidPointerToNonConstArg())
     findPtrToConstParams(PreserveArgs, *this);
 
   // We should not preserve the contents of the region pointed by "this" when
@@ -635,64 +698,9 @@ void AnyFunctionCall::getInitialStackFrameContents(const 
StackFrame *CalleeSF,
 }
 
 bool AnyFunctionCall::argumentsMayEscape() const {
-  if (CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg())
-    return true;
-
-  const FunctionDecl *D = getDecl();
-  if (!D)
-    return true;
-
-  const IdentifierInfo *II = D->getIdentifier();
-  if (!II)
-    return false;
-
-  // This set of "escaping" APIs is
-
-  // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
-  //   value into thread local storage. The value can later be retrieved with
-  //   'void *ptheread_getspecific(pthread_key)'. So even thought the
-  //   parameter is 'const void *', the region escapes through the call.
-  if (II->isStr("pthread_setspecific"))
-    return true;
-
-  // - xpc_connection_set_context stores a value which can be retrieved later
-  //   with xpc_connection_get_context.
-  if (II->isStr("xpc_connection_set_context"))
-    return true;
-
-  // - funopen - sets a buffer for future IO calls.
-  if (II->isStr("funopen"))
-    return true;
-
-  // - __cxa_demangle - can reallocate memory and can return the pointer to
-  // the input buffer.
-  if (II->isStr("__cxa_demangle"))
-    return true;
-
-  StringRef FName = II->getName();
-
-  // - CoreFoundation functions that end with "NoCopy" can free a passed-in
-  //   buffer even if it is const.
-  if (FName.ends_with("NoCopy"))
-    return true;
-
-  // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
-  //   be deallocated by NSMapRemove.
-  if (FName.starts_with("NS") && FName.contains("Insert"))
-    return true;
-
-  // - Many CF containers allow objects to escape through custom
-  //   allocators/deallocators upon container construction. (PR12101)
-  if (FName.starts_with("CF") || FName.starts_with("CG")) {
-    return FName.contains_insensitive("InsertValue") ||
-           FName.contains_insensitive("AddValue") ||
-           FName.contains_insensitive("SetValue") ||
-           FName.contains_insensitive("WithData") ||
-           FName.contains_insensitive("AppendValue") ||
-           FName.contains_insensitive("SetAttribute");
-  }
 
-  return false;
+  return CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg() ||
+         escapingAPIs(*this);
 }
 
 const FunctionDecl *SimpleFunctionCall::getDecl() const {
diff --git a/clang/test/Analysis/call-invalidation.cpp 
b/clang/test/Analysis/call-invalidation.cpp
index 2dad9a5dc64d31..67f362bafe2241 100644
--- a/clang/test/Analysis/call-invalidation.cpp
+++ b/clang/test/Analysis/call-invalidation.cpp
@@ -309,3 +309,28 @@ int aliasing_ptrs_via_mutable_and_const_ptrs() {
   return 100 / (x - 1);
   // expected-warning@-1 {{Division by zero}} FIXME: We shouldn't report this.
 }
+
+void gh222438_opaque(int *p, const int *q);
+void gh222438_opaque_cb(int *p, const int *q, void (*cb)());
+void cb();
+
+void testNoCallbackWithConstArg() {
+  int x = 0, y = 0;
+  gh222438_opaque(&x, &y);
+  clang_analyzer_eval(x == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
+}
+
+void testCallbackWithConstArg() {
+  int x = 1, y = 1;
+  gh222438_opaque_cb(&x, &y, cb);
+  clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
+}
+
+void testNullCallbackWithConstArg() {
+  int x = 1, y = 1;
+  gh222438_opaque_cb(&x, &y, 0);
+  clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}
+}

>From bceecc3fb072cdd5e3850393581228bb4813b20d Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 22 Sep 2026 21:12:42 +0200
Subject: [PATCH 2/4] Commit the formatted changes in MallocChecker.

---
 clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 7db15838bffa96..cfa868e4a2c11d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3940,7 +3940,7 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
 
     if (const RefState *RS = State->get<RegionState>(sym))
       if (RS->isAllocated() || RS->isAllocatedOfSizeZero())
-        if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS) || (Call 
&& Call->argumentsMayEscape()))
+        if (IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS))
           State = State->set<RegionState>(sym, RefState::getEscaped(RS));
   }
   return State;

>From 0dca779d15debe431ec0a66b4a53179b02ef4914 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Tue, 22 Sep 2026 22:27:59 +0200
Subject: [PATCH 3/4] Re-run clang-format again.

---
 clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index cfa868e4a2c11d..3e8298703b407a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3940,7 +3940,8 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
 
     if (const RefState *RS = State->get<RegionState>(sym))
       if (RS->isAllocated() || RS->isAllocatedOfSizeZero())
-        if (IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS))
+        if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS) ||
+            (Call && Call->argumentsMayEscape()))
           State = State->set<RegionState>(sym, RefState::getEscaped(RS));
   }
   return State;

>From ba73186b9c234bd110e062a6a8b8ad03a2fd61c3 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 9 Oct 2026 22:49:01 +0200
Subject: [PATCH 4/4] Inline argumentsMayEscape to MallocChecker.

---
 .../Core/PathSensitive/CallEvent.h            |   2 -
 .../StaticAnalyzer/Checkers/MallocChecker.cpp | 219 +++++++++++-------
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp   |  71 +-----
 3 files changed, 136 insertions(+), 156 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 2010e4b0da84b0..e8e9ef153953c7 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -525,8 +525,6 @@ class AnyFunctionCall : public CallEvent {
 
   RuntimeDefinition getRuntimeDefinition() const override;
 
-  bool argumentsMayEscape() const override;
-
   void getInitialStackFrameContents(const StackFrame *CalleeSF,
                                     BindingsTy &Bindings) const override;
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 3e8298703b407a..74c2304e0769a6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3725,86 +3725,20 @@ ProgramStateRef 
MallocChecker::evalAssume(ProgramStateRef state,
   return state;
 }
 
-bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
-                                              const CallEvent *Call,
-                                              ProgramStateRef State,
-                                              SymbolRef &EscapingSymbol) const 
{
-  assert(Call);
-  EscapingSymbol = nullptr;
-
-  // For now, assume that any C++ or block call can free memory.
-  // TODO: If we want to be more optimistic here, we'll need to make sure that
-  // regions escape to C++ containers. They seem to do that even now, but for
-  // mysterious reasons.
-  if (!isa<SimpleFunctionCall, ObjCMethodCall>(Call))
-    return true;
-
-  // Check Objective-C messages by selector name.
-  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
-    // If it's not a framework call, or if it takes a callback, assume it
-    // can free memory.
-    if (!Call->isInSystemHeader() || Call->argumentsMayEscape())
-      return true;
-
-    // If it's a method we know about, handle it explicitly post-call.
-    // This should happen before the "freeWhenDone" check below.
-    if (isKnownDeallocObjCMethodName(*Msg))
-      return false;
-
-    // If there's a "freeWhenDone" parameter, but the method isn't one we know
-    // about, we can't be sure that the object will use free() to deallocate 
the
-    // memory, so we can't model it explicitly. The best we can do is use it to
-    // decide whether the pointer escapes.
-    if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
-      return *FreeWhenDone;
-
-    // If the first selector piece ends with "NoCopy", and there is no
-    // "freeWhenDone" parameter set to zero, we know ownership is being
-    // transferred. Again, though, we can't be sure that the object will use
-    // free() to deallocate the memory, so we can't model it explicitly.
-    StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);
-    if (FirstSlot.ends_with("NoCopy"))
-      return true;
-
-    // If the first selector starts with addPointer, insertPointer,
-    // or replacePointer, assume we are dealing with NSPointerArray or similar.
-    // This is similar to C++ containers (vector); we still might want to check
-    // that the pointers get freed by following the container itself.
-    if (FirstSlot.starts_with("addPointer") ||
-        FirstSlot.starts_with("insertPointer") ||
-        FirstSlot.starts_with("replacePointer") ||
-        FirstSlot == "valueWithPointer") {
-      return true;
-    }
-
-    // We should escape receiver on call to 'init'. This is especially relevant
-    // to the receiver, as the corresponding symbol is usually not referenced
-    // after the call.
-    if (Msg->getMethodFamily() == OMF_init) {
-      EscapingSymbol = Msg->getReceiverSVal().getAsSymbol();
-      return true;
-    }
-
-    // Otherwise, assume that the method does not free memory.
-    // Most framework methods do not free memory.
+// Returns true if an argument may escape through this call: this
+// covers callees that take ownership of a buffer and may free it
+// later and callees that let an argument escape even through a
+// pointer to a const parameter. Also returns true for a non-null
+// callback argument or a void pointer to non-const argument.
+static bool mayFreeOrLetArgumentsEscape(const CallEvent *Call) {
+  const Decl *D = Call->getDecl();
+  if (!D)
     return false;
-  }
 
-  // At this point the only thing left to handle is straight function calls.
-  const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl();
+  const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
   if (!FD)
-    return true;
-
-  // If it's one of the allocation functions we can reason about, we model
-  // its behavior explicitly.
-  if (isMemCall(*Call))
     return false;
 
-  // If it's not a system call, assume it frees memory.
-  if (!Call->isInSystemHeader())
-    return true;
-
-  // White list the system functions whose arguments escape.
   const IdentifierInfo *II = FD->getIdentifier();
   if (!II)
     return true;
@@ -3831,15 +3765,17 @@ bool 
MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
   // 'closefn' is specified (and if that function does free memory),
   // but it will not if closefn is not specified.
   // Currently, we do not inspect the 'closefn' function (PR12101).
-  if (FName == "funopen")
+  if (FName == "funopen") {
     if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))
       return false;
+    return true;
+  }
 
   // Do not warn on pointers passed to 'setbuf' when used with std streams,
   // these leaks might be intentional when setting the buffer for stdio.
   // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
-  if (FName == "setbuf" || FName =="setbuffer" ||
-      FName == "setlinebuf" || FName == "setvbuf") {
+  if (FName == "setbuf" || FName == "setbuffer" || FName == "setlinebuf" ||
+      FName == "setvbuf") {
     if (Call->getNumArgs() >= 1) {
       const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
       if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
@@ -3886,11 +3822,126 @@ bool 
MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
     return true;
   }
 
+  // 'int pthread_setspecific(ptheread_key k, const void *)' stores a
+  // value into thread local storage. The value can later be retrieved with
+  // 'void *ptheread_getspecific(pthread_key)'. So even thought the
+  // parameter is 'const void *', the region escapes through the call.
+  if (II->isStr("pthread_setspecific"))
+    return true;
+
+  // xpc_connection_set_context stores a value which can be retrieved later
+  // with xpc_connection_get_context.
+  if (II->isStr("xpc_connection_set_context"))
+    return true;
+
+  // __cxa_demangle can reallocate memory and can return the pointer to
+  // the input buffer.
+  if (II->isStr("__cxa_demangle"))
+    return true;
+
+  // NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+  // be deallocated by NSMapRemove.
+  if (FName.starts_with("NS") && FName.contains("Insert"))
+    return true;
+
+  // Many CF containers allow objects to escape through custom
+  // allocators/deallocators upon container construction. (PR12101)
+  if (FName.starts_with("CF") || FName.starts_with("CG")) {
+    return FName.contains_insensitive("InsertValue") ||
+           FName.contains_insensitive("AddValue") ||
+           FName.contains_insensitive("SetValue") ||
+           FName.contains_insensitive("WithData") ||
+           FName.contains_insensitive("AppendValue") ||
+           FName.contains_insensitive("SetAttribute");
+  }
+
+  if (Call->argumentsMayEscape() || Call->hasVoidPointerToNonConstArg())
+    return true;
+  return false;
+}
+
+static bool mayEscapeThroughKnownObjCAPI(const CallEvent *Call,
+                                         SymbolRef &EscapingSymbol) {
+  // Check Objective-C messages by selector name.
+  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
+    EscapingSymbol = nullptr;
+    // If it takes a callback, assume it can free memory.
+    if (Call->argumentsMayEscape())
+      return true;
+
+    // If it's a method we know about, handle it explicitly post-call.
+    // This should happen before the "freeWhenDone" check below.
+    if (isKnownDeallocObjCMethodName(*Msg))
+      return false;
+
+    // If there's a "freeWhenDone" parameter, but the method isn't one we know
+    // about, we can't be sure that the object will use free() to deallocate 
the
+    // memory, so we can't model it explicitly. The best we can do is use it to
+    // decide whether the pointer escapes.
+    if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
+      return *FreeWhenDone;
+
+    // If the first selector piece ends with "NoCopy", and there is no
+    // "freeWhenDone" parameter set to zero, we know ownership is being
+    // transferred. Again, though, we can't be sure that the object will use
+    // free() to deallocate the memory, so we can't model it explicitly.
+    StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);
+    if (FirstSlot.ends_with("NoCopy"))
+      return true;
+
+    // If the first selector starts with addPointer, insertPointer,
+    // or replacePointer, assume we are dealing with NSPointerArray or similar.
+    // This is similar to C++ containers (vector); we still might want to check
+    // that the pointers get freed by following the container itself.
+    if (FirstSlot.starts_with("addPointer") ||
+        FirstSlot.starts_with("insertPointer") ||
+        FirstSlot.starts_with("replacePointer") ||
+        FirstSlot == "valueWithPointer") {
+      return true;
+    }
+
+    // We should escape receiver on call to 'init'. This is especially relevant
+    // to the receiver, as the corresponding symbol is usually not referenced
+    // after the call.
+    if (Msg->getMethodFamily() == OMF_init) {
+      EscapingSymbol = Msg->getReceiverSVal().getAsSymbol();
+      return true;
+    }
+    // Otherwise, assume that the method does not free memory.
+    // Most framework methods do not free memory.
+    return false;
+  }
+  return false;
+}
+
+bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
+    const CallEvent *Call, ProgramStateRef State,
+    SymbolRef &EscapingSymbol) const {
+  assert(Call);
+  EscapingSymbol = nullptr;
+
+  // For now, assume that any C++ or block call can free memory.
+  // TODO: If we want to be more optimistic here, we'll need to make sure that
+  // regions escape to C++ containers. They seem to do that even now, but for
+  // mysterious reasons.
+  if (!isa<SimpleFunctionCall, ObjCMethodCall>(Call))
+    return true;
+
+  // If it's one of the allocation functions we can reason about, we model
+  // its behavior explicitly.
+  if (isMemCall(*Call))
+    return false;
+
+  // If it's not a system call, assume it frees memory.
+  if (!Call->isInSystemHeader())
+    return true;
+
   // Handle cases where we know a buffer's /address/ can escape.
   // Note that the above checks handle some special cases where we know that
   // even though the address escapes, it's still our responsibility to free the
   // buffer.
-  if (Call->argumentsMayEscape())
+  if (mayFreeOrLetArgumentsEscape(Call) ||
+      mayEscapeThroughKnownObjCAPI(Call, EscapingSymbol))
     return true;
 
   // Otherwise, assume that the function does not free memory.
@@ -3898,10 +3949,9 @@ bool 
MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
   return false;
 }
 
-ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,
-                                             const InvalidatedSymbols &Escaped,
-                                             const CallEvent *Call,
-                                             PointerEscapeKind Kind) const {
+ProgramStateRef MallocChecker::checkPointerEscape(
+    ProgramStateRef State, const InvalidatedSymbols &Escaped,
+    const CallEvent *Call, PointerEscapeKind Kind) const {
   return checkPointerEscapeAux(State, Escaped, Call, Kind,
                                /*IsConstPointerEscape*/ false);
 }
@@ -3941,7 +3991,8 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
     if (const RefState *RS = State->get<RegionState>(sym))
       if (RS->isAllocated() || RS->isAllocatedOfSizeZero())
         if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS) ||
-            (Call && Call->argumentsMayEscape()))
+            (Call && mayFreeOrLetArgumentsEscape(Call)) ||
+            (Call && mayEscapeThroughKnownObjCAPI(Call, EscapingSymbol)))
           State = State->set<RegionState>(sym, RefState::getEscaped(RS));
   }
   return State;
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp 
b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 0eea0d686be2ea..5ac77b070b508f 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -236,69 +236,6 @@ static const MemRegion *getThisRegionBaseOrNull(const 
CallEvent &Call) {
   return nullptr;
 }
 
-// Returns true if the Callee is one of the APIs that can let an
-// argument escape even through a pointer to const paramter.
-static bool escapingAPIs(const CallEvent &Call) {
-  const Decl *D = Call.getDecl();
-  if (!D)
-    return false;
-
-  const auto *FD = dyn_cast<FunctionDecl>(D);
-  if (!FD)
-    return false;
-
-  const IdentifierInfo *II = FD->getIdentifier();
-  if (!II)
-    return false;
-
-  // This set of "escaping" APIs is
-
-  // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
-  //   value into thread local storage. The value can later be retrieved with
-  //   'void *ptheread_getspecific(pthread_key)'. So even thought the
-  //   parameter is 'const void *', the region escapes through the call.
-  if (II->isStr("pthread_setspecific"))
-    return true;
-
-  // - xpc_connection_set_context stores a value which can be retrieved later
-  //   with xpc_connection_get_context.
-  if (II->isStr("xpc_connection_set_context"))
-    return true;
-
-  // - funopen - sets a buffer for future IO calls.
-  if (II->isStr("funopen"))
-    return true;
-
-  // - __cxa_demangle - can reallocate memory and can return the pointer to
-  // the input buffer.
-  if (II->isStr("__cxa_demangle"))
-    return true;
-
-  StringRef FName = II->getName();
-
-  // - CoreFoundation functions that end with "NoCopy" can free a passed-in
-  //   buffer even if it is const.
-  if (FName.ends_with("NoCopy"))
-    return true;
-
-  // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
-  //   be deallocated by NSMapRemove.
-  if (FName.starts_with("NS") && FName.contains("Insert"))
-    return true;
-
-  // - Many CF containers allow objects to escape through custom
-  //   allocators/deallocators upon container construction. (PR12101)
-  if (FName.starts_with("CF") || FName.starts_with("CG")) {
-    return FName.contains_insensitive("InsertValue") ||
-           FName.contains_insensitive("AddValue") ||
-           FName.contains_insensitive("SetValue") ||
-           FName.contains_insensitive("WithData") ||
-           FName.contains_insensitive("AppendValue") ||
-           FName.contains_insensitive("SetAttribute");
-  }
-  return false;
-}
-
 ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
                                              ProgramStateRef State) const {
   // Don't invalidate anything if the callee is marked pure/const.
@@ -313,7 +250,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned 
BlockCount,
 
   // Indexes of arguments whose values will be preserved by the call.
   llvm::SmallSet<unsigned, 4> PreserveArgs;
-  if (!escapingAPIs(*this) && !hasVoidPointerToNonConstArg())
+  if (!hasVoidPointerToNonConstArg())
     findPtrToConstParams(PreserveArgs, *this);
 
   // We should not preserve the contents of the region pointed by "this" when
@@ -697,12 +634,6 @@ void AnyFunctionCall::getInitialStackFrameContents(const 
StackFrame *CalleeSF,
   addParameterValuesToBindings(CalleeSF, Bindings, SVB, *this, 
D->parameters());
 }
 
-bool AnyFunctionCall::argumentsMayEscape() const {
-
-  return CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg() ||
-         escapingAPIs(*this);
-}
-
 const FunctionDecl *SimpleFunctionCall::getDecl() const {
   const FunctionDecl *D = getOriginExpr()->getDirectCallee();
   if (D)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to