find_package: Share package information among copies of package stack

Since commit ae373e93fb (install(PACKAGE_INFO): Add version and location
to package dependencies, 2025-07-31, v4.2.0-rc1~340^2) we add package
information to the current package's stack entry as it is discovered.
However, a nested package can cause the current package's stack entry to
be replaced by a copy with a new ordering index due to commit c6e6861e63
(install(EXPORT): Export find_dependency() calls, 2023-11-07,
v3.29.0-rc1~439^2~1).  Depending on whether imported targets were
created by the current package before finding the nested package, the
`CurrentPackageInfo` pointer is either invalidated, or left pointing at
only one of multiple copies.

Fix this by sharing a single instance of package information with all
copies of the current package's stack entry.  Keep a mutable pointer
to the current package's information only while it is still pending.
This also avoids the need to expose mutation from the package stack.

Add a test that exposes the previously-invalidated pointer to dynamic
analysis tools.

Fixes: #27730
This commit is contained in:
Brad King
2026-04-01 13:47:01 -04:00
parent 5aa649d5f6
commit e935ed22fb
8 changed files with 24 additions and 34 deletions
+2 -1
View File
@@ -298,7 +298,8 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
auto pkgInfo = [](cmTarget* t) -> Package { auto pkgInfo = [](cmTarget* t) -> Package {
cmFindPackageStack pkgStack = t->GetFindPackageStack(); cmFindPackageStack pkgStack = t->GetFindPackageStack();
if (!pkgStack.Empty()) { if (!pkgStack.Empty()) {
return std::make_pair(pkgStack.Top().Name, pkgStack.Top().PackageInfo); return std::make_pair(pkgStack.Top().Name,
*pkgStack.Top().PackageInfo);
} }
cmPackageInformation package; cmPackageInformation package;
+9 -7
View File
@@ -1218,13 +1218,15 @@ bool cmFindPackageCommand::FindPackage(
} }
} }
// Record package information discovered while it is loaded.
this->PackageInfo = std::make_shared<cmPackageInformation>();
// RAII objects to ensure we leave this function with consistent state. // RAII objects to ensure we leave this function with consistent state.
FlushDebugBufferOnExit flushDebugBufferOnExit(*this); FlushDebugBufferOnExit flushDebugBufferOnExit(*this);
PushPopRootPathStack pushPopRootPathStack(*this); PushPopRootPathStack pushPopRootPathStack(*this);
SetRestoreFindDefinitions setRestoreFindDefinitions(*this); SetRestoreFindDefinitions setRestoreFindDefinitions(*this);
cmFindPackageStackRAII findPackageStackRAII(this->Makefile, this->Name); cmFindPackageStackRAII findPackageStackRAII(this->Makefile, this->Name,
this->PackageInfo);
findPackageStackRAII.BindTop(this->CurrentPackageInfo);
// See if we have been told to delegate to FetchContent or some other // See if we have been told to delegate to FetchContent or some other
// redirected config package first. We have to check all names that // redirected config package first. We have to check all names that
@@ -1272,8 +1274,8 @@ bool cmFindPackageCommand::FindPackage(
this->Names.clear(); this->Names.clear();
this->Names.emplace_back(overrideName); // Force finding this one this->Names.emplace_back(overrideName); // Force finding this one
this->Variable = cmStrCat(this->Name, "_DIR"); this->Variable = cmStrCat(this->Name, "_DIR");
this->CurrentPackageInfo->Directory = redirectsDir; this->PackageInfo->Directory = redirectsDir;
this->CurrentPackageInfo->Version = this->VersionFound; this->PackageInfo->Version = this->VersionFound;
this->SetConfigDirCacheVariable(redirectsDir); this->SetConfigDirCacheVariable(redirectsDir);
break; break;
} }
@@ -1734,9 +1736,9 @@ bool cmFindPackageCommand::HandlePackageMode(
} }
if (this->UseConfigFiles && found) { if (this->UseConfigFiles && found) {
this->CurrentPackageInfo->Directory = this->PackageInfo->Directory =
cmSystemTools::GetFilenamePath(this->FileFound); cmSystemTools::GetFilenamePath(this->FileFound);
this->CurrentPackageInfo->Version = this->VersionFound; this->PackageInfo->Version = this->VersionFound;
} }
} }
+1 -1
View File
@@ -285,7 +285,7 @@ private:
std::set<std::string> OptionalComponents; std::set<std::string> OptionalComponents;
std::set<std::string> RequiredTargets; std::set<std::string> RequiredTargets;
std::string DebugBuffer; std::string DebugBuffer;
cmPackageInformation* CurrentPackageInfo; std::shared_ptr<cmPackageInformation> PackageInfo;
enum class SearchResult enum class SearchResult
{ {
+3 -8
View File
@@ -39,7 +39,7 @@ class cmFindPackageCall
{ {
public: public:
std::string const Name; std::string const Name;
cmPackageInformation PackageInfo; std::shared_ptr<cmPackageInformation const> PackageInfo;
unsigned int Index; unsigned int Index;
}; };
@@ -50,19 +50,14 @@ public:
class cmFindPackageStackRAII class cmFindPackageStackRAII
{ {
cmMakefile* Makefile; cmMakefile* Makefile;
cmPackageInformation** Value = nullptr;
public: public:
cmFindPackageStackRAII(cmMakefile* mf, std::string const& pkg); cmFindPackageStackRAII(cmMakefile* mf, std::string const& pkg,
std::shared_ptr<cmPackageInformation const> pkgInfo);
~cmFindPackageStackRAII(); ~cmFindPackageStackRAII();
cmFindPackageStackRAII(cmFindPackageStackRAII const&) = delete; cmFindPackageStackRAII(cmFindPackageStackRAII const&) = delete;
cmFindPackageStackRAII& operator=(cmFindPackageStackRAII const&) = delete; cmFindPackageStackRAII& operator=(cmFindPackageStackRAII const&) = delete;
/** Get a mutable pointer to the top of the stack.
The pointer is invalidated if BindTop is called again or when the
cmFindPackageStackRAII goes out of scope. */
void BindTop(cmPackageInformation*& value);
}; };
/** /**
+4 -17
View File
@@ -51,7 +51,6 @@
#include "cmSourceFile.h" #include "cmSourceFile.h"
#include "cmSourceFileLocation.h" #include "cmSourceFileLocation.h"
#include "cmSourceGroup.h" #include "cmSourceGroup.h"
#include "cmStack.h"
#include "cmState.h" #include "cmState.h"
#include "cmStateDirectory.h" #include "cmStateDirectory.h"
#include "cmStateTypes.h" #include "cmStateTypes.h"
@@ -4242,34 +4241,22 @@ cmMakefile::MacroPushPop::~MacroPushPop()
this->Makefile->PopMacroScope(this->ReportError); this->Makefile->PopMacroScope(this->ReportError);
} }
cmFindPackageStackRAII::cmFindPackageStackRAII(cmMakefile* mf, cmFindPackageStackRAII::cmFindPackageStackRAII(
std::string const& name) cmMakefile* mf, std::string const& name,
std::shared_ptr<cmPackageInformation const> pkgInfo)
: Makefile(mf) : Makefile(mf)
{ {
this->Makefile->FindPackageStack = this->Makefile->FindPackageStack =
this->Makefile->FindPackageStack.Push(cmFindPackageCall{ this->Makefile->FindPackageStack.Push(cmFindPackageCall{
name, name,
cmPackageInformation(), std::move(pkgInfo),
this->Makefile->FindPackageStackNextIndex, this->Makefile->FindPackageStackNextIndex,
}); });
this->Makefile->FindPackageStackNextIndex++; this->Makefile->FindPackageStackNextIndex++;
} }
void cmFindPackageStackRAII::BindTop(cmPackageInformation*& value)
{
if (this->Value) {
*this->Value = nullptr;
}
this->Value = &value;
value = &this->Makefile->FindPackageStack.cmStack::Top().PackageInfo;
}
cmFindPackageStackRAII::~cmFindPackageStackRAII() cmFindPackageStackRAII::~cmFindPackageStackRAII()
{ {
if (this->Value) {
*this->Value = nullptr;
}
this->Makefile->FindPackageStackNextIndex = this->Makefile->FindPackageStackNextIndex =
this->Makefile->FindPackageStack.Top().Index + 1; this->Makefile->FindPackageStack.Top().Index + 1;
this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop();
@@ -0,0 +1,3 @@
cmake_policy(SET CMP0074 NEW)
set(Outer_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/NestedConfig)
find_package(Outer CONFIG)
@@ -0,0 +1 @@
find_package(Inner CONFIG NO_DEFAULT_PATH)
@@ -36,6 +36,7 @@ run_cmake(MissingConfigRequired)
run_cmake(MissingConfigVersion) run_cmake(MissingConfigVersion)
run_cmake(MixedModeOptions) run_cmake(MixedModeOptions)
run_cmake_with_options(ModuleModeDebugPkg --debug-find-pkg=Foo,Zot) run_cmake_with_options(ModuleModeDebugPkg --debug-find-pkg=Foo,Zot)
run_cmake(NestedConfig)
run_cmake(PackageRoot) run_cmake(PackageRoot)
run_cmake(PackageRootNestedConfig) run_cmake(PackageRootNestedConfig)
run_cmake(PackageRootNestedModule) run_cmake(PackageRootNestedModule)