mirror of
https://github.com/Kitware/CMake.git
synced 2026-06-24 08:47:59 +00:00
Tests/Fuzzing: Add cmArchiveExtractFuzzer
Fuzz the CMake archive extraction utilities. Tests tar, zip, and other archive format handling.
This commit is contained in:
committed by
Brad King
parent
2eec5a34f5
commit
0a760a2794
@@ -88,3 +88,6 @@ add_fuzzer(cmGlobFuzzer cmGlobFuzzer.cxx)
|
||||
|
||||
# ELF binary parser fuzzer
|
||||
add_fuzzer(cmELFFuzzer cmELFFuzzer.cxx)
|
||||
|
||||
# Archive extraction fuzzer
|
||||
add_fuzzer(cmArchiveExtractFuzzer cmArchiveExtractFuzzer.cxx)
|
||||
|
||||
72
Tests/Fuzzing/cmArchiveExtract.dict
Normal file
72
Tests/Fuzzing/cmArchiveExtract.dict
Normal file
@@ -0,0 +1,72 @@
|
||||
# Archive Format Dictionary
|
||||
|
||||
# Tar magic
|
||||
"ustar"
|
||||
"ustar "
|
||||
"ustar\x0000"
|
||||
|
||||
# Gzip magic
|
||||
"\x1f\x8b"
|
||||
"\x1f\x8b\x08"
|
||||
|
||||
# Bzip2 magic
|
||||
"BZ"
|
||||
"BZh"
|
||||
"BZh9"
|
||||
|
||||
# XZ magic
|
||||
"\xfd7zXZ\x00"
|
||||
|
||||
# Zip magic
|
||||
"PK\x03\x04"
|
||||
"PK\x01\x02"
|
||||
"PK\x05\x06"
|
||||
|
||||
# 7z magic
|
||||
"7z\xbc\xaf\x27\x1c"
|
||||
|
||||
# Tar header fields
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"0000000"
|
||||
"0000644"
|
||||
"0000755"
|
||||
"0000777"
|
||||
"00000000000"
|
||||
" "
|
||||
|
||||
# Path traversal (security testing)
|
||||
"../"
|
||||
"../../"
|
||||
"../../../"
|
||||
"../../../../"
|
||||
"/../"
|
||||
"/../../"
|
||||
|
||||
# Absolute paths
|
||||
"/etc/passwd"
|
||||
"/tmp/"
|
||||
"/home/"
|
||||
"/.."
|
||||
|
||||
# Long paths
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
|
||||
# Symlink indicators
|
||||
"1"
|
||||
"2"
|
||||
|
||||
# File type indicators
|
||||
"0"
|
||||
"5"
|
||||
"L"
|
||||
"K"
|
||||
|
||||
# Checksum placeholder
|
||||
" "
|
||||
|
||||
# Filenames
|
||||
"test.txt"
|
||||
"file.dat"
|
||||
"CMakeLists.txt"
|
||||
".hidden"
|
||||
"__MACOSX"
|
||||
121
Tests/Fuzzing/cmArchiveExtractFuzzer.cxx
Normal file
121
Tests/Fuzzing/cmArchiveExtractFuzzer.cxx
Normal file
@@ -0,0 +1,121 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
|
||||
/*
|
||||
* Fuzzer for CMake's archive extraction (tar/zip)
|
||||
*
|
||||
* CMake extracts archives via cmSystemTools::ExtractTar. This is a critical
|
||||
* attack surface as malicious archives could contain path traversal sequences
|
||||
* (Zip Slip) or other exploits.
|
||||
*
|
||||
* Coverage targets:
|
||||
* - Archive format detection (tar, gzip, bzip2, xz, zip)
|
||||
* - Path handling during extraction
|
||||
* - Symlink handling
|
||||
* - Large file handling
|
||||
* - Malformed archive recovery
|
||||
*
|
||||
* Security focus:
|
||||
* - Path traversal (../) detection
|
||||
* - Absolute path handling
|
||||
* - Symlink escape attempts
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
// Archives can be large but limit for fuzzing
|
||||
static constexpr size_t kMinInputSize = 4; // Minimum magic bytes
|
||||
static constexpr size_t kMaxInputSize = 256 * 1024; // 256KB
|
||||
|
||||
// Sandbox directory for extraction
|
||||
static std::string g_extractDir;
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
// Create a unique extraction directory in /tmp
|
||||
char tmpl[] = "/tmp/cmake_fuzz_extract_XXXXXX";
|
||||
char* dir = mkdtemp(tmpl);
|
||||
if (dir) {
|
||||
g_extractDir = dir;
|
||||
} else {
|
||||
g_extractDir = "/tmp/cmake_fuzz_extract";
|
||||
cmSystemTools::MakeDirectory(g_extractDir);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
if (size < kMinInputSize || size > kMaxInputSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write archive to temp file
|
||||
std::string archiveFile = g_extractDir + "/test_archive";
|
||||
{
|
||||
FILE* fp = fopen(archiveFile.c_str(), "wb");
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
fwrite(data, 1, size, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Create a fresh extraction subdirectory each time
|
||||
std::string extractSubDir = g_extractDir + "/out";
|
||||
cmSystemTools::RemoveADirectory(extractSubDir);
|
||||
cmSystemTools::MakeDirectory(extractSubDir);
|
||||
|
||||
// Save current directory
|
||||
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
|
||||
|
||||
// Change to extraction directory (cmSystemTools extracts to cwd)
|
||||
if (cmSystemTools::ChangeDirectory(extractSubDir)) {
|
||||
// Try extraction with different options
|
||||
std::vector<std::string> files;
|
||||
|
||||
// Extract without verbose, with timestamps
|
||||
bool result1 = cmSystemTools::ExtractTar(
|
||||
archiveFile, files, cmSystemTools::cmTarExtractTimestamps::Yes, false);
|
||||
(void)result1;
|
||||
|
||||
// Restore directory BEFORE removing (can't remove cwd)
|
||||
cmSystemTools::ChangeDirectory(cwd);
|
||||
|
||||
// Clean up extracted files
|
||||
cmSystemTools::RemoveADirectory(extractSubDir);
|
||||
cmSystemTools::MakeDirectory(extractSubDir);
|
||||
|
||||
// Change back for second extraction
|
||||
if (cmSystemTools::ChangeDirectory(extractSubDir)) {
|
||||
// Extract with verbose, without timestamps
|
||||
files.clear();
|
||||
bool result2 = cmSystemTools::ExtractTar(
|
||||
archiveFile, files, cmSystemTools::cmTarExtractTimestamps::No, true);
|
||||
(void)result2;
|
||||
|
||||
// Restore directory
|
||||
cmSystemTools::ChangeDirectory(cwd);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: A more thorough security check would verify nothing escaped the
|
||||
// sandbox, but for fuzzing we rely on sanitizers to catch path traversal
|
||||
|
||||
// Cleanup
|
||||
cmSystemTools::RemoveADirectory(extractSubDir);
|
||||
cmSystemTools::RemoveFile(archiveFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
Tests/Fuzzing/cmArchiveExtractFuzzer.options
Normal file
10
Tests/Fuzzing/cmArchiveExtractFuzzer.options
Normal file
@@ -0,0 +1,10 @@
|
||||
[libfuzzer]
|
||||
max_len = 262144
|
||||
timeout = 60
|
||||
rss_limit_mb = 2048
|
||||
|
||||
[afl]
|
||||
timeout = 60
|
||||
|
||||
[honggfuzz]
|
||||
timeout = 60
|
||||
Reference in New Issue
Block a user