From 2ac0d35159bfce8a75e1ca07d3f6268aa421b8d5 Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Thu, 29 Oct 2026 07:29:42 -0400 Subject: [PATCH] Genex: Diagnose cyclic link information A generator expression such as `$` reachable from the link closure of `tgt` causes a nested re-entry into `GetLinkInformation`. This computation runs over a partially computed link implementation and leads to bogus generation errors or a crash. Reserve the cache entry before computing, and error out when the computation is re-entered. --- Source/cmGeneratorTarget.h | 1 + Source/cmGeneratorTarget_Link.cxx | 19 +++++++++++++++++-- .../RunCMakeTest.cmake | 1 + .../target_link_libraries-cycle3-result.txt | 1 + .../target_link_libraries-cycle3-stderr.txt | 5 +++++ .../target_link_libraries-cycle3.cmake | 18 ++++++++++++++++++ 6 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-result.txt create mode 100644 Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-stderr.txt create mode 100644 Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3.cmake diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index b275632b35..1d061a741d 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -1302,6 +1302,7 @@ private: using cmTargetLinkInformationMap = std::map>; mutable cmTargetLinkInformationMap LinkInformation; + mutable std::set LinkInformationInProgress; void CheckPropertyCompatibility(cmComputeLinkInformation& info, std::string const& config) const; diff --git a/Source/cmGeneratorTarget_Link.cxx b/Source/cmGeneratorTarget_Link.cxx index ee8ef9ebb7..ecda3aebbf 100644 --- a/Source/cmGeneratorTarget_Link.cxx +++ b/Source/cmGeneratorTarget_Link.cxx @@ -347,7 +347,21 @@ cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( // Lookup any existing information for this configuration. std::string key(cmSystemTools::UpperCase(config)); auto i = this->LinkInformation.find(key); - if (i == this->LinkInformation.end()) { + if (i != this->LinkInformation.end()) { + if (cm::contains(this->LinkInformationInProgress, key)) { + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The link dependencies of \"", this->GetName(), + "\" use a generator expression that depends on the link " + "dependencies themselves."), + this->GetBacktrace()); + } + } else { + // Reserve the entry so that a recursive lookup while computing the + // information is diagnosed instead of re-entering the computation. + i = this->LinkInformation.emplace(key, nullptr).first; + this->LinkInformationInProgress.insert(key); + // Compute information for this configuration. auto info = cm::make_unique(this, config); if (info && !info->Compute()) { @@ -355,7 +369,8 @@ cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( } // Store the information for this configuration. - i = this->LinkInformation.emplace(key, std::move(info)).first; + this->LinkInformationInProgress.erase(key); + i->second = std::move(info); if (i->second) { this->CheckPropertyCompatibility(*i->second, config); diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/RunCMakeTest.cmake index a48b525bba..301951cc6d 100644 --- a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/RunCMakeTest.cmake +++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/RunCMakeTest.cmake @@ -5,3 +5,4 @@ run_cmake(static) run_cmake(target_link_libraries) run_cmake(target_link_libraries-cycle1) run_cmake(target_link_libraries-cycle2) +run_cmake(target_link_libraries-cycle3) diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-result.txt b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-stderr.txt b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-stderr.txt new file mode 100644 index 0000000000..b8293444cc --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at target_link_libraries-cycle3\.cmake:[0-9]+ \(add_library\): + The link dependencies of "lib1" use a generator expression that depends on + the link dependencies themselves\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3.cmake new file mode 100644 index 0000000000..3a8f52c1d0 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/target_link_libraries-cycle3.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +# lib1 -> lib2 -> static -> iface -> $ +add_library(lib1 SHARED lib1.c) +add_library(lib2 SHARED lib2.c) +add_library(static STATIC static.c) +add_library(iface INTERFACE) +set_target_properties(iface PROPERTIES + COMPATIBLE_INTERFACE_BOOL FOO + INTERFACE_FOO ON + ) +target_link_libraries(iface INTERFACE $) +target_link_libraries(static PUBLIC iface) +target_link_libraries(lib2 PUBLIC static) +target_link_libraries(lib1 PRIVATE lib2) + +# Evaluate the link interface before the sources of lib1 are computed. +file(GENERATE OUTPUT "out-$.txt" CONTENT "")