xref: /freebsd/contrib/pkgconf/fuzzer/replay.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
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