mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
The TRANSFORM action registry held one live action instance per action for the whole process, and each instance carried per-call state: a raw Selector pointer, REPLACE's helper, APPEND/PREPEND's operands. That was harmless while every action ran to completion uninterrupted, but commitc7af6e94d8(list(TRANSFORM): Add PREDICATE selector, 2026-04-08, v4.4.0-rc1) added a selector that runs a user function once per element, interleaved with the transform. User code reentering list(TRANSFORM) with the same action now rebinds the shared instance mid-flight. That produces silently wrong results, and a use-after-free once any element is transformed after the reentering one. It needs no unusual code to hit: a predicate calling find_package(Python) reaches list(TRANSFORM ... REPLACE) inside FindPython's own module. Make action objects immutable and per-call. Operands and the selector become constructor arguments, Initialize is deleted, and the registry becomes a constexpr table of metadata with a MakeTransformAction factory. The InSelection guard, duplicated in all eight actions, moves into the base class. This also closes a second hole in the same machinery. TransformActionApply overrode only the vector form of Initialize, so transform(APPLY, "f", selector) dispatched to the empty two-argument virtual in the base, left Selector null, and dereferenced it. With no virtual Initialize left to inherit, an APPLY action without a cmMakefile is no longer constructible, so the throwing stub that guarded the vector form is no longer needed. Close a third, from commit651f82642c(Add APPLY action for list(TRANSFORM), 2026-04-08, v4.4.0-rc1): the cmMakefile overload performs APPLY unconditionally but validated only the arity of the action passed to it. APPEND, PREPEND and APPLY all take one argument, so transform(APPEND, "x", makefile) passed validation and then silently ran APPLY, calling "x" as a function instead of appending it. Reject any action but APPLY up front. That path is unreachable from CMake code, since HandleTransformCommand only selects the overload for APPLY, and Tests/CMakeLib has no cmMakefile to drive it with, so it carries no test. Document the predicate's evaluation order while here. It runs once per element, immediately before that element would be transformed. The manual did not state the timing, which matters precisely because a predicate that reenters list(TRANSFORM) observes the outer call mid-flight. Fixes: #28031
149 lines
4.6 KiB
CMake
149 lines
4.6 KiB
CMake
include(RunCMake)
|
|
|
|
run_cmake(EmptyFilterRegex)
|
|
run_cmake(EmptyGet0)
|
|
run_cmake(EmptyRemoveAt0)
|
|
run_cmake(EmptyInsert-1)
|
|
|
|
run_cmake(NoArguments)
|
|
run_cmake(InvalidSubcommand)
|
|
|
|
run_cmake(FILTER-REGEX-InvalidRegex)
|
|
run_cmake(GET-InvalidIndex)
|
|
run_cmake(INSERT-InvalidIndex)
|
|
run_cmake(REMOVE_AT-InvalidIndex)
|
|
run_cmake(SUBLIST-InvalidIndex)
|
|
|
|
run_cmake(FILTER-REGEX-TooManyArguments)
|
|
run_cmake(JOIN-TooManyArguments)
|
|
run_cmake(LENGTH-TooManyArguments)
|
|
run_cmake(REMOVE_DUPLICATES-TooManyArguments)
|
|
run_cmake(REVERSE-TooManyArguments)
|
|
run_cmake(SUBLIST-TooManyArguments)
|
|
|
|
run_cmake(REMOVE_AT-EmptyList)
|
|
|
|
run_cmake(REMOVE_DUPLICATES-PreserveOrder)
|
|
|
|
run_cmake(FILTER-NotList)
|
|
run_cmake(REMOVE_AT-NotList)
|
|
run_cmake(REMOVE_DUPLICATES-NotList)
|
|
run_cmake(REMOVE_ITEM-NotList)
|
|
run_cmake(REMOVE_ITEM-NoItemArg)
|
|
run_cmake(REVERSE-NotList)
|
|
run_cmake(SORT-NotList)
|
|
|
|
run_cmake(FILTER-REGEX-InvalidMode)
|
|
run_cmake(FILTER-REGEX-InvalidOperator)
|
|
run_cmake(FILTER-REGEX-Valid0)
|
|
run_cmake(FILTER-REGEX-Valid1)
|
|
run_cmake(FILTER-PREDICATE-UnknownFunction)
|
|
run_cmake(FILTER-PREDICATE-Macro)
|
|
run_cmake(FILTER-PREDICATE-NoOutput)
|
|
run_cmake(FILTER-PREDICATE-TooManyArguments)
|
|
run_cmake(FILTER-PREDICATE-Include)
|
|
run_cmake(FILTER-PREDICATE-Exclude)
|
|
|
|
run_cmake(JOIN-NoArguments)
|
|
run_cmake(JOIN-NoVariable)
|
|
run_cmake(JOIN)
|
|
|
|
run_cmake(SUBLIST-NoArguments)
|
|
run_cmake(SUBLIST-NoVariable)
|
|
run_cmake(SUBLIST-InvalidLength)
|
|
run_cmake(SUBLIST)
|
|
|
|
run_cmake(TRANSFORM-NoAction)
|
|
run_cmake(TRANSFORM-InvalidAction)
|
|
# 'action' oriented tests
|
|
run_cmake(TRANSFORM-TOUPPER-TooManyArguments)
|
|
run_cmake(TRANSFORM-TOLOWER-TooManyArguments)
|
|
run_cmake(TRANSFORM-STRIP-TooManyArguments)
|
|
run_cmake(TRANSFORM-GENEX_STRIP-TooManyArguments)
|
|
run_cmake(TRANSFORM-APPEND-NoArguments)
|
|
run_cmake(TRANSFORM-APPEND-TooManyArguments)
|
|
run_cmake(TRANSFORM-PREPEND-NoArguments)
|
|
run_cmake(TRANSFORM-PREPEND-TooManyArguments)
|
|
run_cmake(TRANSFORM-REPLACE-NoArguments)
|
|
run_cmake(TRANSFORM-REPLACE-NoEnoughArguments)
|
|
run_cmake(TRANSFORM-REPLACE-TooManyArguments)
|
|
run_cmake(TRANSFORM-REPLACE-InvalidRegex)
|
|
run_cmake(TRANSFORM-REPLACE-InvalidReplace1)
|
|
run_cmake(TRANSFORM-REPLACE-InvalidReplace2)
|
|
run_cmake(TRANSFORM-APPLY-NoFunction)
|
|
run_cmake(TRANSFORM-APPLY-UnknownFunction)
|
|
run_cmake(TRANSFORM-APPLY-Macro)
|
|
run_cmake(TRANSFORM-APPLY-NoOutput)
|
|
# 'selector' oriented tests
|
|
run_cmake(TRANSFORM-Selector-REGEX-NoArguments)
|
|
run_cmake(TRANSFORM-Selector-REGEX-TooManyArguments)
|
|
run_cmake(TRANSFORM-Selector-REGEX-InvalidRegex)
|
|
run_cmake(TRANSFORM-Selector-AT-NoArguments)
|
|
run_cmake(TRANSFORM-Selector-AT-BadArgument)
|
|
run_cmake(TRANSFORM-Selector-AT-InvalidIndex)
|
|
run_cmake(TRANSFORM-Selector-FOR-NoArguments)
|
|
run_cmake(TRANSFORM-Selector-FOR-NoEnoughArguments)
|
|
run_cmake(TRANSFORM-Selector-FOR-TooManyArguments)
|
|
run_cmake(TRANSFORM-Selector-FOR-BadArgument)
|
|
run_cmake(TRANSFORM-Selector-FOR-InvalidIndex)
|
|
run_cmake(TRANSFORM-Selector-FOR-ZeroStepArgument)
|
|
run_cmake(TRANSFORM-Selector-FOR-NegativeStepArgument)
|
|
run_cmake(TRANSFORM-Selector-FOR-BackwardsRange)
|
|
run_cmake(TRANSFORM-Selector-PREDICATE-NoArguments)
|
|
run_cmake(TRANSFORM-Selector-PREDICATE-UnknownFunction)
|
|
run_cmake(TRANSFORM-Selector-PREDICATE-Macro)
|
|
run_cmake(TRANSFORM-Selector-PREDICATE-NoOutput)
|
|
# 'output' oriented tests
|
|
run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments)
|
|
run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments)
|
|
# Successful tests
|
|
run_cmake(TRANSFORM-TOUPPER)
|
|
run_cmake(TRANSFORM-TOLOWER)
|
|
run_cmake(TRANSFORM-STRIP)
|
|
run_cmake(TRANSFORM-GENEX_STRIP)
|
|
run_cmake(TRANSFORM-APPEND)
|
|
run_cmake(TRANSFORM-PREPEND)
|
|
run_cmake(TRANSFORM-REPLACE)
|
|
run_cmake(TRANSFORM-APPLY)
|
|
run_cmake(TRANSFORM-PREDICATE)
|
|
run_cmake(TRANSFORM-PREDICATE-Reentrant)
|
|
run_cmake(CMP0186)
|
|
|
|
# argument tests
|
|
run_cmake(SORT-WrongOption)
|
|
run_cmake(SORT-BadCaseOption)
|
|
run_cmake(SORT-BadCompareOption)
|
|
run_cmake(SORT-BadOrderOption)
|
|
run_cmake(SORT-DuplicateOrderOption)
|
|
run_cmake(SORT-DuplicateCompareOption)
|
|
run_cmake(SORT-DuplicateCaseOption)
|
|
run_cmake(SORT-NoCaseOption)
|
|
run_cmake(SORT-COMPARATOR-UnknownFunction)
|
|
run_cmake(SORT-COMPARATOR-Macro)
|
|
run_cmake(SORT-COMPARATOR-NoOutput)
|
|
run_cmake(SORT-COMPARATOR-CompareConflict)
|
|
run_cmake(SORT-COMPARATOR-CompareConflictReverse)
|
|
run_cmake(SORT-COMPARATOR-NoFunction)
|
|
run_cmake(SORT-COMPARATOR-DuplicateOption)
|
|
run_cmake(SORT-COMPARATOR-NotStrictWeak)
|
|
run_cmake(SORT-COMPARATOR-NotStrictWeakLate)
|
|
|
|
# Successful tests
|
|
run_cmake(SORT)
|
|
run_cmake(SORT-COMPARATOR)
|
|
|
|
# argument tests
|
|
run_cmake(PREPEND-NoArgs)
|
|
# Successful tests
|
|
run_cmake(PREPEND)
|
|
|
|
# argument tests
|
|
run_cmake(POP_BACK-NoArgs)
|
|
run_cmake(POP_FRONT-NoArgs)
|
|
# Successful tests
|
|
run_cmake(POP_BACK)
|
|
run_cmake(POP_FRONT)
|
|
|
|
# Nonexistent variables treated as empty
|
|
run_cmake(LIST-nonexistent)
|