https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/200145
>From 826dbd7525158bf0946b41ec0f629c235b8238b2 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Thu, 28 May 2026 11:37:49 +0200 Subject: [PATCH 1/2] Started implementing the lifetime annotation checker. --- .../clang/StaticAnalyzer/Checkers/Checkers.td | 4 + .../StaticAnalyzer/Checkers/CMakeLists.txt | 1 + .../Checkers/LifetimeAnnotations.cpp | 73 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index eca2afbe340a9..85d59fc139728 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -788,6 +788,10 @@ def SmartPtrChecker: Checker<"SmartPtr">, Dependencies<[SmartPtrModeling]>, Documentation<HasDocumentation>; +def LifetimeAnnotations : Checker<"LifetimeAnnotations">, + HelpText<"Check for lifetime violations using lifetime annotations">, + Documentation<NotDocumented>; + } // end: "alpha.cplusplus" //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 8a0621077b977..3f426186189fa 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 + LifetimeAnnotations.cpp LLVMConventionsChecker.cpp LocalizationChecker.cpp MacOSKeychainAPIChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp new file mode 100644 index 0000000000000..54e98b945c7b3 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp @@ -0,0 +1,73 @@ +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <AllocationState.h> + +using namespace clang; +using namespace ento; + +REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, const MemRegion *, + const MemRegion *); + +class LifetimeAnnotations : public Checker<check::PostCall> { +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; +}; + +void LifetimeAnnotations::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + const auto *MethodDecl = dyn_cast_if_present<CXXMethodDecl>(Call.getDecl()); + + if (!MethodDecl) + return; + + unsigned LBParamIdx = MethodDecl->getNumParams(); + for (unsigned i = 0; i < MethodDecl->getNumParams(); i++) { + if (MethodDecl->getParamDecl(i)->hasAttr<LifetimeBoundAttr>()) { + LBParamIdx = i; + break; + } + } + if (LBParamIdx == MethodDecl->getNumParams()) + return; + + SVal RetVal = Call.getReturnValue(); + const MemRegion *RetValRegion = RetVal.getAsRegion(); + if (!RetValRegion) + return; + + SVal ArgVal = Call.getArgSVal(LBParamIdx); + const MemRegion *ArgValRegion = ArgVal.getAsRegion(); + if (!ArgValRegion) + return; + + State = State->set<LifetimeBoundMap>(RetValRegion, ArgValRegion); + C.addTransition(State); +} + +void LifetimeAnnotations::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + auto LBTy = State->get<LifetimeBoundMap>(); + + if (!LBTy.isEmpty()) { + Out << Sep << "LifetimeBound objects: "; + + for (auto I : LBTy) { + Out << I.first << " bound to " << I.second << NL; + } + } +} + +void ento::registerLifetimeAnnotations(CheckerManager &mgr) { + mgr.registerChecker<LifetimeAnnotations>(); +} + +bool ento::shouldRegisterLifetimeAnnotations(const CheckerManager &mgr) { + return true; +} >From fa2b6c5e8c9de65307f9dfec47019530b005465c Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Sun, 31 May 2026 00:40:48 +0200 Subject: [PATCH 2/2] Addressed mentors feedback. --- .../Checkers/LifetimeAnnotations.cpp | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp index 54e98b945c7b3..4052e13859041 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp @@ -3,12 +3,15 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include <AllocationState.h> +#include "AllocationState.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" + using namespace clang; using namespace ento; +using namespace clang::lifetimes; -REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, const MemRegion *, +REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SymbolRef, const MemRegion *); class LifetimeAnnotations : public Checker<check::PostCall> { @@ -22,45 +25,62 @@ void LifetimeAnnotations::checkPostCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - const auto *MethodDecl = dyn_cast_if_present<CXXMethodDecl>(Call.getDecl()); + const auto *FC = dyn_cast_if_present<AnyFunctionCall>(&Call); + if (!FC) + return; - if (!MethodDecl) + const FunctionDecl *FD = FC->getDecl(); + if (!FD) return; - unsigned LBParamIdx = MethodDecl->getNumParams(); - for (unsigned i = 0; i < MethodDecl->getNumParams(); i++) { - if (MethodDecl->getParamDecl(i)->hasAttr<LifetimeBoundAttr>()) { - LBParamIdx = i; + unsigned LBParamIdx = FD->getNumParams(); + // FIXME: Use range based for loop instead. Currently that would require + // to also change how we create ArgVal which would need a new logic to + // be implemented. + for (unsigned I = 0, E = FD->getNumParams(); I != E; I++) { + if (FD->getParamDecl(I)->hasAttr<LifetimeBoundAttr>()) { + LBParamIdx = I; + // FIXME: If multiple parameters are annotated this logic would + // prevent the analyzer to read after the first parameter. break; } } - if (LBParamIdx == MethodDecl->getNumParams()) - return; - SVal RetVal = Call.getReturnValue(); - const MemRegion *RetValRegion = RetVal.getAsRegion(); - if (!RetValRegion) - return; - SVal ArgVal = Call.getArgSVal(LBParamIdx); - const MemRegion *ArgValRegion = ArgVal.getAsRegion(); - if (!ArgValRegion) + SymbolRef RetValSym = RetVal.getAsSymbol(/*IncludeBaseRegions=*/true); + if(!RetValSym) return; - State = State->set<LifetimeBoundMap>(RetValRegion, ArgValRegion); + if (LBParamIdx != FD->getNumParams()) { + SVal ArgVal = Call.getArgSVal(LBParamIdx); + const MemRegion *ArgValRegion = ArgVal.getAsRegion(); + // FIXME: if(!ArgValRegion) should be also handled since in those cases + // the argument has no region, but still needs to be tracked. + if (ArgValRegion) + State = State->set<LifetimeBoundMap>(RetValSym, ArgValRegion); + } + + if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) { + if (implicitObjectParamIsLifetimeBound(FD)) { + const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion(); + + if (AttrRegion) + State = State->set<LifetimeBoundMap>(RetValSym, AttrRegion); + } + } C.addTransition(State); } void LifetimeAnnotations::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { - auto LBTy = State->get<LifetimeBoundMap>(); + auto LBVal = State->get<LifetimeBoundMap>(); - if (!LBTy.isEmpty()) { - Out << Sep << "LifetimeBound objects: "; + if (LBVal.isEmpty()) + return; - for (auto I : LBTy) { - Out << I.first << " bound to " << I.second << NL; - } + Out << Sep << "LifetimeBound bindings:" << NL; + for (auto&& [RetValSym, ArgValRegion] : LBVal) { + Out << " Origin " << RetValSym << " contains Loan " << ArgValRegion << NL; } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
