mirror of
https://github.com/Kitware/CMake.git
synced 2026-02-04 22:29:40 -06:00
file(LOCK): Avoid truncating existing files
Previously the command opened the lock file using fopen with "w" mode, which truncates the file to zero length. This is unsafe because: 1. If the lock file path is a symlink, the target file gets truncated 2. Race conditions between path resolution and file opening can be exploited to truncate arbitrary files An attacker can exploit this by creating a symlink at a predictable lock file location pointing to a critical file (e.g., source files, configuration, or system files). When cmake runs file(LOCK), it follows the symlink and destroys the target file's contents. Fix by changing the file mode from "w" (write/truncate) to "a" (append). This creates the file if it doesn't exist but preserves existing content, preventing data destruction attacks.
This commit is contained in:
committed by
Brad King
parent
86a2d3e9a5
commit
8ada1bcf8c
@@ -3017,7 +3017,7 @@ bool HandleLockCommand(std::vector<std::string> const& args,
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
return false;
|
||||
}
|
||||
FILE* file = cmsys::SystemTools::Fopen(path, "w");
|
||||
FILE* file = cmsys::SystemTools::Fopen(path, "a");
|
||||
if (!file) {
|
||||
status.GetMakefile().IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
|
||||
4
Tests/RunCMake/file/LOCK-symlink-no-truncate-stdout.txt
Normal file
4
Tests/RunCMake/file/LOCK-symlink-no-truncate-stdout.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
-- Original content: IMPORTANT DATA THAT MUST NOT BE DESTROYED
|
||||
-- Lock result: 0
|
||||
-- Final content: IMPORTANT DATA THAT MUST NOT BE DESTROYED
|
||||
-- PASS: Symlink target was not truncated
|
||||
37
Tests/RunCMake/file/LOCK-symlink-no-truncate.cmake
Normal file
37
Tests/RunCMake/file/LOCK-symlink-no-truncate.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
# Test that file(LOCK) does not truncate existing files when the lock path
|
||||
# is a symlink pointing to another file. This is a regression test for a
|
||||
# data destruction vulnerability (CWE-59).
|
||||
|
||||
set(target_file "${CMAKE_CURRENT_BINARY_DIR}/target_file.txt")
|
||||
set(lock_symlink "${CMAKE_CURRENT_BINARY_DIR}/lock_symlink")
|
||||
|
||||
# Create a target file with known content
|
||||
file(WRITE "${target_file}" "IMPORTANT DATA THAT MUST NOT BE DESTROYED")
|
||||
|
||||
# Read original content for comparison
|
||||
file(READ "${target_file}" original_content)
|
||||
message(STATUS "Original content: ${original_content}")
|
||||
|
||||
# Create a symlink pointing to the target file
|
||||
file(CREATE_LINK "${target_file}" "${lock_symlink}" SYMBOLIC)
|
||||
|
||||
# Attempt to lock the symlink - this should NOT truncate the target
|
||||
file(LOCK "${lock_symlink}" RESULT_VARIABLE lock_result)
|
||||
message(STATUS "Lock result: ${lock_result}")
|
||||
|
||||
# Release the lock
|
||||
file(LOCK "${lock_symlink}" RELEASE)
|
||||
|
||||
# Verify the target file still has its content
|
||||
file(READ "${target_file}" final_content)
|
||||
message(STATUS "Final content: ${final_content}")
|
||||
|
||||
if(NOT final_content STREQUAL original_content)
|
||||
message(FATAL_ERROR
|
||||
"VULNERABILITY: file(LOCK) truncated the symlink target!\n"
|
||||
"Original: '${original_content}'\n"
|
||||
"Final: '${final_content}'"
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "PASS: Symlink target was not truncated")
|
||||
@@ -91,6 +91,8 @@ if(NOT WIN32
|
||||
if(NOT CYGWIN)
|
||||
run_cmake(INSTALL-FOLLOW_SYMLINK_CHAIN)
|
||||
endif()
|
||||
# Test that file(LOCK) doesn't truncate symlink targets (CVE regression test)
|
||||
run_cmake(LOCK-symlink-no-truncate)
|
||||
endif()
|
||||
|
||||
run_cmake(REAL_PATH-non-existing)
|
||||
|
||||
Reference in New Issue
Block a user