1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery * solver-fuzzer.c
3*592efe25SPierre Pronchery * dependency solver 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 /* the fuzzer input is split on NUL bytes into up to this many .pc files, named
29*592efe25SPierre Pronchery * a.pc, b.pc, ... so that Requires/Conflicts/Provides between them resolve.
30*592efe25SPierre Pronchery */
31*592efe25SPierre Pronchery #define UNIVERSE_MAX 4
32*592efe25SPierre Pronchery #define SOLVE_MAXDEPTH 10
33*592efe25SPierre Pronchery
34*592efe25SPierre Pronchery static const char universe_names[UNIVERSE_MAX] = { 'a', 'b', 'c', 'd' };
35*592efe25SPierre Pronchery
36*592efe25SPierre Pronchery static const char *
environ_lookup_handler(const pkgconf_client_t * client,const char * key)37*592efe25SPierre Pronchery environ_lookup_handler(const pkgconf_client_t *client, const char *key)
38*592efe25SPierre Pronchery {
39*592efe25SPierre Pronchery (void) client;
40*592efe25SPierre Pronchery (void) key;
41*592efe25SPierre Pronchery
42*592efe25SPierre Pronchery return NULL;
43*592efe25SPierre Pronchery }
44*592efe25SPierre Pronchery
45*592efe25SPierre Pronchery static int
write_all(int fd,const uint8_t * data,size_t size)46*592efe25SPierre Pronchery write_all(int fd, const uint8_t *data, size_t size)
47*592efe25SPierre Pronchery {
48*592efe25SPierre Pronchery while (size > 0)
49*592efe25SPierre Pronchery {
50*592efe25SPierre Pronchery ssize_t n = write(fd, data, size);
51*592efe25SPierre Pronchery
52*592efe25SPierre Pronchery if (n < 0)
53*592efe25SPierre Pronchery {
54*592efe25SPierre Pronchery if (errno == EINTR)
55*592efe25SPierre Pronchery continue;
56*592efe25SPierre Pronchery return -1;
57*592efe25SPierre Pronchery }
58*592efe25SPierre Pronchery
59*592efe25SPierre Pronchery data += n;
60*592efe25SPierre Pronchery size -= n;
61*592efe25SPierre Pronchery }
62*592efe25SPierre Pronchery
63*592efe25SPierre Pronchery return 0;
64*592efe25SPierre Pronchery }
65*592efe25SPierre Pronchery
66*592efe25SPierre Pronchery static void
write_pkg(const char * dir,char name,const uint8_t * data,size_t size)67*592efe25SPierre Pronchery write_pkg(const char *dir, char name, const uint8_t *data, size_t size)
68*592efe25SPierre Pronchery {
69*592efe25SPierre Pronchery char path[PKGCONF_ITEM_SIZE];
70*592efe25SPierre Pronchery int fd;
71*592efe25SPierre Pronchery
72*592efe25SPierre Pronchery snprintf(path, sizeof path, "%s/%c.pc", dir, name);
73*592efe25SPierre Pronchery
74*592efe25SPierre Pronchery fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
75*592efe25SPierre Pronchery if (fd < 0)
76*592efe25SPierre Pronchery return;
77*592efe25SPierre Pronchery
78*592efe25SPierre Pronchery write_all(fd, data, size);
79*592efe25SPierre Pronchery close(fd);
80*592efe25SPierre Pronchery }
81*592efe25SPierre Pronchery
82*592efe25SPierre Pronchery static void
write_universe(const char * dir,const uint8_t * data,size_t size)83*592efe25SPierre Pronchery write_universe(const char *dir, const uint8_t *data, size_t size)
84*592efe25SPierre Pronchery {
85*592efe25SPierre Pronchery size_t start = 0, idx = 0;
86*592efe25SPierre Pronchery
87*592efe25SPierre Pronchery for (size_t i = 0; i < size && idx < UNIVERSE_MAX; i++)
88*592efe25SPierre Pronchery {
89*592efe25SPierre Pronchery if (data[i] == 0x00)
90*592efe25SPierre Pronchery {
91*592efe25SPierre Pronchery write_pkg(dir, universe_names[idx++], data + start, i - start);
92*592efe25SPierre Pronchery start = i + 1;
93*592efe25SPierre Pronchery }
94*592efe25SPierre Pronchery }
95*592efe25SPierre Pronchery
96*592efe25SPierre Pronchery if (idx < UNIVERSE_MAX)
97*592efe25SPierre Pronchery write_pkg(dir, universe_names[idx], data + start, size - start);
98*592efe25SPierre Pronchery }
99*592efe25SPierre Pronchery
100*592efe25SPierre Pronchery static void
cleanup_universe(const char * dir)101*592efe25SPierre Pronchery cleanup_universe(const char *dir)
102*592efe25SPierre Pronchery {
103*592efe25SPierre Pronchery char path[PKGCONF_ITEM_SIZE];
104*592efe25SPierre Pronchery
105*592efe25SPierre Pronchery for (size_t i = 0; i < UNIVERSE_MAX; i++)
106*592efe25SPierre Pronchery {
107*592efe25SPierre Pronchery snprintf(path, sizeof path, "%s/%c.pc", dir, universe_names[i]);
108*592efe25SPierre Pronchery unlink(path);
109*592efe25SPierre Pronchery }
110*592efe25SPierre Pronchery
111*592efe25SPierre Pronchery rmdir(dir);
112*592efe25SPierre Pronchery }
113*592efe25SPierre Pronchery
114*592efe25SPierre Pronchery static void
run_solve(const pkgconf_cross_personality_t * pers,const char * dir)115*592efe25SPierre Pronchery run_solve(const pkgconf_cross_personality_t *pers, const char *dir)
116*592efe25SPierre Pronchery {
117*592efe25SPierre Pronchery pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, environ_lookup_handler);
118*592efe25SPierre Pronchery if (client == NULL)
119*592efe25SPierre Pronchery return;
120*592efe25SPierre Pronchery
121*592efe25SPierre Pronchery pkgconf_path_add(dir, &client->dir_list, false);
122*592efe25SPierre Pronchery
123*592efe25SPierre Pronchery pkgconf_pkg_t world = {
124*592efe25SPierre Pronchery .id = "virtual:world",
125*592efe25SPierre Pronchery .realname = "virtual world package",
126*592efe25SPierre Pronchery .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
127*592efe25SPierre Pronchery };
128*592efe25SPierre Pronchery pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER;
129*592efe25SPierre Pronchery
130*592efe25SPierre Pronchery pkgconf_queue_push(&pkgq, "a");
131*592efe25SPierre Pronchery
132*592efe25SPierre Pronchery if (pkgconf_queue_solve(client, &pkgq, &world, SOLVE_MAXDEPTH))
133*592efe25SPierre Pronchery {
134*592efe25SPierre Pronchery pkgconf_list_t cflags = PKGCONF_LIST_INITIALIZER;
135*592efe25SPierre Pronchery pkgconf_list_t libs = PKGCONF_LIST_INITIALIZER;
136*592efe25SPierre Pronchery pkgconf_buffer_t render = PKGCONF_BUFFER_INITIALIZER;
137*592efe25SPierre Pronchery
138*592efe25SPierre Pronchery pkgconf_pkg_cflags(client, &world, &cflags, SOLVE_MAXDEPTH);
139*592efe25SPierre Pronchery pkgconf_pkg_libs(client, &world, &libs, SOLVE_MAXDEPTH);
140*592efe25SPierre Pronchery
141*592efe25SPierre Pronchery pkgconf_fragment_render_buf(&cflags, &render, true, NULL, ' ');
142*592efe25SPierre Pronchery pkgconf_fragment_render_buf(&libs, &render, true, NULL, ' ');
143*592efe25SPierre Pronchery
144*592efe25SPierre Pronchery pkgconf_buffer_finalize(&render);
145*592efe25SPierre Pronchery pkgconf_fragment_free(&cflags);
146*592efe25SPierre Pronchery pkgconf_fragment_free(&libs);
147*592efe25SPierre Pronchery }
148*592efe25SPierre Pronchery
149*592efe25SPierre Pronchery pkgconf_solution_free(client, &world);
150*592efe25SPierre Pronchery pkgconf_queue_free(&pkgq);
151*592efe25SPierre Pronchery pkgconf_client_free(client);
152*592efe25SPierre Pronchery }
153*592efe25SPierre Pronchery
154*592efe25SPierre Pronchery int
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)155*592efe25SPierre Pronchery LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
156*592efe25SPierre Pronchery {
157*592efe25SPierre Pronchery if (size == 0)
158*592efe25SPierre Pronchery return 0;
159*592efe25SPierre Pronchery
160*592efe25SPierre Pronchery char dir[] = "/tmp/pkgconf-fuzz-univ-XXXXXX";
161*592efe25SPierre Pronchery if (mkdtemp(dir) == NULL)
162*592efe25SPierre Pronchery return 0;
163*592efe25SPierre Pronchery
164*592efe25SPierre Pronchery write_universe(dir, data, size);
165*592efe25SPierre Pronchery
166*592efe25SPierre Pronchery pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
167*592efe25SPierre Pronchery
168*592efe25SPierre Pronchery /* baseline run with all allocations succeeding */
169*592efe25SPierre Pronchery run_solve(pers, dir);
170*592efe25SPierre Pronchery
171*592efe25SPierre Pronchery /* then fail each allocation site reachable by this input, one at a time */
172*592efe25SPierre Pronchery for (unsigned long i = 1; i <= ALLOC_FAIL_MAX; i++)
173*592efe25SPierre Pronchery {
174*592efe25SPierre Pronchery alloc_inject_arm(i);
175*592efe25SPierre Pronchery run_solve(pers, dir);
176*592efe25SPierre Pronchery alloc_inject_disarm();
177*592efe25SPierre Pronchery
178*592efe25SPierre Pronchery if (!alloc_inject_fired())
179*592efe25SPierre Pronchery break;
180*592efe25SPierre Pronchery }
181*592efe25SPierre Pronchery
182*592efe25SPierre Pronchery pkgconf_cross_personality_deinit(pers);
183*592efe25SPierre Pronchery cleanup_universe(dir);
184*592efe25SPierre Pronchery
185*592efe25SPierre Pronchery return 0;
186*592efe25SPierre Pronchery }
187