blob: 64651fd4c6ecffc0bcd31ee9609fd17a696ca932 [file] [log] [blame] [edit]
/* 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;
}