Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Tests/Fuzzing/cmELFFuzzer.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
4
/*
5
 * Fuzzer for CMake's ELF parser
6
 *
7
 * CMake parses ELF files to extract RPATH, RUNPATH, and SONAME information.
8
 * Malformed ELF files from untrusted sources could trigger vulnerabilities.
9
 *
10
 * Coverage targets:
11
 * - ELF header parsing (32-bit and 64-bit)
12
 * - Section header parsing
13
 * - Dynamic section parsing
14
 * - String table extraction
15
 * - RPATH/RUNPATH/SONAME extraction
16
 *
17
 * Performance notes:
18
 * - Uses memfd_create on Linux for memory-backed file I/O
19
 * - Falls back to temp files on other platforms
20
 */
21
22
#include <cstddef>
23
#include <cstdint>
24
#include <cstdio>
25
#include <cstdlib>
26
#include <sstream>
27
#include <string>
28
29
#include <unistd.h>
30
31
#include "cmELF.h"
32
33
#ifdef __linux__
34
#  include <sys/mman.h>
35
#  ifndef MFD_CLOEXEC
36
#    define MFD_CLOEXEC 0x0001U
37
#  endif
38
#endif
39
40
// ELF files can be large, but we limit to avoid timeouts
41
static constexpr size_t kMinInputSize = 16;         // Minimum ELF header
42
static constexpr size_t kMaxInputSize = 512 * 1024; // 512KB
43
44
static int g_memfd = -1;
45
static std::string g_memfdPath;
46
47
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
48
2
{
49
2
  (void)argc;
50
2
  (void)argv;
51
52
2
#ifdef __linux__
53
2
  g_memfd = memfd_create("cmake_fuzz_elf", MFD_CLOEXEC);
54
2
  if (g_memfd >= 0) {
55
2
    g_memfdPath = "/proc/self/fd/" + std::to_string(g_memfd);
56
2
  }
57
2
#endif
58
2
  return 0;
59
2
}
60
61
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
62
1.00k
{
63
  // ELF files need minimum header size
64
1.00k
  if (size < kMinInputSize || size > kMaxInputSize) {
65
6
    return 0;
66
6
  }
67
68
998
  std::string testFile;
69
70
998
#ifdef __linux__
71
998
  if (g_memfd >= 0) {
72
    // Use memfd for better performance
73
998
    ftruncate(g_memfd, 0);
74
998
    lseek(g_memfd, 0, SEEK_SET);
75
998
    if (write(g_memfd, data, size) != static_cast<ssize_t>(size)) {
76
0
      return 0;
77
0
    }
78
998
    testFile = g_memfdPath;
79
998
  } else
80
0
#endif
81
0
  {
82
    // Fallback to temp file
83
0
    char tmpFile[] = "/tmp/fuzz_elf_XXXXXX";
84
0
    int fd = mkstemp(tmpFile);
85
0
    if (fd < 0) {
86
0
      return 0;
87
0
    }
88
0
    if (write(fd, data, size) != static_cast<ssize_t>(size)) {
89
0
      close(fd);
90
0
      unlink(tmpFile);
91
0
      return 0;
92
0
    }
93
0
    close(fd);
94
0
    testFile = tmpFile;
95
0
  }
96
97
  // Parse the ELF file
98
998
  {
99
998
    cmELF elf(testFile.c_str());
100
101
    // Check validity
102
998
    if (elf) {
103
      // Exercise all the parsing functions
104
611
      (void)elf.GetFileType();
105
611
      (void)elf.GetMachine();
106
611
      (void)elf.GetNumberOfSections();
107
611
      (void)elf.HasDynamicSection();
108
611
      (void)elf.IsMIPS();
109
110
      // Try to get string entries
111
611
      std::string soname;
112
611
      elf.GetSOName(soname);
113
611
      (void)elf.GetSOName();
114
611
      (void)elf.GetRPath();
115
611
      (void)elf.GetRunPath();
116
117
      // Get dynamic entries
118
611
      auto entries = elf.GetDynamicEntries();
119
611
      if (!entries.empty()) {
120
100
        auto encoded = elf.EncodeDynamicEntries(entries);
121
100
        (void)encoded;
122
123
        // Get positions (limit iterations to avoid timeout)
124
6.61k
        for (size_t i = 0; i < entries.size() && i < 100; ++i) {
125
6.51k
          (void)elf.GetDynamicEntryPosition(static_cast<int>(i));
126
6.51k
        }
127
100
      }
128
129
      // Print info to exercise that code path
130
611
      std::ostringstream oss;
131
611
      elf.PrintInfo(oss);
132
611
    }
133
134
    // Always check error message path
135
998
    (void)elf.GetErrorMessage();
136
998
  }
137
138
  // Cleanup temp file (memfd doesn't need cleanup)
139
998
#ifdef __linux__
140
998
  if (g_memfd < 0)
141
0
#endif
142
0
  {
143
0
    unlink(testFile.c_str());
144
0
  }
145
146
998
  return 0;
147
998
}