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
