mirror of
https://github.com/Kitware/CMake.git
synced 2026-06-24 08:47:59 +00:00
Tests/Fuzzing: Add cmELFFuzzer
Fuzz the CMake ELF binary parser. Tests parsing of ELF headers and sections.
This commit is contained in:
committed by
Brad King
parent
26f02d8f2a
commit
2eec5a34f5
@@ -85,3 +85,6 @@ add_fuzzer(cmCMakePathFuzzer cmCMakePathFuzzer.cxx)
|
||||
|
||||
# File glob fuzzer
|
||||
add_fuzzer(cmGlobFuzzer cmGlobFuzzer.cxx)
|
||||
|
||||
# ELF binary parser fuzzer
|
||||
add_fuzzer(cmELFFuzzer cmELFFuzzer.cxx)
|
||||
|
||||
85
Tests/Fuzzing/cmELF.dict
Normal file
85
Tests/Fuzzing/cmELF.dict
Normal file
@@ -0,0 +1,85 @@
|
||||
# ELF File Format Dictionary
|
||||
|
||||
# ELF magic number
|
||||
"\x7fELF"
|
||||
|
||||
# ELF class (32/64 bit)
|
||||
"\x01"
|
||||
"\x02"
|
||||
|
||||
# Data encoding (little/big endian)
|
||||
"\x01"
|
||||
"\x02"
|
||||
|
||||
# ELF version
|
||||
"\x01"
|
||||
|
||||
# OS/ABI
|
||||
"\x00"
|
||||
"\x03"
|
||||
"\x09"
|
||||
|
||||
# Object file types
|
||||
"\x00\x00"
|
||||
"\x01\x00"
|
||||
"\x02\x00"
|
||||
"\x03\x00"
|
||||
"\x04\x00"
|
||||
|
||||
# Machine types
|
||||
"\x03\x00"
|
||||
"\x3e\x00"
|
||||
"\x28\x00"
|
||||
"\xb7\x00"
|
||||
"\x08\x00"
|
||||
|
||||
# Section types
|
||||
"\x00\x00\x00\x00"
|
||||
"\x01\x00\x00\x00"
|
||||
"\x02\x00\x00\x00"
|
||||
"\x03\x00\x00\x00"
|
||||
"\x04\x00\x00\x00"
|
||||
"\x05\x00\x00\x00"
|
||||
"\x06\x00\x00\x00"
|
||||
"\x07\x00\x00\x00"
|
||||
"\x08\x00\x00\x00"
|
||||
"\x09\x00\x00\x00"
|
||||
"\x0b\x00\x00\x00"
|
||||
|
||||
# Dynamic tags
|
||||
"\x01\x00\x00\x00"
|
||||
"\x0e\x00\x00\x00"
|
||||
"\x0f\x00\x00\x00"
|
||||
"\x1d\x00\x00\x00"
|
||||
|
||||
# Section names
|
||||
".text"
|
||||
".data"
|
||||
".bss"
|
||||
".rodata"
|
||||
".dynamic"
|
||||
".dynsym"
|
||||
".dynstr"
|
||||
".interp"
|
||||
".note"
|
||||
".shstrtab"
|
||||
".strtab"
|
||||
".symtab"
|
||||
".rela"
|
||||
".rel"
|
||||
".plt"
|
||||
".got"
|
||||
".init"
|
||||
".fini"
|
||||
|
||||
# String entries
|
||||
"RPATH"
|
||||
"RUNPATH"
|
||||
"SONAME"
|
||||
"NEEDED"
|
||||
|
||||
# Common library paths
|
||||
"/lib"
|
||||
"/usr/lib"
|
||||
"$ORIGIN"
|
||||
"$LIB"
|
||||
147
Tests/Fuzzing/cmELFFuzzer.cxx
Normal file
147
Tests/Fuzzing/cmELFFuzzer.cxx
Normal file
@@ -0,0 +1,147 @@
|
||||
/* 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 ELF parser
|
||||
*
|
||||
* CMake parses ELF files to extract RPATH, RUNPATH, and SONAME information.
|
||||
* Malformed ELF files from untrusted sources could trigger vulnerabilities.
|
||||
*
|
||||
* Coverage targets:
|
||||
* - ELF header parsing (32-bit and 64-bit)
|
||||
* - Section header parsing
|
||||
* - Dynamic section parsing
|
||||
* - String table extraction
|
||||
* - RPATH/RUNPATH/SONAME extraction
|
||||
*
|
||||
* Performance notes:
|
||||
* - Uses memfd_create on Linux for memory-backed file I/O
|
||||
* - Falls back to temp files on other platforms
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cmELF.h"
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/mman.h>
|
||||
# ifndef MFD_CLOEXEC
|
||||
# define MFD_CLOEXEC 0x0001U
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// ELF files can be large, but we limit to avoid timeouts
|
||||
static constexpr size_t kMinInputSize = 16; // Minimum ELF header
|
||||
static constexpr size_t kMaxInputSize = 512 * 1024; // 512KB
|
||||
|
||||
static int g_memfd = -1;
|
||||
static std::string g_memfdPath;
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
#ifdef __linux__
|
||||
g_memfd = memfd_create("cmake_fuzz_elf", MFD_CLOEXEC);
|
||||
if (g_memfd >= 0) {
|
||||
g_memfdPath = "/proc/self/fd/" + std::to_string(g_memfd);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
// ELF files need minimum header size
|
||||
if (size < kMinInputSize || size > kMaxInputSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string testFile;
|
||||
|
||||
#ifdef __linux__
|
||||
if (g_memfd >= 0) {
|
||||
// Use memfd for better performance
|
||||
ftruncate(g_memfd, 0);
|
||||
lseek(g_memfd, 0, SEEK_SET);
|
||||
if (write(g_memfd, data, size) != static_cast<ssize_t>(size)) {
|
||||
return 0;
|
||||
}
|
||||
testFile = g_memfdPath;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Fallback to temp file
|
||||
char tmpFile[] = "/tmp/fuzz_elf_XXXXXX";
|
||||
int fd = mkstemp(tmpFile);
|
||||
if (fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (write(fd, data, size) != static_cast<ssize_t>(size)) {
|
||||
close(fd);
|
||||
unlink(tmpFile);
|
||||
return 0;
|
||||
}
|
||||
close(fd);
|
||||
testFile = tmpFile;
|
||||
}
|
||||
|
||||
// Parse the ELF file
|
||||
{
|
||||
cmELF elf(testFile.c_str());
|
||||
|
||||
// Check validity
|
||||
if (elf) {
|
||||
// Exercise all the parsing functions
|
||||
(void)elf.GetFileType();
|
||||
(void)elf.GetMachine();
|
||||
(void)elf.GetNumberOfSections();
|
||||
(void)elf.HasDynamicSection();
|
||||
(void)elf.IsMIPS();
|
||||
|
||||
// Try to get string entries
|
||||
std::string soname;
|
||||
elf.GetSOName(soname);
|
||||
(void)elf.GetSOName();
|
||||
(void)elf.GetRPath();
|
||||
(void)elf.GetRunPath();
|
||||
|
||||
// Get dynamic entries
|
||||
auto entries = elf.GetDynamicEntries();
|
||||
if (!entries.empty()) {
|
||||
auto encoded = elf.EncodeDynamicEntries(entries);
|
||||
(void)encoded;
|
||||
|
||||
// Get positions (limit iterations to avoid timeout)
|
||||
for (size_t i = 0; i < entries.size() && i < 100; ++i) {
|
||||
(void)elf.GetDynamicEntryPosition(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Print info to exercise that code path
|
||||
std::ostringstream oss;
|
||||
elf.PrintInfo(oss);
|
||||
}
|
||||
|
||||
// Always check error message path
|
||||
(void)elf.GetErrorMessage();
|
||||
}
|
||||
|
||||
// Cleanup temp file (memfd doesn't need cleanup)
|
||||
#ifdef __linux__
|
||||
if (g_memfd < 0)
|
||||
#endif
|
||||
{
|
||||
unlink(testFile.c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
Tests/Fuzzing/cmELFFuzzer.options
Normal file
10
Tests/Fuzzing/cmELFFuzzer.options
Normal file
@@ -0,0 +1,10 @@
|
||||
[libfuzzer]
|
||||
max_len = 524288
|
||||
timeout = 30
|
||||
rss_limit_mb = 1024
|
||||
|
||||
[afl]
|
||||
timeout = 30
|
||||
|
||||
[honggfuzz]
|
||||
timeout = 30
|
||||
Reference in New Issue
Block a user