mirror of
https://github.com/Kitware/CMake.git
synced 2026-06-24 08:47:59 +00:00
148 lines
3.4 KiB
C++
148 lines
3.4 KiB
C++
/* 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;
|
|
}
|