https://github.com/Flandini updated https://github.com/llvm/llvm-project/pull/125638
>From 0674909f03703a70c3e259acd0590f50cea4615f Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 27 Jan 2025 11:35:03 -0600 Subject: [PATCH 01/27] wip --- .../Checkers/StackAddrEscapeChecker.cpp | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index f4de3b500499c48..59b47371df0d29e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -233,6 +234,43 @@ void StackAddrEscapeChecker::checkReturnedBlockCaptures( } } +class FindEscapingStackAddrsVisitor : public FullSValVisitor<FindEscapingStackAddrsVisitor, bool> +{ + const StackFrameContext *Ctxt; + + SmallVector<const MemRegion *> Escaped; + + bool IsTopFrame; + +public: + explicit FindEscapingStackAddrsVisitor(CheckerContext &CC, bool IsTopFrame) : + Ctxt(CC.getStackFrame()), IsTopFrame(IsTopFrame) {} + + bool CheckForEscapes(SVal V) { return Visit(V); } + + bool VisitSymbolVal(nonloc::SymbolVal SymV) { + return Visit(SymV.getSymbol()); + } + + bool VisitLocAsInteger(nonloc::LocAsInteger LAI) { + return Visit(LAI.getAsRegion()); + } + + bool VisitMemRegionVal(loc::MemRegionVal MRVal) { + return Visit(MRVal.getRegion()); + } + + bool VisitMemRegion(const MemRegion *R) { + const MemSpaceRegion *MS = R->getMemorySpace(); + const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MS); + if (SSR && Ctxt == SSR->getStackFrame()) { + Escaped.push_back(R); + return true; + } + return false; + } +}; + void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker]) @@ -257,7 +295,12 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, return; RetE = RetE->IgnoreParens(); + FindEscapingStackAddrsVisitor EscapeFinder(C, C.getPredecessor()->getLocationContext()->inTopFrame()); + SVal V = C.getSVal(RetE); + + bool AnyEscapes = EscapeFinder.CheckForEscapes(V); + const MemRegion *R = V.getAsRegion(); if (!R) return; @@ -402,7 +445,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, bool checkForDanglingStackVariable(const MemRegion *Referrer, const MemRegion *Referred) { const auto *ReferrerMemSpace = getStackOrGlobalSpaceRegion(Referrer); - const auto *ReferredMemSpace = + const auto *ReferredMemSpace = Referred->getMemorySpace()->getAs<StackSpaceRegion>(); if (!ReferrerMemSpace || !ReferredMemSpace) >From 0a26f83cd5f4a42238ba4fadb3436fec83e4bfcb Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Wed, 29 Jan 2025 00:34:45 -0600 Subject: [PATCH 02/27] wip --- .../Checkers/StackAddrEscapeChecker.cpp | 143 ++++++++++-------- clang/test/Analysis/stack-addr-ps.cpp | 73 ++++++--- clang/test/Analysis/stackaddrleak.cpp | 2 +- 3 files changed, 136 insertions(+), 82 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 59b47371df0d29e..b6ef375936cccc8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -22,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -210,6 +211,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( } } + void StackAddrEscapeChecker::checkReturnedBlockCaptures( const BlockDataRegion &B, CheckerContext &C) const { for (const MemRegion *Region : getCapturedStackRegions(B, C)) { @@ -234,43 +236,6 @@ void StackAddrEscapeChecker::checkReturnedBlockCaptures( } } -class FindEscapingStackAddrsVisitor : public FullSValVisitor<FindEscapingStackAddrsVisitor, bool> -{ - const StackFrameContext *Ctxt; - - SmallVector<const MemRegion *> Escaped; - - bool IsTopFrame; - -public: - explicit FindEscapingStackAddrsVisitor(CheckerContext &CC, bool IsTopFrame) : - Ctxt(CC.getStackFrame()), IsTopFrame(IsTopFrame) {} - - bool CheckForEscapes(SVal V) { return Visit(V); } - - bool VisitSymbolVal(nonloc::SymbolVal SymV) { - return Visit(SymV.getSymbol()); - } - - bool VisitLocAsInteger(nonloc::LocAsInteger LAI) { - return Visit(LAI.getAsRegion()); - } - - bool VisitMemRegionVal(loc::MemRegionVal MRVal) { - return Visit(MRVal.getRegion()); - } - - bool VisitMemRegion(const MemRegion *R) { - const MemSpaceRegion *MS = R->getMemorySpace(); - const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MS); - if (SSR && Ctxt == SSR->getStackFrame()) { - Escaped.push_back(R); - return true; - } - return false; - } -}; - void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker]) @@ -285,6 +250,33 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, } } +class FindStackRegionsSymbolVisitor final : public SymbolVisitor { + const StackFrameContext *StackFrameContext; + SmallVector<const MemRegion *> &EscapingStackRegions; +public: + explicit FindStackRegionsSymbolVisitor(CheckerContext &Ctxt, SmallVector<const MemRegion *> &StorageForStackRegions) + : StackFrameContext(Ctxt.getStackFrame()), EscapingStackRegions(StorageForStackRegions) {} + + bool VisitSymbol(SymbolRef sym) override { return true; } + + bool VisitMemRegion(const MemRegion *MR) override { + llvm::dbgs() << "Visiting region: "; + MR->dumpToStream(llvm::dbgs()); + llvm::dbgs() << '\n'; + + const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace()); + if (SSR && SSR->getStackFrame() == StackFrameContext) + EscapingStackRegions.push_back(MR); + return true; + } +}; + +static void FindEscapingStackRegions(CheckerContext &C, SmallVector<const MemRegion *> &EscapedRegionsStorage, SVal SVal) { + FindStackRegionsSymbolVisitor Finder(C, EscapedRegionsStorage); + ScanReachableSymbols Scanner(C.getState(), Finder); + Scanner.scan(SVal); +} + void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) @@ -295,40 +287,67 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, return; RetE = RetE->IgnoreParens(); - FindEscapingStackAddrsVisitor EscapeFinder(C, C.getPredecessor()->getLocationContext()->inTopFrame()); - SVal V = C.getSVal(RetE); - bool AnyEscapes = EscapeFinder.CheckForEscapes(V); + SmallVector<const MemRegion *> EscapedRegions; + FindEscapingStackRegions(C, EscapedRegions, V); + + for (const MemRegion *ER : EscapedRegions) { + llvm::dbgs() << "Escaped region: "; + ER->dumpToStream(llvm::dbgs()); + llvm::dbgs() << '\n'; + + const MemRegion *R = ER; + while (true) { + switch (R->getKind()) { + case MemRegion::ElementRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::CXXBaseObjectRegionKind: + case MemRegion::CXXDerivedObjectRegionKind: { + const SubRegion *SR = cast<SubRegion>(R); + R = SR->getSuperRegion(); + llvm::dbgs() << "SuperRegion: "; + R->dumpToStream(llvm::dbgs()); + llvm::dbgs() << '\n'; + continue; + } + default: + break; + } + break; + } - const MemRegion *R = V.getAsRegion(); - if (!R) - return; + const VarRegion *Base = dyn_cast<VarRegion>(ER->getBaseRegion()); + if (Base) { + Base->getDecl()->getBeginLoc().dump(C.getSourceManager()); + } - if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) - checkReturnedBlockCaptures(*B, C); + EmitStackError(C, ER, RetE); + } - if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C)) - return; + // if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) + // checkReturnedBlockCaptures(*B, C); // Returning a record by value is fine. (In this case, the returned // expression will be a copy-constructor, possibly wrapped in an // ExprWithCleanups node.) - if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) - RetE = Cleanup->getSubExpr(); - if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) - return; - - // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied - // so the stack address is not escaping here. - if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { - if (isa<BlockDataRegion>(R) && - ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) { - return; - } - } - - EmitStackError(C, R, RetE); + // if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) + // RetE = Cleanup->getSubExpr(); + // if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) + // return; + + // // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied + // // so the stack address is not escaping here. + // if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { + // const MemRegion *R = V.getAsRegion(); + // if (R && isa<BlockDataRegion>(R) && + // ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) { + // return; + // } + // } + + // EmitStackError(C, R, RetE); } static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) { diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index 73e9dbeca460f60..cfa887da025a804 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -410,16 +410,16 @@ void** returned_arr_of_ptr_top() { int* p = &local; void** arr = new void*[2]; arr[1] = p; - return arr; -} // no-warning False Negative + return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}} +} void** returned_arr_of_ptr_callee() { int local = 42; int* p = &local; void** arr = new void*[2]; arr[1] = p; - return arr; -} // no-warning False Negative + return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}} +} void returned_arr_of_ptr_caller() { void** arr = returned_arr_of_ptr_callee(); @@ -466,16 +466,16 @@ void** returned_arr_of_ptr_top(int idx) { int* p = &local; void** arr = new void*[2]; arr[idx] = p; - return arr; -} // no-warning False Negative + return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}} +} void** returned_arr_of_ptr_callee(int idx) { int local = 42; int* p = &local; void** arr = new void*[2]; arr[idx] = p; - return arr; -} // no-warning False Negative + return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}} +} void returned_arr_of_ptr_caller(int idx) { void** arr = returned_arr_of_ptr_callee(idx); @@ -525,14 +525,25 @@ S returned_struct_with_ptr_top() { int local = 42; S s; s.p = &local; - return s; -} // no-warning False Negative, requires traversing returned LazyCompoundVals + return s; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}} +} S returned_struct_with_ptr_callee() { int local = 42; S s; s.p = &local; - return s; // expected-warning{{'local' is still referred to by the caller variable 's'}} + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} + +S leak_through_field_of_returned_object() { + int local = 14; + S s{&local}; + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} + +S leak_through_compound_literal() { + int local = 0; + return (S) { &local }; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} } void returned_struct_with_ptr_caller() { @@ -555,6 +566,30 @@ void static_struct_with_ptr() { } } // namespace leaking_via_struct_with_ptr +namespace leaking_via_nested_structs_with_ptr { +struct Inner { + int *ptr; +}; + +struct Outer { + Inner I; +}; + +struct Deriving : public Outer {}; + +Outer leaks_through_nested_objects() { + int local = 0; + Outer O{&local}; + return O; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} + +Deriving leaks_through_base_objects() { + int local = 0; + Deriving D{&local}; + return D; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} +} // namespace leaking_via_nested_structs_with_ptr + namespace leaking_via_ref_to_struct_with_ptr { struct S { int* p; @@ -613,15 +648,15 @@ S* returned_ptr_to_struct_with_ptr_top() { int local = 42; S* s = new S; s->p = &local; - return s; -} // no-warning False Negative + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} S* returned_ptr_to_struct_with_ptr_callee() { int local = 42; S* s = new S; s->p = &local; - return s; -} // no-warning False Negative + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} void returned_ptr_to_struct_with_ptr_caller() { S* s = returned_ptr_to_struct_with_ptr_callee(); @@ -676,15 +711,15 @@ S* returned_ptr_to_struct_with_ptr_top() { int local = 42; S* s = new S[2]; s[1].p = &local; - return s; -} // no-warning False Negative + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} S* returned_ptr_to_struct_with_ptr_callee() { int local = 42; S* s = new S[2]; s[1].p = &local; - return s; -} // no-warning False Negative + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} +} void returned_ptr_to_struct_with_ptr_caller() { S* s = returned_ptr_to_struct_with_ptr_callee(); diff --git a/clang/test/Analysis/stackaddrleak.cpp b/clang/test/Analysis/stackaddrleak.cpp index 3daffb35a6cd9a6..82697d94a7f60d9 100644 --- a/clang/test/Analysis/stackaddrleak.cpp +++ b/clang/test/Analysis/stackaddrleak.cpp @@ -22,4 +22,4 @@ myfunction create_func() { } void gh_66221() { create_func()(); -} +} \ No newline at end of file >From 3ebf2221ec153c384db450890c7b65996df9d19b Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Wed, 29 Jan 2025 20:49:26 -0600 Subject: [PATCH 03/27] Changes to checkPreStmt --- .../Checkers/StackAddrEscapeChecker.cpp | 102 ++++++++---------- clang/test/Analysis/stackaddrleak.cpp | 2 +- 2 files changed, 44 insertions(+), 60 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index b6ef375936cccc8..ed9fa80a10de6c8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -29,7 +29,8 @@ using namespace ento; namespace { class StackAddrEscapeChecker - : public Checker<check::PreCall, check::PreStmt<ReturnStmt>, + : public Checker<check::PreCall, + check::PreStmt<ReturnStmt>, check::EndFunction> { mutable IdentifierInfo *dispatch_semaphore_tII = nullptr; mutable std::unique_ptr<BugType> BT_stackleak; @@ -260,9 +261,7 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { bool VisitSymbol(SymbolRef sym) override { return true; } bool VisitMemRegion(const MemRegion *MR) override { - llvm::dbgs() << "Visiting region: "; - MR->dumpToStream(llvm::dbgs()); - llvm::dbgs() << '\n'; + llvm::dbgs() << "Visiting region: " << MR << '\n'; const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace()); if (SSR && SSR->getStackFrame() == StackFrameContext) @@ -277,8 +276,44 @@ static void FindEscapingStackRegions(CheckerContext &C, SmallVector<const MemReg Scanner.scan(SVal); } -void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, - CheckerContext &C) const { +static void FilterReturnExpressionLeaks(SmallVector<const MemRegion *> &MaybeEscaped, + CheckerContext &C, + const Expr *RetE, + SVal &RetVal) { + + // Returning a record by value is fine. (In this case, the returned + // expression will be a copy-constructor, possibly wrapped in an + // ExprWithCleanups node.) + if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) + RetE = Cleanup->getSubExpr(); + bool IsConstructExpr = isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType(); + + const MemRegion *RetRegion = RetVal.getAsRegion(); + + // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied + // so the stack address is not escaping here. + bool IsCopyAndAutoreleaseBlockObj = false; + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { + IsCopyAndAutoreleaseBlockObj = RetRegion && + isa<BlockDataRegion>(RetRegion) && + ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject; + } + + // Assuming MR is never nullptr + auto ShouldNotEmitError = [=](const MemRegion *MR) -> bool { + // If this particular MR is one of these special cases, then + // don't emit an error for this MR, but this still allows emitting these + // errors for MRs captured by, e.g., the temporary object. + if (RetRegion == MR) { + return IsConstructExpr || IsCopyAndAutoreleaseBlockObj;; + } + return false; + }; + + std::ignore = std::remove_if(MaybeEscaped.begin(), MaybeEscaped.end(), ShouldNotEmitError); +} + +void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) return; @@ -290,64 +325,13 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, SVal V = C.getSVal(RetE); SmallVector<const MemRegion *> EscapedRegions; + FindEscapingStackRegions(C, EscapedRegions, V); + FilterReturnExpressionLeaks(EscapedRegions, C, RetE, V); for (const MemRegion *ER : EscapedRegions) { - llvm::dbgs() << "Escaped region: "; - ER->dumpToStream(llvm::dbgs()); - llvm::dbgs() << '\n'; - - const MemRegion *R = ER; - while (true) { - switch (R->getKind()) { - case MemRegion::ElementRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::CXXBaseObjectRegionKind: - case MemRegion::CXXDerivedObjectRegionKind: { - const SubRegion *SR = cast<SubRegion>(R); - R = SR->getSuperRegion(); - llvm::dbgs() << "SuperRegion: "; - R->dumpToStream(llvm::dbgs()); - llvm::dbgs() << '\n'; - continue; - } - default: - break; - } - break; - } - - const VarRegion *Base = dyn_cast<VarRegion>(ER->getBaseRegion()); - if (Base) { - Base->getDecl()->getBeginLoc().dump(C.getSourceManager()); - } - EmitStackError(C, ER, RetE); } - - // if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) - // checkReturnedBlockCaptures(*B, C); - - // Returning a record by value is fine. (In this case, the returned - // expression will be a copy-constructor, possibly wrapped in an - // ExprWithCleanups node.) - // if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) - // RetE = Cleanup->getSubExpr(); - // if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) - // return; - - // // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied - // // so the stack address is not escaping here. - // if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { - // const MemRegion *R = V.getAsRegion(); - // if (R && isa<BlockDataRegion>(R) && - // ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) { - // return; - // } - // } - - // EmitStackError(C, R, RetE); } static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) { diff --git a/clang/test/Analysis/stackaddrleak.cpp b/clang/test/Analysis/stackaddrleak.cpp index 82697d94a7f60d9..733a86be1091a0a 100644 --- a/clang/test/Analysis/stackaddrleak.cpp +++ b/clang/test/Analysis/stackaddrleak.cpp @@ -18,7 +18,7 @@ struct myfunction { myfunction create_func() { int n; auto c = [&n] {}; - return c; // expected-warning {{Address of stack memory associated with local variable 'n' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference}} + return c; // expected-warning {{Address of stack memory associated with local variable 'n' returned to caller}} } void gh_66221() { create_func()(); >From 17bf25036cfa2ba9e93b15298c6141cbdd5dea35 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Wed, 29 Jan 2025 20:49:56 -0600 Subject: [PATCH 04/27] start separating checkEndFunction and checkPreStmt --- .../Checkers/StackAddrEscapeChecker.cpp | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index ed9fa80a10de6c8..5234f2b27806eb7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -434,8 +434,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, Node = Node->getFirstPred(); } - // Iterate over all bindings to global variables and see if it contains - // a memory region in the stack space. + // Iterate over all bindings to global variables and stack arguments + // see if they contain a memory region in the current stack frame. class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; @@ -503,20 +503,33 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, SVal Val) override { recordInInvalidatedRegions(Region); - const MemRegion *VR = Val.getAsRegion(); - if (!VR) - return true; - if (checkForDanglingStackVariable(Region, VR)) - return true; + const MemRegion *ReferrerMR = getStackOrGlobalSpaceRegion(Region); - // Check the globals for the same. - if (!isa_and_nonnull<GlobalsSpaceRegion>( - getStackOrGlobalSpaceRegion(Region))) + if (!isa_and_nonnull<GlobalsSpaceRegion, StackArgumentsSpaceRegion>(ReferrerMR)) return true; - if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) - V.emplace_back(Region, VR); + + SmallVector<const MemRegion *> EscapingStackAddrs; + FindEscapingStackRegions(Ctx, EscapingStackAddrs, Val); + + for (const MemRegion *Escapee : EscapingStackAddrs) { + V.emplace_back(Region, Escapee); + } + return true; + // const MemRegion *VR = Val.getAsRegion(); + // if (!VR) + // return true; + + // if (checkForDanglingStackVariable(Region, VR)) + // return true; + + // // Check the globals for the same. + // if (!isa_and_nonnull<GlobalsSpaceRegion>()) + // return true; + // if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) + // V.emplace_back(Region, VR); + // return true; } }; >From 89faf662ea20bfe8d4e24a50671e4ff5dab0b4bb Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Wed, 29 Jan 2025 23:00:43 -0600 Subject: [PATCH 05/27] changes to checkEndFunction --- .../Checkers/StackAddrEscapeChecker.cpp | 66 ++++++++----------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 5234f2b27806eb7..7bc99d681802e61 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -261,7 +261,7 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { bool VisitSymbol(SymbolRef sym) override { return true; } bool VisitMemRegion(const MemRegion *MR) override { - llvm::dbgs() << "Visiting region: " << MR << '\n'; + // llvm::dbgs() << "Visiting region: " << MR << '\n'; const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace()); if (SSR && SSR->getStackFrame() == StackFrameContext) @@ -445,38 +445,39 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, /// Look for stack variables referring to popped stack variables. /// Returns true only if it found some dangling stack variables /// referred by an other stack variable from different stack frame. - bool checkForDanglingStackVariable(const MemRegion *Referrer, - const MemRegion *Referred) { - const auto *ReferrerMemSpace = getStackOrGlobalSpaceRegion(Referrer); - const auto *ReferredMemSpace = - Referred->getMemorySpace()->getAs<StackSpaceRegion>(); + /// Here, ReferrerMS is either a StackSpaceRegion or GlobalSpaceRegion, + /// and Referred has StackSpaceRegion that is the same as PoppedFrame. + bool isDanglingStackVariable(const MemRegion *Referrer, const MemSpaceRegion *ReferrerMemSpace, + const MemRegion *Referred) { + // Case 1: The referrer is a global and Referred is in the current stack context + if (isa<GlobalsSpaceRegion>(ReferrerMemSpace)) + return true; - if (!ReferrerMemSpace || !ReferredMemSpace) + const auto *ReferredMemSpace = Referred->getMemorySpace()->getAs<StackSpaceRegion>(); + if (!ReferredMemSpace) return false; - const auto *ReferrerStackSpace = - ReferrerMemSpace->getAs<StackSpaceRegion>(); - + const auto *ReferrerStackSpace = ReferrerMemSpace->getAs<StackSpaceRegion>(); if (!ReferrerStackSpace) return false; - if (const auto *ReferredFrame = ReferredMemSpace->getStackFrame(); - ReferredFrame != PoppedFrame) { - return false; - } - + // Case 2: The referrer stack space is a parent of the referred stack space, e.g., + // in case of leaking a local in a lambda to the outer scope if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) { - V.emplace_back(Referrer, Referred); return true; } + + // Case 3: The referrer is a memregion of a stack argument, e.g., an out + // argument, this is a top-level function, and referred is this top level + // stack space if (isa<StackArgumentsSpaceRegion>(ReferrerMemSpace) && // Not a simple ptr (int*) but something deeper, e.g. int** isa<SymbolicRegion>(Referrer->getBaseRegion()) && ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) { // Output parameter of a top-level function - V.emplace_back(Referrer, Referred); return true; } + return false; } @@ -500,36 +501,23 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, CallBack(CheckerContext &CC, bool TopFrame) : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {} - bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, + bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, SVal Val) override { - recordInInvalidatedRegions(Region); - - const MemRegion *ReferrerMR = getStackOrGlobalSpaceRegion(Region); + recordInInvalidatedRegions(Referrer); - if (!isa_and_nonnull<GlobalsSpaceRegion, StackArgumentsSpaceRegion>(ReferrerMR)) + const MemSpaceRegion *ReferrerMS = getStackOrGlobalSpaceRegion(Referrer); + if (!ReferrerMS) return true; - SmallVector<const MemRegion *> EscapingStackAddrs; - FindEscapingStackRegions(Ctx, EscapingStackAddrs, Val); + SmallVector<const MemRegion *> PotentialEscapingAddrs; + FindEscapingStackRegions(Ctx, PotentialEscapingAddrs, Val); - for (const MemRegion *Escapee : EscapingStackAddrs) { - V.emplace_back(Region, Escapee); + for (const MemRegion *Escapee : PotentialEscapingAddrs) { + if (isDanglingStackVariable(Referrer, ReferrerMS, Escapee)) + V.emplace_back(Referrer, Escapee); } return true; - // const MemRegion *VR = Val.getAsRegion(); - // if (!VR) - // return true; - - // if (checkForDanglingStackVariable(Region, VR)) - // return true; - - // // Check the globals for the same. - // if (!isa_and_nonnull<GlobalsSpaceRegion>()) - // return true; - // if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) - // V.emplace_back(Region, VR); - // return true; } }; >From f225a2d98683fc078906dcbf9de49f000634409e Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Thu, 30 Jan 2025 00:10:20 -0600 Subject: [PATCH 06/27] wip --- .../Checkers/StackAddrEscapeChecker.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 7bc99d681802e61..439963d744ba98c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -261,8 +261,6 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { bool VisitSymbol(SymbolRef sym) override { return true; } bool VisitMemRegion(const MemRegion *MR) override { - // llvm::dbgs() << "Visiting region: " << MR << '\n'; - const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace()); if (SSR && SSR->getStackFrame() == StackFrameContext) EscapingStackRegions.push_back(MR); @@ -270,7 +268,7 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { } }; -static void FindEscapingStackRegions(CheckerContext &C, SmallVector<const MemRegion *> &EscapedRegionsStorage, SVal SVal) { +static void FindEscapingStackRegions(SmallVector<const MemRegion *> &EscapedRegionsStorage, CheckerContext &C, SVal SVal) { FindStackRegionsSymbolVisitor Finder(C, EscapedRegionsStorage); ScanReachableSymbols Scanner(C.getState(), Finder); Scanner.scan(SVal); @@ -326,7 +324,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SmallVector<const MemRegion *> EscapedRegions; - FindEscapingStackRegions(C, EscapedRegions, V); + FindEscapingStackRegions(EscapedRegions, C, V); FilterReturnExpressionLeaks(EscapedRegions, C, RetE, V); for (const MemRegion *ER : EscapedRegions) { @@ -463,9 +461,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, // Case 2: The referrer stack space is a parent of the referred stack space, e.g., // in case of leaking a local in a lambda to the outer scope - if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) { + if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) return true; - } // Case 3: The referrer is a memregion of a stack argument, e.g., an out // argument, this is a top-level function, and referred is this top level @@ -503,6 +500,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, SVal Val) override { + llvm::dbgs() << "HandleBinding: (" << Referrer << ", " << Val << ")\n"; recordInInvalidatedRegions(Referrer); const MemSpaceRegion *ReferrerMS = getStackOrGlobalSpaceRegion(Referrer); @@ -510,7 +508,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, return true; SmallVector<const MemRegion *> PotentialEscapingAddrs; - FindEscapingStackRegions(Ctx, PotentialEscapingAddrs, Val); + FindEscapingStackRegions(PotentialEscapingAddrs, Ctx, Val); for (const MemRegion *Escapee : PotentialEscapingAddrs) { if (isDanglingStackVariable(Referrer, ReferrerMS, Escapee)) >From 5816e91e1736f234a5f6099f9984b634c77c2621 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Sun, 2 Feb 2025 18:27:08 -0600 Subject: [PATCH 07/27] wip, fixed lambdas test failures --- .../Checkers/StackAddrEscapeChecker.cpp | 135 ++++++++++++------ 1 file changed, 89 insertions(+), 46 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 439963d744ba98c..032caf0a19f5400 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -213,29 +213,29 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( } -void StackAddrEscapeChecker::checkReturnedBlockCaptures( - const BlockDataRegion &B, CheckerContext &C) const { - for (const MemRegion *Region : getCapturedStackRegions(B, C)) { - if (isNotInCurrentFrame(Region, C)) - continue; - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - continue; - if (!BT_capturedstackret) - BT_capturedstackret = std::make_unique<BugType>( - CheckNames[CK_StackAddrEscapeChecker], - "Address of stack-allocated memory is captured"); - SmallString<128> Buf; - llvm::raw_svector_ostream Out(Buf); - SourceRange Range = genName(Out, Region, C.getASTContext()); - Out << " is captured by a returned block"; - auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret, - Out.str(), N); - if (Range.isValid()) - Report->addRange(Range); - C.emitReport(std::move(Report)); - } -} +// void StackAddrEscapeChecker::checkReturnedBlockCaptures( +// const BlockDataRegion &B, CheckerContext &C) const { +// for (const MemRegion *Region : getCapturedStackRegions(B, C)) { +// if (isNotInCurrentFrame(Region, C)) +// continue; +// ExplodedNode *N = C.generateNonFatalErrorNode(); +// if (!N) +// continue; +// if (!BT_capturedstackret) +// BT_capturedstackret = std::make_unique<BugType>( +// CheckNames[CK_StackAddrEscapeChecker], +// "Address of stack-allocated memory is captured"); +// SmallString<128> Buf; +// llvm::raw_svector_ostream Out(Buf); +// SourceRange Range = genName(Out, Region, C.getASTContext()); +// Out << " is captured by a returned block"; +// auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret, +// Out.str(), N); +// if (Range.isValid()) +// Report->addRange(Range); +// C.emitReport(std::move(Report)); +// } +// } void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { @@ -264,21 +264,27 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace()); if (SSR && SSR->getStackFrame() == StackFrameContext) EscapingStackRegions.push_back(MR); - return true; + return true; } }; -static void FindEscapingStackRegions(SmallVector<const MemRegion *> &EscapedRegionsStorage, CheckerContext &C, SVal SVal) { - FindStackRegionsSymbolVisitor Finder(C, EscapedRegionsStorage); - ScanReachableSymbols Scanner(C.getState(), Finder); +static SmallVector<const MemRegion *> FindEscapingStackRegions(CheckerContext &C, SVal SVal) { + SmallVector<const MemRegion *> FoundStackRegions; + + FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions); + ScanReachableSymbols Scanner(C.getState(), Finder); Scanner.scan(SVal); + + return FoundStackRegions; } -static void FilterReturnExpressionLeaks(SmallVector<const MemRegion *> &MaybeEscaped, +static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, CheckerContext &C, const Expr *RetE, SVal &RetVal) { + SmallVector<const MemRegion *> WillEscape; + // Returning a record by value is fine. (In this case, the returned // expression will be a copy-constructor, possibly wrapped in an // ExprWithCleanups node.) @@ -297,18 +303,59 @@ static void FilterReturnExpressionLeaks(SmallVector<const MemRegion *> &MaybeEsc ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject; } - // Assuming MR is never nullptr - auto ShouldNotEmitError = [=](const MemRegion *MR) -> bool { - // If this particular MR is one of these special cases, then - // don't emit an error for this MR, but this still allows emitting these - // errors for MRs captured by, e.g., the temporary object. - if (RetRegion == MR) { - return IsConstructExpr || IsCopyAndAutoreleaseBlockObj;; + for (const MemRegion *MR : MaybeEscaped) { + if (IsCopyAndAutoreleaseBlockObj) { + if (MR == RetRegion) + continue; + + const SubRegion *SR = dyn_cast<SubRegion>(MR); + if (SR && SR->isSubRegionOf(RetRegion)) + continue; } - return false; - }; - std::ignore = std::remove_if(MaybeEscaped.begin(), MaybeEscaped.end(), ShouldNotEmitError); + if (RetRegion == MR && (IsConstructExpr || isa<CXXTempObjectRegion>(MR))) + continue; + + if (isa<CXXTempObjectRegion>(MR->getBaseRegion())) + continue; + + WillEscape.push_back(MR); + } + + return WillEscape; + + // // Assuming MR is never nullptr + // auto ShouldNotEmitError = [=](const MemRegion *MR) -> bool { + // // If we are checking the region that is returned (compared to + // // a region stored somewhere deeper in the return value), then + // // there are some potential false positives with reporting this + // // returned region: + // // 1. If we are returning a temp obj in an inlined call, back to this + // // same stack frame context: + // if (IsCopyAndAutoreleaseBlockObj) { + // if (MR == RetRegion) + // return true; + + // const SubRegion *SR = dyn_cast<SubRegion>(MR); + // if (SR && SR->isSubRegionOf(RetRegion)) + // return true; + // } + + // if (RetRegion == MR) { + // return IsConstructExpr || isa<CXXTempObjectRegion>(MR); + // } + + // if (isa<CXXTempObjectRegion>(MR->getBaseRegion())) + // return true; + + // return false; + // }; + + // const auto NewEndIter = std::remove_if(MaybeEscaped.begin(), MaybeEscaped.end(), ShouldNotEmitError); + + // llvm::dbgs() << "New num escaped regions: " << std::distance(MaybeEscaped.begin(), NewEndIter) << '\n'; + + // return SmallVector<const MemRegion *>() } void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { @@ -322,10 +369,9 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SVal V = C.getSVal(RetE); - SmallVector<const MemRegion *> EscapedRegions; + SmallVector<const MemRegion *> MaybeEscapedRegions = FindEscapingStackRegions(C, V); - FindEscapingStackRegions(EscapedRegions, C, V); - FilterReturnExpressionLeaks(EscapedRegions, C, RetE, V); + SmallVector<const MemRegion *> EscapedRegions = FilterReturnExpressionLeaks(MaybeEscapedRegions, C, RetE, V); for (const MemRegion *ER : EscapedRegions) { EmitStackError(C, ER, RetE); @@ -498,17 +544,14 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, CallBack(CheckerContext &CC, bool TopFrame) : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {} - bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, - SVal Val) override { - llvm::dbgs() << "HandleBinding: (" << Referrer << ", " << Val << ")\n"; + bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, SVal Val) override { recordInInvalidatedRegions(Referrer); const MemSpaceRegion *ReferrerMS = getStackOrGlobalSpaceRegion(Referrer); if (!ReferrerMS) return true; - SmallVector<const MemRegion *> PotentialEscapingAddrs; - FindEscapingStackRegions(PotentialEscapingAddrs, Ctx, Val); + SmallVector<const MemRegion *> PotentialEscapingAddrs = FindEscapingStackRegions(Ctx, Val); for (const MemRegion *Escapee : PotentialEscapingAddrs) { if (isDanglingStackVariable(Referrer, ReferrerMS, Escapee)) >From 27c6cf8a3f9d8ef254ab23f44558d6878edd20e6 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Sun, 2 Feb 2025 18:27:22 -0600 Subject: [PATCH 08/27] formatting --- .../Checkers/StackAddrEscapeChecker.cpp | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 032caf0a19f5400..1e2801faefe0bd3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -212,7 +212,6 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( } } - // void StackAddrEscapeChecker::checkReturnedBlockCaptures( // const BlockDataRegion &B, CheckerContext &C) const { // for (const MemRegion *Region : getCapturedStackRegions(B, C)) { @@ -229,7 +228,8 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( // llvm::raw_svector_ostream Out(Buf); // SourceRange Range = genName(Out, Region, C.getASTContext()); // Out << " is captured by a returned block"; -// auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret, +// auto Report = +// std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret, // Out.str(), N); // if (Range.isValid()) // Report->addRange(Range); @@ -268,20 +268,20 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { } }; -static SmallVector<const MemRegion *> FindEscapingStackRegions(CheckerContext &C, SVal SVal) { +static SmallVector<const MemRegion *> +FindEscapingStackRegions(CheckerContext &C, SVal SVal) { SmallVector<const MemRegion *> FoundStackRegions; FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions); - ScanReachableSymbols Scanner(C.getState(), Finder); + ScanReachableSymbols Scanner(C.getState(), Finder); Scanner.scan(SVal); return FoundStackRegions; } -static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, - CheckerContext &C, - const Expr *RetE, - SVal &RetVal) { +static SmallVector<const MemRegion *> +FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, + CheckerContext &C, const Expr *RetE, SVal &RetVal) { SmallVector<const MemRegion *> WillEscape; @@ -351,9 +351,11 @@ static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(const SmallVec // return false; // }; - // const auto NewEndIter = std::remove_if(MaybeEscaped.begin(), MaybeEscaped.end(), ShouldNotEmitError); + // const auto NewEndIter = std::remove_if(MaybeEscaped.begin(), + // MaybeEscaped.end(), ShouldNotEmitError); - // llvm::dbgs() << "New num escaped regions: " << std::distance(MaybeEscaped.begin(), NewEndIter) << '\n'; + // llvm::dbgs() << "New num escaped regions: " << + // std::distance(MaybeEscaped.begin(), NewEndIter) << '\n'; // return SmallVector<const MemRegion *>() } @@ -369,9 +371,11 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SVal V = C.getSVal(RetE); - SmallVector<const MemRegion *> MaybeEscapedRegions = FindEscapingStackRegions(C, V); + SmallVector<const MemRegion *> MaybeEscapedRegions = + FindEscapingStackRegions(C, V); - SmallVector<const MemRegion *> EscapedRegions = FilterReturnExpressionLeaks(MaybeEscapedRegions, C, RetE, V); + SmallVector<const MemRegion *> EscapedRegions = + FilterReturnExpressionLeaks(MaybeEscapedRegions, C, RetE, V); for (const MemRegion *ER : EscapedRegions) { EmitStackError(C, ER, RetE); @@ -544,14 +548,16 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, CallBack(CheckerContext &CC, bool TopFrame) : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {} - bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, SVal Val) override { + bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, + SVal Val) override { recordInInvalidatedRegions(Referrer); const MemSpaceRegion *ReferrerMS = getStackOrGlobalSpaceRegion(Referrer); if (!ReferrerMS) return true; - SmallVector<const MemRegion *> PotentialEscapingAddrs = FindEscapingStackRegions(Ctx, Val); + SmallVector<const MemRegion *> PotentialEscapingAddrs = + FindEscapingStackRegions(Ctx, Val); for (const MemRegion *Escapee : PotentialEscapingAddrs) { if (isDanglingStackVariable(Referrer, ReferrerMS, Escapee)) >From b73ad74338acf64bcb0a6854b906a6a35024a6f6 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Sun, 2 Feb 2025 18:47:26 -0600 Subject: [PATCH 09/27] some cleanup --- .../Checkers/StackAddrEscapeChecker.cpp | 45 +++---------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 1e2801faefe0bd3..b8879ae5ab526c5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -313,51 +313,16 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, continue; } - if (RetRegion == MR && (IsConstructExpr || isa<CXXTempObjectRegion>(MR))) - continue; + // if (RetRegion == MR && (IsConstructExpr || isa<CXXTempObjectRegion>(MR))) + // continue; - if (isa<CXXTempObjectRegion>(MR->getBaseRegion())) - continue; + // if (isa<CXXTempObjectRegion>(MR->getBaseRegion())) + // continue; WillEscape.push_back(MR); } return WillEscape; - - // // Assuming MR is never nullptr - // auto ShouldNotEmitError = [=](const MemRegion *MR) -> bool { - // // If we are checking the region that is returned (compared to - // // a region stored somewhere deeper in the return value), then - // // there are some potential false positives with reporting this - // // returned region: - // // 1. If we are returning a temp obj in an inlined call, back to this - // // same stack frame context: - // if (IsCopyAndAutoreleaseBlockObj) { - // if (MR == RetRegion) - // return true; - - // const SubRegion *SR = dyn_cast<SubRegion>(MR); - // if (SR && SR->isSubRegionOf(RetRegion)) - // return true; - // } - - // if (RetRegion == MR) { - // return IsConstructExpr || isa<CXXTempObjectRegion>(MR); - // } - - // if (isa<CXXTempObjectRegion>(MR->getBaseRegion())) - // return true; - - // return false; - // }; - - // const auto NewEndIter = std::remove_if(MaybeEscaped.begin(), - // MaybeEscaped.end(), ShouldNotEmitError); - - // llvm::dbgs() << "New num escaped regions: " << - // std::distance(MaybeEscaped.begin(), NewEndIter) << '\n'; - - // return SmallVector<const MemRegion *>() } void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { @@ -595,7 +560,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, // Generate a report for this bug. const StringRef CommonSuffix = - " upon returning to the caller. This will be a dangling reference"; + " upon returning to the caller. This will be a dangling reference"; SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); const SourceRange Range = genName(Out, Referred, Ctx.getASTContext()); >From 13eac11477ccc136f58ddd226c446c011f068884 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Sun, 2 Feb 2025 19:16:11 -0600 Subject: [PATCH 10/27] emit returned by part added --- .../Checkers/StackAddrEscapeChecker.cpp | 119 ++++++++---------- 1 file changed, 54 insertions(+), 65 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index b8879ae5ab526c5..5d31c4410fe3d1c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -57,8 +57,7 @@ class StackAddrEscapeChecker CheckerContext &C) const; void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B, CheckerContext &C) const; - void EmitStackError(CheckerContext &C, const MemRegion *R, - const Expr *RetE) const; + void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion, const Expr *RetE) const; bool isSemaphoreCaptured(const BlockDecl &B) const; static SourceRange genName(raw_ostream &os, const MemRegion *R, ASTContext &Ctx); @@ -150,7 +149,19 @@ StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B, return Regions; } -void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, +static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, + SVal ReturnedVal, + const MemRegion *LeakedRegion) { + if (const MemRegion *ReturnedRegion = ReturnedVal.getAsRegion()) { + if (isa<BlockDataRegion>(ReturnedRegion)) + OS << " is captured by a returned block"; + } else { + // Generic message + OS << " returned to caller"; + } +} + +void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const { ExplodedNode *N = C.generateNonFatalErrorNode(); @@ -160,11 +171,15 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, BT_returnstack = std::make_unique<BugType>( CheckNames[CK_StackAddrEscapeChecker], "Return of address to stack-allocated memory"); + // Generate a report for this bug. SmallString<128> buf; llvm::raw_svector_ostream os(buf); + + // Error message formatting SourceRange range = genName(os, R, C.getASTContext()); - os << " returned to caller"; + EmitReturnedAsPartOfError(os, C.getSVal(RetE), R); + auto report = std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N); report->addRange(RetE->getSourceRange()); @@ -212,31 +227,6 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( } } -// void StackAddrEscapeChecker::checkReturnedBlockCaptures( -// const BlockDataRegion &B, CheckerContext &C) const { -// for (const MemRegion *Region : getCapturedStackRegions(B, C)) { -// if (isNotInCurrentFrame(Region, C)) -// continue; -// ExplodedNode *N = C.generateNonFatalErrorNode(); -// if (!N) -// continue; -// if (!BT_capturedstackret) -// BT_capturedstackret = std::make_unique<BugType>( -// CheckNames[CK_StackAddrEscapeChecker], -// "Address of stack-allocated memory is captured"); -// SmallString<128> Buf; -// llvm::raw_svector_ostream Out(Buf); -// SourceRange Range = genName(Out, Region, C.getASTContext()); -// Out << " is captured by a returned block"; -// auto Report = -// std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret, -// Out.str(), N); -// if (Range.isValid()) -// Report->addRange(Range); -// C.emitReport(std::move(Report)); -// } -// } - void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker]) @@ -342,9 +332,8 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SmallVector<const MemRegion *> EscapedRegions = FilterReturnExpressionLeaks(MaybeEscapedRegions, C, RetE, V); - for (const MemRegion *ER : EscapedRegions) { - EmitStackError(C, ER, RetE); - } + for (const MemRegion *ER : EscapedRegions) + EmitReturnLeakError(C, ER, RetE); } static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) { @@ -447,8 +436,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, Node = Node->getFirstPred(); } - // Iterate over all bindings to global variables and stack arguments - // see if they contain a memory region in the current stack frame. + // Iterate over all bindings to global variables and see if it contains + // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; @@ -458,38 +447,38 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, /// Look for stack variables referring to popped stack variables. /// Returns true only if it found some dangling stack variables /// referred by an other stack variable from different stack frame. - /// Here, ReferrerMS is either a StackSpaceRegion or GlobalSpaceRegion, - /// and Referred has StackSpaceRegion that is the same as PoppedFrame. - bool isDanglingStackVariable(const MemRegion *Referrer, const MemSpaceRegion *ReferrerMemSpace, - const MemRegion *Referred) { - // Case 1: The referrer is a global and Referred is in the current stack context - if (isa<GlobalsSpaceRegion>(ReferrerMemSpace)) - return true; + bool checkForDanglingStackVariable(const MemRegion *Referrer, + const MemRegion *Referred) { + const auto *ReferrerMemSpace = getStackOrGlobalSpaceRegion(Referrer); + const auto *ReferredMemSpace = + Referred->getMemorySpace()->getAs<StackSpaceRegion>(); - const auto *ReferredMemSpace = Referred->getMemorySpace()->getAs<StackSpaceRegion>(); - if (!ReferredMemSpace) + if (!ReferrerMemSpace || !ReferredMemSpace) return false; - const auto *ReferrerStackSpace = ReferrerMemSpace->getAs<StackSpaceRegion>(); + const auto *ReferrerStackSpace = + ReferrerMemSpace->getAs<StackSpaceRegion>(); + if (!ReferrerStackSpace) return false; - // Case 2: The referrer stack space is a parent of the referred stack space, e.g., - // in case of leaking a local in a lambda to the outer scope - if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) - return true; + if (const auto *ReferredFrame = ReferredMemSpace->getStackFrame(); + ReferredFrame != PoppedFrame) { + return false; + } - // Case 3: The referrer is a memregion of a stack argument, e.g., an out - // argument, this is a top-level function, and referred is this top level - // stack space + if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) { + V.emplace_back(Referrer, Referred); + return true; + } if (isa<StackArgumentsSpaceRegion>(ReferrerMemSpace) && // Not a simple ptr (int*) but something deeper, e.g. int** isa<SymbolicRegion>(Referrer->getBaseRegion()) && ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) { // Output parameter of a top-level function + V.emplace_back(Referrer, Referred); return true; } - return false; } @@ -513,22 +502,22 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, CallBack(CheckerContext &CC, bool TopFrame) : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {} - bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Referrer, + bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, SVal Val) override { - recordInInvalidatedRegions(Referrer); - - const MemSpaceRegion *ReferrerMS = getStackOrGlobalSpaceRegion(Referrer); - if (!ReferrerMS) + recordInInvalidatedRegions(Region); + const MemRegion *VR = Val.getAsRegion(); + if (!VR) return true; - SmallVector<const MemRegion *> PotentialEscapingAddrs = - FindEscapingStackRegions(Ctx, Val); - - for (const MemRegion *Escapee : PotentialEscapingAddrs) { - if (isDanglingStackVariable(Referrer, ReferrerMS, Escapee)) - V.emplace_back(Referrer, Escapee); - } + if (checkForDanglingStackVariable(Region, VR)) + return true; + // Check the globals for the same. + if (!isa_and_nonnull<GlobalsSpaceRegion>( + getStackOrGlobalSpaceRegion(Region))) + return true; + if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) + V.emplace_back(Region, VR); return true; } }; @@ -560,7 +549,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, // Generate a report for this bug. const StringRef CommonSuffix = - " upon returning to the caller. This will be a dangling reference"; + " upon returning to the caller. This will be a dangling reference"; SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); const SourceRange Range = genName(Out, Referred, Ctx.getASTContext()); >From 129777e578c5ebd5601e3694cadc4721a4ad5d89 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Sun, 2 Feb 2025 19:49:15 -0600 Subject: [PATCH 11/27] add back IsConstructExpr bool in filtering --- .../Checkers/StackAddrEscapeChecker.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 5d31c4410fe3d1c..6983e5baefa543b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -153,12 +153,14 @@ static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal, const MemRegion *LeakedRegion) { if (const MemRegion *ReturnedRegion = ReturnedVal.getAsRegion()) { - if (isa<BlockDataRegion>(ReturnedRegion)) + if (isa<BlockDataRegion>(ReturnedRegion)) { OS << " is captured by a returned block"; - } else { - // Generic message - OS << " returned to caller"; + return; + } } + + // Generic message + OS << " returned to caller"; } void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C, @@ -294,6 +296,8 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, } for (const MemRegion *MR : MaybeEscaped) { + // In this case, the block_data region being returned isn't a leak, + // and all of its subregions are not leaks if (IsCopyAndAutoreleaseBlockObj) { if (MR == RetRegion) continue; @@ -303,11 +307,8 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, continue; } - // if (RetRegion == MR && (IsConstructExpr || isa<CXXTempObjectRegion>(MR))) - // continue; - - // if (isa<CXXTempObjectRegion>(MR->getBaseRegion())) - // continue; + if (RetRegion == MR && IsConstructExpr) + continue; WillEscape.push_back(MR); } >From adb78d192ed956778b7d0b7f6ddedfbe38dd2a3d Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Sun, 2 Feb 2025 22:33:29 -0600 Subject: [PATCH 12/27] wip --- .../Checkers/StackAddrEscapeChecker.cpp | 21 ++++++++++++++----- clang/test/Analysis/stack-addr-ps.cpp | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 6983e5baefa543b..dab14aa88e97564 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -244,16 +244,27 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, } class FindStackRegionsSymbolVisitor final : public SymbolVisitor { + CheckerContext &Ctxt; const StackFrameContext *StackFrameContext; SmallVector<const MemRegion *> &EscapingStackRegions; public: explicit FindStackRegionsSymbolVisitor(CheckerContext &Ctxt, SmallVector<const MemRegion *> &StorageForStackRegions) - : StackFrameContext(Ctxt.getStackFrame()), EscapingStackRegions(StorageForStackRegions) {} + : Ctxt(Ctxt), StackFrameContext(Ctxt.getStackFrame()), EscapingStackRegions(StorageForStackRegions) {} bool VisitSymbol(SymbolRef sym) override { return true; } bool VisitMemRegion(const MemRegion *MR) override { - const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(MR->getMemorySpace()); + if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) { + for (auto Var : BDR->referenced_vars()) { + SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion()); + const MemRegion *Region = Val.getAsRegion(); + if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) + EscapingStackRegions.push_back(Region); + } + return false; + } + + const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>(); if (SSR && SSR->getStackFrame() == StackFrameContext) EscapingStackRegions.push_back(MR); return true; @@ -302,9 +313,9 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, if (MR == RetRegion) continue; - const SubRegion *SR = dyn_cast<SubRegion>(MR); - if (SR && SR->isSubRegionOf(RetRegion)) - continue; + // const SubRegion *SR = dyn_cast<SubRegion>(MR); + // if (SR && SR->isSubRegionOf(RetRegion)) + // continue; } if (RetRegion == MR && IsConstructExpr) diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index cfa887da025a804..8413757b57a92be 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -251,7 +251,7 @@ void* lambda_to_context_direct_pointer_uncalled() { int local = 42; p = &local; // no-warning: analyzed only as top-level, ignored explicitly by the checker }; - return new MyFunction(&lambda); + return new MyFunction(&lambda); // expected-warning{{Address of stack memory associated with local variable 'lambda' returned to caller}} } void lambda_to_context_direct_pointer_lifetime_extended() { >From 2c629607662325109a6cda773ed562954c5f45b5 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 12:45:06 -0600 Subject: [PATCH 13/27] cleanup --- .../Checkers/StackAddrEscapeChecker.cpp | 56 ++++++++----------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index dab14aa88e97564..cb8dfcad21cbcbf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -258,8 +258,10 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { for (auto Var : BDR->referenced_vars()) { SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion()); const MemRegion *Region = Val.getAsRegion(); - if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) + if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) { EscapingStackRegions.push_back(Region); + VisitMemRegion(Region); + } } return false; } @@ -271,23 +273,14 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { } }; -static SmallVector<const MemRegion *> -FindEscapingStackRegions(CheckerContext &C, SVal SVal) { - SmallVector<const MemRegion *> FoundStackRegions; - - FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions); - ScanReachableSymbols Scanner(C.getState(), Finder); - Scanner.scan(SVal); - - return FoundStackRegions; -} - static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, CheckerContext &C, const Expr *RetE, SVal &RetVal) { SmallVector<const MemRegion *> WillEscape; + const MemRegion *RetRegion = RetVal.getAsRegion(); + // Returning a record by value is fine. (In this case, the returned // expression will be a copy-constructor, possibly wrapped in an // ExprWithCleanups node.) @@ -295,30 +288,17 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, RetE = Cleanup->getSubExpr(); bool IsConstructExpr = isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType(); - const MemRegion *RetRegion = RetVal.getAsRegion(); - // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied // so the stack address is not escaping here. bool IsCopyAndAutoreleaseBlockObj = false; if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { - IsCopyAndAutoreleaseBlockObj = RetRegion && - isa<BlockDataRegion>(RetRegion) && + IsCopyAndAutoreleaseBlockObj = + isa_and_nonnull<BlockDataRegion>(RetRegion) && ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject; } for (const MemRegion *MR : MaybeEscaped) { - // In this case, the block_data region being returned isn't a leak, - // and all of its subregions are not leaks - if (IsCopyAndAutoreleaseBlockObj) { - if (MR == RetRegion) - continue; - - // const SubRegion *SR = dyn_cast<SubRegion>(MR); - // if (SR && SR->isSubRegionOf(RetRegion)) - // continue; - } - - if (RetRegion == MR && IsConstructExpr) + if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr)) continue; WillEscape.push_back(MR); @@ -327,6 +307,17 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, return WillEscape; } +static SmallVector<const MemRegion *> +FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) { + SmallVector<const MemRegion *> FoundStackRegions; + + FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions); + ScanReachableSymbols Scanner(C.getState(), Finder); + Scanner.scan(RetVal); + + return FilterReturnExpressionLeaks(FoundStackRegions, C, RetE, RetVal); +} + void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) return; @@ -338,13 +329,10 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SVal V = C.getSVal(RetE); - SmallVector<const MemRegion *> MaybeEscapedRegions = - FindEscapingStackRegions(C, V); - - SmallVector<const MemRegion *> EscapedRegions = - FilterReturnExpressionLeaks(MaybeEscapedRegions, C, RetE, V); + SmallVector<const MemRegion *> EscapedStackRegions = + FindEscapingStackRegions(C, RetE, V); - for (const MemRegion *ER : EscapedRegions) + for (const MemRegion *ER : EscapedStackRegions) EmitReturnLeakError(C, ER, RetE); } >From e43fb34395db988c9621507915def609f0265122 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 17:17:31 -0600 Subject: [PATCH 14/27] fix objc stack-allocated-capturing block return --- .../Checkers/StackAddrEscapeChecker.cpp | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index cb8dfcad21cbcbf..4d0f5dbc896ab9a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -253,22 +253,27 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { bool VisitSymbol(SymbolRef sym) override { return true; } - bool VisitMemRegion(const MemRegion *MR) override { - if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) { - for (auto Var : BDR->referenced_vars()) { - SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion()); - const MemRegion *Region = Val.getAsRegion(); - if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) { - EscapingStackRegions.push_back(Region); - VisitMemRegion(Region); - } + bool VisitBlockDataRegion(const BlockDataRegion *BDR) { + for (auto Var : BDR->referenced_vars()) { + SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion()); + const MemRegion *Region = Val.getAsRegion(); + if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) { + EscapingStackRegions.push_back(Region); + VisitMemRegion(Region); } - return false; } + return false; + } + + bool VisitMemRegion(const MemRegion *MR) override { const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>(); if (SSR && SSR->getStackFrame() == StackFrameContext) EscapingStackRegions.push_back(MR); + + if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) + return VisitBlockDataRegion(BDR); + return true; } }; @@ -330,7 +335,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SVal V = C.getSVal(RetE); SmallVector<const MemRegion *> EscapedStackRegions = - FindEscapingStackRegions(C, RetE, V); + FindEscapingStackRegions(C, RetE, V); for (const MemRegion *ER : EscapedStackRegions) EmitReturnLeakError(C, ER, RetE); >From 8c39d666caeeb27d7d3f7a425a6b93351d6364af Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 18:12:28 -0600 Subject: [PATCH 15/27] why does this work --- .../StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 4d0f5dbc896ab9a..b64a5b4214d02a3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -253,7 +253,8 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { bool VisitSymbol(SymbolRef sym) override { return true; } - bool VisitBlockDataRegion(const BlockDataRegion *BDR) { + /// Visits the captured region values + bool VisitBlockDataRegionCaptures(const BlockDataRegion *BDR) { for (auto Var : BDR->referenced_vars()) { SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion()); const MemRegion *Region = Val.getAsRegion(); @@ -272,7 +273,7 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { EscapingStackRegions.push_back(MR); if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) - return VisitBlockDataRegion(BDR); + return VisitBlockDataRegionCaptures(BDR); return true; } @@ -306,6 +307,10 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr)) continue; + if (const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>()) + if (SSR->getStackFrame() != C.getStackFrame()) + continue; + WillEscape.push_back(MR); } >From 76493d1262eb53b8751a2b5a658647b504560aa9 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 18:31:59 -0600 Subject: [PATCH 16/27] double up these test expectations --- clang/test/Analysis/stack-addr-ps.cpp | 2 +- clang/test/Analysis/stackaddrleak.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index 8413757b57a92be..392982d92a3f14c 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -532,7 +532,7 @@ S returned_struct_with_ptr_callee() { int local = 42; S s; s.p = &local; - return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} + return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} expected-warning{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 's' upon returning to the caller. This will be a dangling reference}} } S leak_through_field_of_returned_object() { diff --git a/clang/test/Analysis/stackaddrleak.cpp b/clang/test/Analysis/stackaddrleak.cpp index 733a86be1091a0a..a44fb1f7d2dd121 100644 --- a/clang/test/Analysis/stackaddrleak.cpp +++ b/clang/test/Analysis/stackaddrleak.cpp @@ -18,7 +18,7 @@ struct myfunction { myfunction create_func() { int n; auto c = [&n] {}; - return c; // expected-warning {{Address of stack memory associated with local variable 'n' returned to caller}} + return c; // expected-warning {{Address of stack memory associated with local variable 'n' returned to caller}} expected-warning{{Address of stack memory associated with local variable 'n' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference}} } void gh_66221() { create_func()(); >From 82400a79d81291d07d79d7916216f27f922a0217 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 20:24:37 -0600 Subject: [PATCH 17/27] check everytime visitor adds to escaped region --- .../Checkers/StackAddrEscapeChecker.cpp | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index b64a5b4214d02a3..d18e551d6497fc6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -253,30 +253,34 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { bool VisitSymbol(SymbolRef sym) override { return true; } - /// Visits the captured region values + bool VisitMemRegion(const MemRegion *MR) override { + SaveIfEscapes(MR); + + if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) + return VisitBlockDataRegionCaptures(BDR); + + return true; + } + +private: + void SaveIfEscapes(const MemRegion *MR) { + const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>(); + if (SSR && SSR->getStackFrame() == StackFrameContext) + EscapingStackRegions.push_back(MR); + } + bool VisitBlockDataRegionCaptures(const BlockDataRegion *BDR) { for (auto Var : BDR->referenced_vars()) { SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion()); const MemRegion *Region = Val.getAsRegion(); - if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) { - EscapingStackRegions.push_back(Region); + if (Region) { + SaveIfEscapes(Region); VisitMemRegion(Region); } } return false; } - - bool VisitMemRegion(const MemRegion *MR) override { - const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>(); - if (SSR && SSR->getStackFrame() == StackFrameContext) - EscapingStackRegions.push_back(MR); - - if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) - return VisitBlockDataRegionCaptures(BDR); - - return true; - } }; static SmallVector<const MemRegion *> >From 39df282dfa3553aaa4617004353942f00adb009d Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 22:57:45 -0600 Subject: [PATCH 18/27] fix comments --- .../Checkers/StackAddrEscapeChecker.cpp | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index d18e551d6497fc6..919ef932b40c29a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -243,6 +243,13 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, } } +/// A visitor made for use with a ScanReachableSymbols scanner, used +/// for finding stack regions within an SVal that live on the current +/// stack frame of the given checker context. This visitor excludes +/// NonParamVarRegion that data is bound to in a BlockDataRegion's +/// bindings, since these are likely uninteresting, e.g., in case a +/// temporary is constructed on the stack, but it captures values +/// that would leak. class FindStackRegionsSymbolVisitor final : public SymbolVisitor { CheckerContext &Ctxt; const StackFrameContext *StackFrameContext; @@ -283,6 +290,11 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { } }; +/// Given some memory regions that are flagged by FindStackRegionsSymbolVisitor, +/// this function filters out memory regions that are being returned that are +/// likely not true leaks: +/// 1. If returning a block data region that has stack memory space +/// 2. If returning a constructed object that has stack memory space static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, CheckerContext &C, const Expr *RetE, SVal &RetVal) { @@ -311,9 +323,10 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr)) continue; - if (const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>()) - if (SSR->getStackFrame() != C.getStackFrame()) - continue; + // If this is a construct expr of an unelided return value copy, then don't + // warn about returning a region that currently lives on the stack. + if (IsConstructExpr && RetVal.getAs<nonloc::LazyCompoundVal>() && isa<CXXTempObjectRegion>(MR)) + continue; WillEscape.push_back(MR); } @@ -321,6 +334,8 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, return WillEscape; } +/// For use in finding regions that live on the checker context's current +/// stack frame, deep in the SVal representing the return value. static SmallVector<const MemRegion *> FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) { SmallVector<const MemRegion *> FoundStackRegions; >From 65bee7a910a7fa02981fca35b0619ac4c64ca233 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 23:05:13 -0600 Subject: [PATCH 19/27] formatting --- .../Checkers/StackAddrEscapeChecker.cpp | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 919ef932b40c29a..2b1bd97c78743f8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -29,8 +29,7 @@ using namespace ento; namespace { class StackAddrEscapeChecker - : public Checker<check::PreCall, - check::PreStmt<ReturnStmt>, + : public Checker<check::PreCall, check::PreStmt<ReturnStmt>, check::EndFunction> { mutable IdentifierInfo *dispatch_semaphore_tII = nullptr; mutable std::unique_ptr<BugType> BT_stackleak; @@ -57,7 +56,8 @@ class StackAddrEscapeChecker CheckerContext &C) const; void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B, CheckerContext &C) const; - void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion, const Expr *RetE) const; + void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion, + const Expr *RetE) const; bool isSemaphoreCaptured(const BlockDecl &B) const; static SourceRange genName(raw_ostream &os, const MemRegion *R, ASTContext &Ctx); @@ -149,8 +149,7 @@ StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B, return Regions; } -static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, - SVal ReturnedVal, +static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal, const MemRegion *LeakedRegion) { if (const MemRegion *ReturnedRegion = ReturnedVal.getAsRegion()) { if (isa<BlockDataRegion>(ReturnedRegion)) { @@ -164,8 +163,8 @@ static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, } void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C, - const MemRegion *R, - const Expr *RetE) const { + const MemRegion *R, + const Expr *RetE) const { ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) return; @@ -243,26 +242,30 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, } } -/// A visitor made for use with a ScanReachableSymbols scanner, used +/// A visitor made for use with a ScanReachableSymbols scanner, used /// for finding stack regions within an SVal that live on the current /// stack frame of the given checker context. This visitor excludes /// NonParamVarRegion that data is bound to in a BlockDataRegion's /// bindings, since these are likely uninteresting, e.g., in case a /// temporary is constructed on the stack, but it captures values -/// that would leak. +/// that would leak. class FindStackRegionsSymbolVisitor final : public SymbolVisitor { CheckerContext &Ctxt; const StackFrameContext *StackFrameContext; SmallVector<const MemRegion *> &EscapingStackRegions; + public: - explicit FindStackRegionsSymbolVisitor(CheckerContext &Ctxt, SmallVector<const MemRegion *> &StorageForStackRegions) - : Ctxt(Ctxt), StackFrameContext(Ctxt.getStackFrame()), EscapingStackRegions(StorageForStackRegions) {} + explicit FindStackRegionsSymbolVisitor( + CheckerContext &Ctxt, + SmallVector<const MemRegion *> &StorageForStackRegions) + : Ctxt(Ctxt), StackFrameContext(Ctxt.getStackFrame()), + EscapingStackRegions(StorageForStackRegions) {} bool VisitSymbol(SymbolRef sym) override { return true; } bool VisitMemRegion(const MemRegion *MR) override { SaveIfEscapes(MR); - + if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>()) return VisitBlockDataRegionCaptures(BDR); @@ -271,7 +274,8 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor { private: void SaveIfEscapes(const MemRegion *MR) { - const StackSpaceRegion *SSR = MR->getMemorySpace()->getAs<StackSpaceRegion>(); + const StackSpaceRegion *SSR = + MR->getMemorySpace()->getAs<StackSpaceRegion>(); if (SSR && SSR->getStackFrame() == StackFrameContext) EscapingStackRegions.push_back(MR); } @@ -308,15 +312,16 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, // ExprWithCleanups node.) if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) RetE = Cleanup->getSubExpr(); - bool IsConstructExpr = isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType(); + bool IsConstructExpr = + isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType(); // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied // so the stack address is not escaping here. bool IsCopyAndAutoreleaseBlockObj = false; if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { IsCopyAndAutoreleaseBlockObj = - isa_and_nonnull<BlockDataRegion>(RetRegion) && - ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject; + isa_and_nonnull<BlockDataRegion>(RetRegion) && + ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject; } for (const MemRegion *MR : MaybeEscaped) { @@ -325,7 +330,8 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, // If this is a construct expr of an unelided return value copy, then don't // warn about returning a region that currently lives on the stack. - if (IsConstructExpr && RetVal.getAs<nonloc::LazyCompoundVal>() && isa<CXXTempObjectRegion>(MR)) + if (IsConstructExpr && RetVal.getAs<nonloc::LazyCompoundVal>() && + isa<CXXTempObjectRegion>(MR)) continue; WillEscape.push_back(MR); @@ -347,7 +353,8 @@ FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) { return FilterReturnExpressionLeaks(FoundStackRegions, C, RetE, RetVal); } -void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { +void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, + CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) return; @@ -359,7 +366,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext & SVal V = C.getSVal(RetE); SmallVector<const MemRegion *> EscapedStackRegions = - FindEscapingStackRegions(C, RetE, V); + FindEscapingStackRegions(C, RetE, V); for (const MemRegion *ER : EscapedStackRegions) EmitReturnLeakError(C, ER, RetE); >From 47f3b408eff9f92978dfa28c2444bdd141df22f9 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Mon, 3 Feb 2025 23:06:23 -0600 Subject: [PATCH 20/27] remove old includes --- clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 2b1bd97c78743f8..86f0949994cf6b9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -20,9 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; >From 6a2a01f31ef86d7882c92227bb034657171bc7ba Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Tue, 4 Feb 2025 14:17:27 -0600 Subject: [PATCH 21/27] restore whitespace at end of file --- clang/test/Analysis/stackaddrleak.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/stackaddrleak.cpp b/clang/test/Analysis/stackaddrleak.cpp index a44fb1f7d2dd121..df202a2fee77628 100644 --- a/clang/test/Analysis/stackaddrleak.cpp +++ b/clang/test/Analysis/stackaddrleak.cpp @@ -22,4 +22,4 @@ myfunction create_func() { } void gh_66221() { create_func()(); -} \ No newline at end of file +} >From 30ea259896668d199c7e8545f77e5019835843da Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Tue, 4 Feb 2025 14:35:25 -0600 Subject: [PATCH 22/27] add basic int* and int return assign expr tests --- clang/test/Analysis/stack-addr-ps.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index 392982d92a3f14c..cdd6e08f579b6ef 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -90,6 +90,12 @@ int *mf() { return &x; // expected-warning{{Address of stack memory associated with local variable 's1' returned}} expected-warning {{address of stack memory associated with local variable 's1' returned}} } +int *return_assign_expr_leak() { + int x = 1; + int *y; + return y = &x; // expected-warning{{Address of stack memory associated with local variable 'x' returned}} +} + void *lf() { label: void *const &x = &&label; // expected-note {{binding reference variable 'x' here}} @@ -912,3 +918,14 @@ void top_malloc_no_crash_fn() { free(pptr); } } // namespace alloca_region_pointer + +namespace true_negatives_return_expressions { +void return_void() { + return void(); // no-warning +} + +int return_safe_assign_expr() { + int y, x = 14; + return y = x; // no-warning +} +} \ No newline at end of file >From 567c8e6e1fbd7386822b89da2118b3bcb708835a Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Tue, 4 Feb 2025 16:01:55 -0600 Subject: [PATCH 23/27] return comma-separated expression --- clang/test/Analysis/stack-addr-ps.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index cdd6e08f579b6ef..edaec8ec4be48db 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -96,6 +96,12 @@ int *return_assign_expr_leak() { return y = &x; // expected-warning{{Address of stack memory associated with local variable 'x' returned}} } +// Additional diagnostic from -Wreturn-stack-address in the simple case? +int *return_comma_separated_expressions_leak() { + int x = 1; + return (x=14), &x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning{{address of stack memory associated with local variable 'x' returned}} +} + void *lf() { label: void *const &x = &&label; // expected-note {{binding reference variable 'x' here}} @@ -920,12 +926,26 @@ void top_malloc_no_crash_fn() { } // namespace alloca_region_pointer namespace true_negatives_return_expressions { +struct Container { int *x; }; + void return_void() { return void(); // no-warning } -int return_safe_assign_expr() { +int return_assign_expr_safe() { int y, x = 14; return y = x; // no-warning } + +int return_comma_separated_expressions_safe() { + int x = 1; + int *y; + return (y=&x), (x=15); // no-warning +} + +int return_comma_separated_expressions_container_safe() { + int x = 1; + Container Other; + return Other = Container{&x}, x = 14; // no-warning +} } \ No newline at end of file >From 4d4d537297954216ec9d299fcb1572909daffcdf Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Thu, 6 Feb 2025 19:07:36 -0600 Subject: [PATCH 24/27] Add expected warning to copy-elision.cpp no-elide tests --- clang/test/Analysis/copy-elision.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/clang/test/Analysis/copy-elision.cpp b/clang/test/Analysis/copy-elision.cpp index cd941854fc7f196..e69f52f351f1bc0 100644 --- a/clang/test/Analysis/copy-elision.cpp +++ b/clang/test/Analysis/copy-elision.cpp @@ -154,20 +154,26 @@ class ClassWithoutDestructor { void push() { v.push(this); } }; +// Two warnings on no-elide: arg v holds the address of the temporary, and we +// are returning an object which holds v which holds the address of the temporary ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) { - return ClassWithoutDestructor(v); + return ClassWithoutDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}} // no-elide-warning@-1 {{Address of stack memory associated with temporary \ object of type 'ClassWithoutDestructor' is still \ referred to by the caller variable 'v' upon returning to the caller}} } +// Two warnings on no-elide: arg v holds the address of the temporary, and we +// are returning an object which holds v which holds the address of the temporary ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) { - return make1(v); + return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}} // no-elide-warning@-1 {{Address of stack memory associated with temporary \ object of type 'ClassWithoutDestructor' is still \ referred to by the caller variable 'v' upon returning to the caller}} } +// Two warnings on no-elide: arg v holds the address of the temporary, and we +// are returning an object which holds v which holds the address of the temporary ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) { - return make2(v); + return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}} // no-elide-warning@-1 {{Address of stack memory associated with temporary \ object of type 'ClassWithoutDestructor' is still \ referred to by the caller variable 'v' upon returning to the caller}} @@ -298,21 +304,26 @@ to by the caller variable 'v' upon returning to the caller}} #endif } - +// Two warnings on no-elide: arg v holds the address of the temporary, and we +// are returning an object which holds v which holds the address of the temporary ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) { - return ClassWithDestructor(v); + return ClassWithDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}} // no-elide-warning@-1 {{Address of stack memory associated with temporary \ object of type 'ClassWithDestructor' is still referred \ to by the caller variable 'v' upon returning to the caller}} } +// Two warnings on no-elide: arg v holds the address of the temporary, and we +// are returning an object which holds v which holds the address of the temporary ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) { - return make1(v); + return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}} // no-elide-warning@-1 {{Address of stack memory associated with temporary \ object of type 'ClassWithDestructor' is still referred \ to by the caller variable 'v' upon returning to the caller}} } +// Two warnings on no-elide: arg v holds the address of the temporary, and we +// are returning an object which holds v which holds the address of the temporary ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) { - return make2(v); + return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}} // no-elide-warning@-1 {{Address of stack memory associated with temporary \ object of type 'ClassWithDestructor' is still referred \ to by the caller variable 'v' upon returning to the caller}} >From a4ad3cb7371477178418af429a46f2a69bf35409 Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Thu, 6 Feb 2025 19:08:58 -0600 Subject: [PATCH 25/27] Add simple true negative test case from code review --- clang/test/Analysis/stack-addr-ps.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index edaec8ec4be48db..c691b4960207716 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -928,6 +928,11 @@ void top_malloc_no_crash_fn() { namespace true_negatives_return_expressions { struct Container { int *x; }; +int test2() { + int x = 14; + return x; // no-warning +} + void return_void() { return void(); // no-warning } >From 8603e15ae8f664ab99a72fc58a45e89e4ca1218d Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Thu, 6 Feb 2025 19:09:33 -0600 Subject: [PATCH 26/27] Remove temp object check in return expr checking --- .../lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 86f0949994cf6b9..4f8099e1a6f8564 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -326,12 +326,6 @@ FilterReturnExpressionLeaks(const SmallVector<const MemRegion *> &MaybeEscaped, if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr)) continue; - // If this is a construct expr of an unelided return value copy, then don't - // warn about returning a region that currently lives on the stack. - if (IsConstructExpr && RetVal.getAs<nonloc::LazyCompoundVal>() && - isa<CXXTempObjectRegion>(MR)) - continue; - WillEscape.push_back(MR); } >From 7953dba62dfdf960629d9e7b2ea903c4bde81b7a Mon Sep 17 00:00:00 2001 From: Michael Flanders <flanders.micha...@gmail.com> Date: Thu, 6 Feb 2025 19:10:30 -0600 Subject: [PATCH 27/27] Add newline end of file --- clang/test/Analysis/stack-addr-ps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp index c691b4960207716..7e5e6842cf22cbe 100644 --- a/clang/test/Analysis/stack-addr-ps.cpp +++ b/clang/test/Analysis/stack-addr-ps.cpp @@ -953,4 +953,4 @@ int return_comma_separated_expressions_container_safe() { Container Other; return Other = Container{&x}, x = 14; // no-warning } -} \ No newline at end of file +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits