From 6e638ce1091529cd9796fb72c9e182173c5a4db3 Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Tue, 14 Apr 2026 15:46:58 -0400 Subject: [PATCH 1/2] Tests/File_Archive: Relax stderr line matching "Compression format unsupported" errors of this form can only possibly come from `file(ARCHIVE_CREATE)`, so we don't need to track the line number of the called function. Reduce the need to update these numbers every time the test helper script is updated. --- .../File_Archive/gnutar-with-bad-compression-stderr.txt | 2 +- Tests/RunCMake/File_Archive/unsupported-format-stderr.txt | 2 +- Tests/RunCMake/File_Archive/zip-with-bad-compression-stderr.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/RunCMake/File_Archive/gnutar-with-bad-compression-stderr.txt b/Tests/RunCMake/File_Archive/gnutar-with-bad-compression-stderr.txt index a0531c8b65..7be59b588f 100644 --- a/Tests/RunCMake/File_Archive/gnutar-with-bad-compression-stderr.txt +++ b/Tests/RunCMake/File_Archive/gnutar-with-bad-compression-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at roundtrip\.cmake:38 \(file\): +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): file PPMd compression is not supported for gnutar format Call Stack \(most recent call first\): gnutar-with-bad-compression\.cmake:6 \(include\) diff --git a/Tests/RunCMake/File_Archive/unsupported-format-stderr.txt b/Tests/RunCMake/File_Archive/unsupported-format-stderr.txt index 1938c8f519..c2899314ca 100644 --- a/Tests/RunCMake/File_Archive/unsupported-format-stderr.txt +++ b/Tests/RunCMake/File_Archive/unsupported-format-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at roundtrip\.cmake:38 \(file\): +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): file archive format rar not supported Call Stack \(most recent call first\): unsupported-format\.cmake:5 \(include\) diff --git a/Tests/RunCMake/File_Archive/zip-with-bad-compression-stderr.txt b/Tests/RunCMake/File_Archive/zip-with-bad-compression-stderr.txt index 3db2297bf9..136856b9bf 100644 --- a/Tests/RunCMake/File_Archive/zip-with-bad-compression-stderr.txt +++ b/Tests/RunCMake/File_Archive/zip-with-bad-compression-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at roundtrip\.cmake:38 \(file\): +CMake Error at roundtrip\.cmake:[0-9]+ \(file\): file PPMd compression is not supported for zip format Call Stack \(most recent call first\): zip-with-bad-compression\.cmake:6 \(include\) From 4397f89c169433490c282deb8b5abaacf222a819 Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Tue, 14 Apr 2026 12:55:52 -0400 Subject: [PATCH 2/2] Restore archive extraction between directory symlinks Commit 03f19aa4ea (cmSystemTools: Fix path traversal vulnerability in archive extraction, 2026-01-06, v4.3.0-rc1~179^2) incidentally introduced the `ARCHIVE_EXTRACT_SECURE_SYMLINKS` flag when extracting archives, which prevents extraction of objects whose location would be altered by a symlink on disk. However, this particular behavior change was never documented or tested alongside the other changes in the commit (absolute paths and path traversal components). Divergent behavior of this flag from `libarchive` on Windows and non-Windows platforms needs further investigation. In particular, on Windows, archives cannot be extracted through directory symlinks, but can be extracted through junctions. On Linux, archives *can* still be extracted through symlinks, and archives containing symlinks can be extracted as well. The symlink change breaks compatibility with many common use cases of existing projects, e.g., when extracting an archive inside a symlinked build directory. Revert the flag until this issue can be explored in a future version of CMake. Fixes: #27752 Reported-by: scivision --- Source/cmSystemTools.cxx | 4 ++-- .../CommandLineTar/extract-through-symlink.cmake | 12 ++++++++++++ Tests/RunCMake/CommandLineTar/roundtrip.cmake | 8 +++++++- Tests/RunCMake/File_Archive/RunCMakeTest.cmake | 2 ++ .../File_Archive/extract-through-symlink.cmake | 9 +++++++++ Tests/RunCMake/File_Archive/roundtrip.cmake | 8 +++++++- 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 Tests/RunCMake/CommandLineTar/extract-through-symlink.cmake create mode 100644 Tests/RunCMake/File_Archive/extract-through-symlink.cmake diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 2329449c8b..c0049ba3a6 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -2626,8 +2626,8 @@ bool extract_tar(std::string const& arFileName, struct archive* a = archive_read_new(); struct archive* ext = archive_write_disk_new(); if (extract) { - int flags = ARCHIVE_EXTRACT_SECURE_NODOTDOT | - ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS | ARCHIVE_EXTRACT_SECURE_SYMLINKS; + int flags = + ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; if (extractTimestamps == cmSystemTools::cmTarExtractTimestamps::Yes) { flags |= ARCHIVE_EXTRACT_TIME; } diff --git a/Tests/RunCMake/CommandLineTar/extract-through-symlink.cmake b/Tests/RunCMake/CommandLineTar/extract-through-symlink.cmake new file mode 100644 index 0000000000..f08f0966d5 --- /dev/null +++ b/Tests/RunCMake/CommandLineTar/extract-through-symlink.cmake @@ -0,0 +1,12 @@ +# Test that cmake -E tar can extract to a symlinked directory + +set(OUTPUT_NAME "test.tar.gz") + +set(COMPRESSION_FLAGS -cvzf) +set(COMPRESSION_OPTIONS --format=gnutar) + +set(DECOMPRESSION_FLAGS -xvzf) + +set(DESTINATION_SYMLINK ON) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) diff --git a/Tests/RunCMake/CommandLineTar/roundtrip.cmake b/Tests/RunCMake/CommandLineTar/roundtrip.cmake index a6311ea6f6..eeafcdbf1c 100644 --- a/Tests/RunCMake/CommandLineTar/roundtrip.cmake +++ b/Tests/RunCMake/CommandLineTar/roundtrip.cmake @@ -44,7 +44,13 @@ endif() file(REMOVE ${FULL_OUTPUT_NAME}) file(REMOVE_RECURSE ${FULL_DECOMPRESS_DIR}) -file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}) +if(DESTINATION_SYMLINK AND UNIX) + file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}-dir) + execute_process(COMMAND ln -sf + ${FULL_DECOMPRESS_DIR}-dir ${FULL_DECOMPRESS_DIR}) +else() + file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}) +endif() run_tar(${CMAKE_CURRENT_BINARY_DIR} ${COMPRESSION_FLAGS} ${FULL_OUTPUT_NAME} ${COMPRESSION_OPTIONS} ${COMPRESS_DIR}) run_tar(${FULL_DECOMPRESS_DIR} ${DECOMPRESSION_FLAGS} ${FULL_OUTPUT_NAME} ${DECOMPRESSION_OPTIONS} -- ${DECOMPRESSION_PATHNAMES}) diff --git a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake index 0602db1a01..a49c7f00fc 100644 --- a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake +++ b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake @@ -58,3 +58,5 @@ if(Python_EXECUTABLE) run_cmake_script(path-absolute -DPython_EXECUTABLE=${Python_EXECUTABLE}) run_cmake_script(path-traversal -DPython_EXECUTABLE=${Python_EXECUTABLE}) endif() + +run_cmake(extract-through-symlink) diff --git a/Tests/RunCMake/File_Archive/extract-through-symlink.cmake b/Tests/RunCMake/File_Archive/extract-through-symlink.cmake new file mode 100644 index 0000000000..2734bc62ab --- /dev/null +++ b/Tests/RunCMake/File_Archive/extract-through-symlink.cmake @@ -0,0 +1,9 @@ +# Test that file(ARCHIVE_EXTRACT) can extract to a symlinked directory + +set(OUTPUT_NAME "test.tar.gz") + +set(ARCHIVE_FORMAT gnutar) +set(COMPRESSION_TYPE GZip) +set(DESTINATION_SYMLINK ON) + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) diff --git a/Tests/RunCMake/File_Archive/roundtrip.cmake b/Tests/RunCMake/File_Archive/roundtrip.cmake index 99b87af5cb..a2048930de 100644 --- a/Tests/RunCMake/File_Archive/roundtrip.cmake +++ b/Tests/RunCMake/File_Archive/roundtrip.cmake @@ -33,7 +33,13 @@ endif() file(REMOVE ${FULL_OUTPUT_NAME}) file(REMOVE_RECURSE ${FULL_DECOMPRESS_DIR}) -file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}) +if(DESTINATION_SYMLINK AND UNIX) + file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}-dir) + execute_process(COMMAND ln -sf + ${FULL_DECOMPRESS_DIR}-dir ${FULL_DECOMPRESS_DIR}) +else() + file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR}) +endif() file(ARCHIVE_CREATE OUTPUT ${FULL_OUTPUT_NAME}