xref: /freebsd/contrib/pkgconf/fuzzer/parser-fuzzer.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1 /*
2  * parser-fuzzer.c
3  * parser fuzzing harness
4  *
5  * SPDX-License-Identifier: pkgconf
6  *
7  * Copyright (c) 2026 pkgconf authors (see AUTHORS).
8  *
9  * Permission to use, copy, modify, and/or distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * This software is provided 'as is' and without any warranty, express or
14  * implied.  In no event shall the authors be liable for any damages arising
15  * from the use of this software.
16  */
17 
18 #include <libpkgconf/stdinc.h>
19 #include <libpkgconf/libpkgconf.h>
20 
21 #include "alloc-inject.h"
22 
23 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
24 
25 /* bound the number of injection rounds per input to keep executions cheap */
26 #define ALLOC_FAIL_MAX 4096
27 
28 static int
write_all(int fd,const uint8_t * data,size_t size)29 write_all(int fd, const uint8_t *data, size_t size)
30 {
31 	while (size > 0)
32 	{
33 		ssize_t n = write(fd, data, size);
34 
35 		if (n < 0)
36 		{
37 			if (errno == EINTR)
38 				continue;
39 			return -1;
40 		}
41 
42 		data += n;
43 		size -= n;
44 	}
45 
46 	return 0;
47 }
48 
49 static const char *
environ_lookup_handler(const pkgconf_client_t * client,const char * key)50 environ_lookup_handler(const pkgconf_client_t *client, const char *key)
51 {
52 	(void) client;
53 	(void) key;
54 
55 	return NULL;
56 }
57 
58 static void
run_once(pkgconf_client_t * client,const char * path)59 run_once(pkgconf_client_t *client, const char *path)
60 {
61 	pkgconf_pkg_t *pkg = pkgconf_pkg_new_from_path(client, path, 0);
62 	if (pkg == NULL)
63 		return;
64 
65 	pkgconf_list_t cflags = PKGCONF_LIST_INITIALIZER;
66 	pkgconf_list_t libs = PKGCONF_LIST_INITIALIZER;
67 	pkgconf_buffer_t render = PKGCONF_BUFFER_INITIALIZER;
68 
69 	pkgconf_pkg_verify_graph(client, pkg, 2);
70 
71 	pkgconf_pkg_cflags(client, pkg, &cflags, 2);
72 	pkgconf_pkg_libs(client, pkg, &libs, 2);
73 
74 	pkgconf_fragment_render_buf(&cflags, &render, true, NULL, ' ');
75 	pkgconf_fragment_render_buf(&libs, &render, true, NULL, ' ');
76 
77 	pkgconf_buffer_finalize(&render);
78 	pkgconf_fragment_free(&cflags);
79 	pkgconf_fragment_free(&libs);
80 	pkgconf_pkg_free(client, pkg);
81 }
82 
83 int
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)84 LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
85 {
86 	if (size == 0)
87 		return 0;
88 
89 	pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
90 	pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, environ_lookup_handler);
91 	if (client == NULL)
92 		return 0;
93 
94 	char path[] = "/tmp/pkgconf-fuzz-XXXXXX.pc";
95 	int fd = mkstemps(path, 3);  // keep ".pc"
96 	if (fd < 0)
97 	{
98 		pkgconf_client_free(client);
99 		return 0;
100 	}
101 
102 	if (write_all(fd, data, size) != 0)
103 	{
104 		close(fd);
105 		unlink(path);
106 		pkgconf_client_free(client);
107 		return 0;
108 	}
109 
110 	close(fd);
111 
112 	/* baseline run with all allocations succeeding */
113 	run_once(client, path);
114 
115 	/* then fail each allocation site reachable by this input, one at a time */
116 	for (unsigned long i = 1; i <= ALLOC_FAIL_MAX; i++)
117 	{
118 		alloc_inject_arm(i);
119 		run_once(client, path);
120 		alloc_inject_disarm();
121 
122 		/* this input made fewer than i allocations; no point going further */
123 		if (!alloc_inject_fired())
124 			break;
125 	}
126 
127 	unlink(path);
128 	pkgconf_client_free(client);
129 	pkgconf_cross_personality_deinit(pers);
130 
131 	return 0;
132 }
133