Commit 40628b25 authored by Brad King's avatar Brad King Committed by Kitware Robot
Browse files

Merge topic 'max-recursion-depth'

a6982cff

 cmMakefile: Impose maximum recursion limit
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Acked-by: Juraj Oršulić's avatarJuraj Oršulić <juraj.orsulic@fer.hr>
Merge-request: !2746
parents 24b6e483 a6982cff
......@@ -190,6 +190,7 @@ Variables that Change Behavior
/variable/CMAKE_LIBRARY_PATH
/variable/CMAKE_LINK_DIRECTORIES_BEFORE
/variable/CMAKE_MFC_FLAG
/variable/CMAKE_MAXIMUM_RECURSION_DEPTH
/variable/CMAKE_MODULE_PATH
/variable/CMAKE_NOT_USING_CONFIG_FLAGS
/variable/CMAKE_POLICY_DEFAULT_CMPNNNN
......
max-recursion-depth
-------------------
* CMake now imposes a maximum recursion limit to prevent a stack overflow on
scripts that recurse infinitely. The limit can be adjusted at runtime with
:variable:`CMAKE_MAXIMUM_RECURSION_DEPTH`.
CMAKE_MAXIMUM_RECURSION_DEPTH
-----------------------------
Maximum recursion depth for CMake scripts. It is intended to be set on the
command line with ``-DCMAKE_MAXIMUM_RECURSION_DEPTH=<x>``, or within
``CMakeLists.txt`` by projects that require a large recursion depth. Projects
that set this variable should provide the user with a way to override it. For
example:
.. code-block:: cmake
# About to perform deeply recursive actions
if(NOT CMAKE_MAXIMUM_RECURSION_DEPTH)
set(CMAKE_MAXIMUM_RECURSION_DEPTH 2000)
endif()
If it is not set, or is set to a non-integer value, a sensible default limit is
used. If the recursion limit is reached, the script terminates immediately with
a fatal error.
Calling any of the following commands increases the recursion depth:
* :command:`include`
* :command:`find_package`
* :command:`add_subdirectory`
* :command:`try_compile`
* :command:`ctest_read_custom_files`
* :command:`ctest_run_script` (unless ``NEW_PROCESS`` is specified)
* User-defined :command:`function`'s and :command:`macro`'s (note that
:command:`function` and :command:`macro` themselves don't increase recursion
depth)
* Reading or writing variables that are being watched by a
:command:`variable_watch`
......@@ -31,6 +31,16 @@ else()
set(CMAKE_USE_ELF_PARSER)
endif()
if(NOT CMake_DEFAULT_RECURSION_LIMIT)
if(DEFINED ENV{DASHBOARD_TEST_FROM_CTEST})
set(CMake_DEFAULT_RECURSION_LIMIT 100)
elseif(MINGW)
set(CMake_DEFAULT_RECURSION_LIMIT 400)
else()
set(CMake_DEFAULT_RECURSION_LIMIT 1000)
endif()
endif()
if(APPLE)
set(CMAKE_USE_MACH_PARSER 1)
endif()
......
......@@ -39,7 +39,8 @@ bool cmCTestRunScriptCommand::InitialPass(std::vector<std::string> const& args,
++i;
} else {
int ret;
cmCTestScriptHandler::RunScript(this->CTest, args[i].c_str(), !np, &ret);
cmCTestScriptHandler::RunScript(this->CTest, this->Makefile,
args[i].c_str(), !np, &ret);
std::ostringstream str;
str << ret;
this->Makefile->AddDefinition(returnVariable, str.str().c_str());
......
......@@ -78,6 +78,7 @@ cmCTestScriptHandler::cmCTestScriptHandler()
this->EmptyBinDir = false;
this->EmptyBinDirOnce = false;
this->Makefile = nullptr;
this->ParentMakefile = nullptr;
this->CMake = nullptr;
this->GlobalGenerator = nullptr;
......@@ -117,6 +118,7 @@ void cmCTestScriptHandler::Initialize()
delete this->Makefile;
this->Makefile = nullptr;
this->ParentMakefile = nullptr;
delete this->GlobalGenerator;
this->GlobalGenerator = nullptr;
......@@ -292,6 +294,10 @@ void cmCTestScriptHandler::CreateCMake()
snapshot.GetDirectory().SetCurrentSource(cwd);
snapshot.GetDirectory().SetCurrentBinary(cwd);
this->Makefile = new cmMakefile(this->GlobalGenerator, snapshot);
if (this->ParentMakefile) {
this->Makefile->SetRecursionDepth(
this->ParentMakefile->GetRecursionDepth());
}
this->CMake->SetProgressCallback(ctestScriptProgressCallback, this->CTest);
......@@ -891,11 +897,13 @@ void cmCTestScriptHandler::RestoreBackupDirectories()
}
}
bool cmCTestScriptHandler::RunScript(cmCTest* ctest, const char* sname,
bool InProcess, int* returnValue)
bool cmCTestScriptHandler::RunScript(cmCTest* ctest, cmMakefile* mf,
const char* sname, bool InProcess,
int* returnValue)
{
cmCTestScriptHandler* sh = new cmCTestScriptHandler();
sh->SetCTestInstance(ctest);
sh->ParentMakefile = mf;
sh->AddConfigurationScript(sname, InProcess);
int res = sh->ProcessHandler();
if (returnValue) {
......
......@@ -72,8 +72,8 @@ public:
/*
* Run a script
*/
static bool RunScript(cmCTest* ctest, const char* script, bool InProcess,
int* returnValue);
static bool RunScript(cmCTest* ctest, cmMakefile* mf, const char* script,
bool InProcess, int* returnValue);
int RunCurrentScript();
/*
......@@ -166,6 +166,7 @@ private:
std::chrono::steady_clock::time_point ScriptStartTime;
cmMakefile* Makefile;
cmMakefile* ParentMakefile;
cmGlobalGenerator* GlobalGenerator;
cmake* CMake;
};
......
......@@ -20,6 +20,7 @@
#cmakedefine CMAKE_USE_ELF_PARSER
#cmakedefine CMAKE_USE_MACH_PARSER
#cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE
#define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@
#define CMAKE_BIN_DIR "/@CMAKE_BIN_DIR@"
#define CMAKE_DATA_DIR "/@CMAKE_DATA_DIR@"
......
......@@ -98,6 +98,8 @@ cmGlobalGenerator::cmGlobalGenerator(cmake* cm)
this->ConfigureDoneCMP0026AndCMP0024 = false;
this->FirstTimeProgress = 0.0f;
this->RecursionDepth = 0;
cm->GetState()->SetIsGeneratorMultiConfig(false);
cm->GetState()->SetMinGWMake(false);
cm->GetState()->SetMSYSShell(false);
......@@ -1166,6 +1168,7 @@ void cmGlobalGenerator::Configure()
this->CMakeInstance->GetHomeOutputDirectory());
cmMakefile* dirMf = new cmMakefile(this, snapshot);
dirMf->SetRecursionDepth(this->RecursionDepth);
this->Makefiles.push_back(dirMf);
this->IndexMakefile(dirMf);
......
......@@ -432,6 +432,8 @@ public:
std::string MakeSilentFlag;
int RecursionDepth;
protected:
typedef std::vector<cmLocalGenerator*> GeneratorVector;
// for a project collect all its targets by following depend
......
......@@ -45,6 +45,8 @@
#include "cm_sys_stat.h"
#include "cmake.h"
#include "cmConfigure.h" // IWYU pragma: keep
#ifdef CMAKE_BUILD_WITH_CMAKE
# include "cmVariableWatch.h"
#endif
......@@ -83,6 +85,7 @@ cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator,
this->StateSnapshot =
this->StateSnapshot.GetState()->CreatePolicyScopeSnapshot(
this->StateSnapshot);
this->RecursionDepth = 0;
// Enter a policy level for this directory.
this->PushPolicy();
......@@ -333,12 +336,14 @@ public:
cmListFileContext const& lfc = cmListFileContext::FromCommandContext(
cc, this->Makefile->StateSnapshot.GetExecutionListFile());
this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc);
++this->Makefile->RecursionDepth;
this->Makefile->ExecutionStatusStack.push_back(&status);
}
~cmMakefileCall()
{
this->Makefile->ExecutionStatusStack.pop_back();
--this->Makefile->RecursionDepth;
this->Makefile->Backtrace = this->Makefile->Backtrace.Pop();
}
......@@ -361,6 +366,24 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
cmMakefileCall stack_manager(this, lff, status);
static_cast<void>(stack_manager);
// Check for maximum recursion depth.
int depth = CMake_DEFAULT_RECURSION_LIMIT;
const char* depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
if (depthStr) {
std::istringstream s(depthStr);
int d;
if (s >> d) {
depth = d;
}
}
if (this->RecursionDepth > depth) {
std::ostringstream e;
e << "Maximum recursion depth of " << depth << " exceeded";
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
cmSystemTools::SetFatalErrorOccured();
return false;
}
// Lookup the command prototype.
if (cmCommand* proto =
this->GetState()->GetCommandByExactName(lff.Name.Lower)) {
......@@ -1369,6 +1392,9 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent)
// Imported targets.
this->ImportedTargets = parent->ImportedTargets;
// Recursion depth.
this->RecursionDepth = parent->RecursionDepth;
}
void cmMakefile::PushFunctionScope(std::string const& fileName,
......@@ -2724,6 +2750,16 @@ bool cmMakefile::IsProjectFile(const char* filename) const
!cmSystemTools::IsSubDirectory(filename, "/CMakeFiles"));
}
int cmMakefile::GetRecursionDepth() const
{
return this->RecursionDepth;
}
void cmMakefile::SetRecursionDepth(int recursionDepth)
{
this->RecursionDepth = recursionDepth;
}
MessageType cmMakefile::ExpandVariablesInStringNew(
std::string& errorstr, std::string& source, bool escapeQuotes,
bool noEscapes, bool atOnly, const char* filename, long line,
......@@ -3387,6 +3423,7 @@ int cmMakefile::TryCompile(const std::string& srcdir,
this->IsSourceFileTryCompile = false;
return 1;
}
gg->RecursionDepth = this->RecursionDepth;
cm.SetGlobalGenerator(gg);
// do a configure
......@@ -3406,6 +3443,12 @@ int cmMakefile::TryCompile(const std::string& srcdir,
cmStateEnums::STRING);
}
}
const char* recursionDepth =
this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
if (recursionDepth) {
cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth,
"Maximum recursion depth", cmStateEnums::STRING);
}
// if cmake args were provided then pass them in
if (cmakeArgs) {
// FIXME: Workaround to ignore unused CLI variables in try-compile.
......
......@@ -870,6 +870,9 @@ public:
const char* sourceFilename) const;
bool IsProjectFile(const char* filename) const;
int GetRecursionDepth() const;
void SetRecursionDepth(int recursionDepth);
protected:
// add link libraries and directories to the target
void AddGlobalLinkInformation(cmTarget& target);
......@@ -930,6 +933,7 @@ protected:
private:
cmStateSnapshot StateSnapshot;
cmListFileBacktrace Backtrace;
int RecursionDepth;
void ReadListFile(cmListFile const& listFile,
const std::string& filenametoread);
......
......@@ -215,6 +215,7 @@ endif()
add_RunCMake_test(CompatibleInterface)
add_RunCMake_test(Syntax)
add_RunCMake_test(WorkingDirectory)
add_RunCMake_test(MaxRecursionDepth)
add_RunCMake_test(add_custom_command)
add_RunCMake_test(add_custom_target)
......
cmake_minimum_required(VERSION 3.13)
if(DEFINED CMAKE_GENERATOR)
project(${RunCMake_TEST} NONE)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/${TEST_NAME}.cmake")
cmake_minimum_required(VERSION 3.12)
project(MaxRecursionDepth NONE)
message("${x}")
math(EXPR x "${x} + 1")
ctest_read_custom_files("${CMAKE_CURRENT_LIST_DIR}")
message("${x}")
math(EXPR x "${x} + 1")
find_package(RecursivePackage)
include(RunCMake)
include(RunCTest)
function(run_cmake_recursive name)
set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
run_cmake(${name}-default)
unset(RunCMake_TEST_OPTIONS)
set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
run_cmake(${name}-var)
unset(RunCMake_TEST_OPTIONS)
set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a)
run_cmake(${name}-invalid-var)
unset(RunCMake_TEST_OPTIONS)
run_cmake_command(${name}-default-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
run_cmake_command(${name}-var-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10 -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
run_cmake_command(${name}-invalid-var-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
endfunction()
function(run_ctest_recursive name)
run_ctest(${name}-default "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
run_ctest(${name}-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
run_ctest(${name}-invalid-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a)
endfunction()
run_cmake_recursive(function)
run_cmake_recursive(macro)
run_cmake_recursive(include)
run_cmake_recursive(find_package)
run_cmake_recursive(variable_watch)
# We run these tests separately and only with a small limit because they are
# taxing and slow. The "implicit" and "invalid" cases are already thoroughly
# covered by the other tests above.
set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=add_subdirectory -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
run_cmake(add_subdirectory-var)
unset(RunCMake_TEST_OPTIONS)
set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=try_compile -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
run_cmake(try_compile-var)
unset(RunCMake_TEST_OPTIONS)
run_ctest_recursive(ctest_read_custom_files)
# We run the ctest_run_script() test separately and only with an explicit limit
# because ctest_run_script() is taxing and slow, and because the implicit
# recursion limit is hit by CTestScriptMode.cmake before we can test it
# properly. The "implicit" and "invalid" cases are already thoroughly covered
# by the other tests above.
run_ctest(ctest_run_script-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=ctest_run_script -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
^3
4
5
6
7
8
9
10
CMake Error at add_subdirectory/CMakeLists\.txt:1 \(message\):
Maximum recursion depth of 10 exceeded$
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment