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