https://github.com/benedekaibas created https://github.com/llvm/llvm-project/pull/206460
None >From d1c7eb6cfd432345ee492fd3d25d14a02305d4e1 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 10:19:38 +0200 Subject: [PATCH 1/4] Start implementing the reportDanglingPtrDeref checker. --- .../include/clang/StaticAnalyzer/Checkers/Checkers.td | 11 +++++++++++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt | 1 + 2 files changed, 12 insertions(+) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index b565481d28fdb..631a273bb4f18 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -788,9 +788,20 @@ def SmartPtrChecker: Checker<"SmartPtr">, Dependencies<[SmartPtrModeling]>, Documentation<HasDocumentation>; +def LifetimeModeling : Checker<"LifetimeModeling">, + HelpText<"Model [[clang::lifetimebound]] annotations for lifetime analysis">, + Documentation<NotDocumented>, + Hidden; + def UseAfterLifetimeEnd : Checker<"UseAfterLifetimeEnd">, HelpText<"Check for uses of references or pointers that " "outlive their bound object">, + Dependencies<[LifetimeModeling]>, + Documentation<NotDocumented>; + +def ReportDanglingPtrDeref : Checker<"ReportDanglingPtrDeref">, + HelpText<"Check for dereferences of a dangling pointer">, + Dependencies<[LifetimeModeling]>, Documentation<NotDocumented>; } // end: "alpha.cplusplus" diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 46c0c36fda736..b05a51c46485c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -92,6 +92,7 @@ add_clang_library(clangStaticAnalyzerCheckers PointerSubChecker.cpp PthreadLockChecker.cpp PutenvStackArrayChecker.cpp + ReportDanglingPtrDeref.cpp RetainCountChecker/RetainCountChecker.cpp RetainCountChecker/RetainCountDiagnostics.cpp ReturnPointerRangeChecker.cpp >From 5e7f0fb5a14052b220fe75106aba33eb5f64bd24 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Fri, 26 Jun 2026 01:09:44 +0200 Subject: [PATCH 2/4] Implemented the Modeling checker. --- .../Checkers/LifetimeModeling.h | 18 ++++ .../StaticAnalyzer/Checkers/CMakeLists.txt | 1 + .../Checkers/LifetimeModeling.cpp | 101 ++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h create mode 100644 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp diff --git a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h new file mode 100644 index 0000000000000..671d5ce1ec5da --- /dev/null +++ b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h @@ -0,0 +1,18 @@ +#ifndef LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H +#define LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <vector> + +namespace clang { +namespace ento { +namespace lifetimemodeling { + +std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef, SVal); + +} // namespace lifetimemodeling +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index b05a51c46485c..4ec6e368c8fc3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -55,6 +55,7 @@ add_clang_library(clangStaticAnalyzerCheckers IteratorModeling.cpp IteratorRangeChecker.cpp IvarInvalidationChecker.cpp + LifetimeModeling.cpp LLVMConventionsChecker.cpp LocalizationChecker.cpp MacOSKeychainAPIChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp new file mode 100644 index 0000000000000..65aa071a3284a --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -0,0 +1,101 @@ +#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h" +#include "clang/AST/Attr.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; +using namespace lifetimemodeling; + +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *) +REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet) + +REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *) + +namespace { + +class LifetimeModeling : public Checker<check::PostCall, check::LifetimeEnd> { +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkLifetimeEnd(const VarDecl *VD, CheckerContext &C) const; +}; + +} // namespace + +std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, + SVal Val) { + std::vector<const MemRegion *> StoreRegion; + if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { + for (const MemRegion *Region : *SourceSet) + StoreRegion.push_back(Region); + return StoreRegion; + } + return StoreRegion; +} + +static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, + const MemRegion *Source) { + LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>(); + const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal); + + LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet(); + Set = F.add(Set, Source); + State = State->set<LifetimeBoundMap>(RetVal, Set); + return State; +} + +void LifetimeModeling::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + const auto *FC = dyn_cast<AnyFunctionCall>(&Call); + if (!FC) + return; + + const FunctionDecl *FD = FC->getDecl(); + if (!FD) + return; + + SVal RetVal = Call.getReturnValue(); + + for (const ParmVarDecl *PVD : FD->parameters()) { + if (PVD->hasAttr<LifetimeBoundAttr>()) { + unsigned Idx = PVD->getFunctionScopeIndex(); + SVal Arg = Call.getArgSVal(Idx); + if (const MemRegion *ArgValRegion = Arg.getAsRegion()) + State = bindValues(State, RetVal, ArgValRegion); + } + } + + if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) { + if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) { + if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) + State = bindValues(State, RetVal, AttrRegion); + } + } + C.addTransition(State); +} + +void LifetimeModeling::checkLifetimeEnd(const VarDecl *VD, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!VD) + return; + + SVal SourceVal = State->getLValue(VD, C.getStackFrame()); + if (const MemRegion *SourceValRegion = SourceVal.getAsRegion()) { + State = State->add<DeallocatedSourceSet>(SourceValRegion); + C.addTransition(State); + } +} + +void ento::registerLifetimeModeling(CheckerManager &Mgr) { + Mgr.registerChecker<LifetimeModeling>(); +} + +bool ento::shouldRegisterLifetimeModeling(const CheckerManager &Mgr) { + return true; +} >From 187fcbf113977244fb14ec79ccc81c9893f44073 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 10:54:21 +0200 Subject: [PATCH 3/4] Add isDeallocated API to the modeling checker. --- .../include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h | 1 + clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h index 671d5ce1ec5da..e9eb4979e05d2 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h +++ b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h @@ -10,6 +10,7 @@ namespace ento { namespace lifetimemodeling { std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef, SVal); +bool isDeallocated(ProgramStateRef, const MemRegion *); } // namespace lifetimemodeling } // namespace ento diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index 65aa071a3284a..1355c4218f921 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -31,11 +31,14 @@ std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { for (const MemRegion *Region : *SourceSet) StoreRegion.push_back(Region); - return StoreRegion; } return StoreRegion; } +bool isDeallocated(ProgramStateRef State, const MemRegion *Region) { + return State->contains<DeallocatedSourceSet>(Region); +} + static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, const MemRegion *Source) { LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>(); >From 98aaa83b3029da553dfe751488597e97367c17ea Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 12:57:19 +0200 Subject: [PATCH 4/4] Added ReportDanglingPtrDeref checker. --- .../Checkers/LifetimeModeling.cpp | 10 +++- .../Checkers/ReportDanglingPtrDeref.cpp | 46 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index 1355c4218f921..ea9949d9696ba 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -8,7 +8,6 @@ using namespace clang; using namespace ento; -using namespace lifetimemodeling; REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *) REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet) @@ -24,9 +23,12 @@ class LifetimeModeling : public Checker<check::PostCall, check::LifetimeEnd> { }; } // namespace - +namespace clang { +namespace ento { +namespace lifetimemodeling { std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, SVal Val) { + std::vector<const MemRegion *> StoreRegion; if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { for (const MemRegion *Region : *SourceSet) @@ -39,6 +41,10 @@ bool isDeallocated(ProgramStateRef State, const MemRegion *Region) { return State->contains<DeallocatedSourceSet>(Region); } +} // namespace lifetimemodeling +} // namespace ento +} // namespace clang + static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, const MemRegion *Source) { LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>(); diff --git a/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp new file mode 100644 index 0000000000000..24620d369b80d --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp @@ -0,0 +1,46 @@ +#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReportDanglingPtrDeref : public Checker<check::Location> { +public: + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const; + void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const; + const BugType BugMsg{this, "ReportDanglingPtrDeref", "LifetimeBound"}; +}; +} // namespace + +void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (const MemRegion *LocRegion = Loc.getAsRegion()) { + if (lifetimemodeling::isDeallocated(State, LocRegion)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) + reportUseAfterScope(LocRegion, N, C); + } + } +} + +void ReportDanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const { + auto BR = std::make_unique<PathSensitiveBugReport>( + BugMsg, + (llvm::Twine("Use of '") + Region->getString() + + "' after its lifetime ended.") + .str(), + N); + C.emitReport(std::move(BR)); +} + +void ento::registerReportDanglingPtrDeref(CheckerManager &Mgr) { + Mgr.registerChecker<ReportDanglingPtrDeref>(); +} + +bool ento::shouldRegisterReportDanglingPtrDeref(const CheckerManager &Mgr) { + return true; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
