1 /*
2 * replay.c
3 * Standalone driver that replays corpus files through a libFuzzer
4 * LLVMFuzzerTestOneInput entry point, without libFuzzer.
5 *
6 * This lets the fuzz targets (and the allocator fault injection they drive via
7 * alloc-inject) run deterministically over their seed corpus under an ordinary
8 * compiler -- in particular a coverage build -- so their OOM-path coverage is
9 * captured by codecov, which the libFuzzer/clang fuzz job does not provide.
10 *
11 * SPDX-License-Identifier: pkgconf
12 *
13 * Copyright (c) 2026 pkgconf authors (see AUTHORS).
14 *
15 * Permission to use, copy, modify, and/or distribute this software for any
16 * purpose with or without fee is hereby granted, provided that the above
17 * copyright notice and this permission notice appear in all copies.
18 *
19 * This software is provided 'as is' and without any warranty, express or
20 * implied. In no event shall the authors be liable for any damages arising
21 * from the use of this software.
22 */
23
24 #include <libpkgconf/stdinc.h>
25 #include <sys/stat.h>
26 #include <dirent.h>
27
28 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
29
30 static void
replay_file(const char * path)31 replay_file(const char *path)
32 {
33 FILE *f = fopen(path, "rb");
34 if (f == NULL)
35 return;
36
37 if (fseek(f, 0, SEEK_END) != 0)
38 {
39 fclose(f);
40 return;
41 }
42
43 long len = ftell(f);
44 rewind(f);
45
46 if (len < 0)
47 {
48 fclose(f);
49 return;
50 }
51
52 uint8_t *buf = malloc((size_t) len + 1);
53 if (buf == NULL)
54 {
55 fclose(f);
56 return;
57 }
58
59 size_t got = fread(buf, 1, (size_t) len, f);
60 fclose(f);
61
62 LLVMFuzzerTestOneInput(buf, got);
63 free(buf);
64 }
65
66 static void
replay_path(const char * path)67 replay_path(const char *path)
68 {
69 struct stat st;
70 if (stat(path, &st) != 0)
71 return;
72
73 if (!S_ISDIR(st.st_mode))
74 {
75 replay_file(path);
76 return;
77 }
78
79 DIR *dir = opendir(path);
80 if (dir == NULL)
81 return;
82
83 struct dirent *ent;
84 while ((ent = readdir(dir)) != NULL)
85 {
86 if (ent->d_name[0] == '.')
87 continue;
88
89 char child[4096];
90 snprintf(child, sizeof child, "%s/%s", path, ent->d_name);
91 replay_path(child);
92 }
93
94 closedir(dir);
95 }
96
97 int
main(int argc,char * argv[])98 main(int argc, char *argv[])
99 {
100 for (int i = 1; i < argc; i++)
101 replay_path(argv[i]);
102
103 return 0;
104 }
105