summaryrefslogtreecommitdiff
path: root/fuzzer/parser-fuzzer.c
diff options
context:
space:
mode:
Diffstat (limited to 'fuzzer/parser-fuzzer.c')
-rw-r--r--fuzzer/parser-fuzzer.c132
1 files changed, 132 insertions, 0 deletions
diff --git a/fuzzer/parser-fuzzer.c b/fuzzer/parser-fuzzer.c
new file mode 100644
index 000000000000..d53a8b5f7aea
--- /dev/null
+++ b/fuzzer/parser-fuzzer.c
@@ -0,0 +1,132 @@
+/*
+ * parser-fuzzer.c
+ * parser fuzzing harness
+ *
+ * SPDX-License-Identifier: pkgconf
+ *
+ * Copyright (c) 2026 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+#include "alloc-inject.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+/* bound the number of injection rounds per input to keep executions cheap */
+#define ALLOC_FAIL_MAX 4096
+
+static int
+write_all(int fd, const uint8_t *data, size_t size)
+{
+ while (size > 0)
+ {
+ ssize_t n = write(fd, data, size);
+
+ if (n < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ data += n;
+ size -= n;
+ }
+
+ return 0;
+}
+
+static const char *
+environ_lookup_handler(const pkgconf_client_t *client, const char *key)
+{
+ (void) client;
+ (void) key;
+
+ return NULL;
+}
+
+static void
+run_once(pkgconf_client_t *client, const char *path)
+{
+ pkgconf_pkg_t *pkg = pkgconf_pkg_new_from_path(client, path, 0);
+ if (pkg == NULL)
+ return;
+
+ pkgconf_list_t cflags = PKGCONF_LIST_INITIALIZER;
+ pkgconf_list_t libs = PKGCONF_LIST_INITIALIZER;
+ pkgconf_buffer_t render = PKGCONF_BUFFER_INITIALIZER;
+
+ pkgconf_pkg_verify_graph(client, pkg, 2);
+
+ pkgconf_pkg_cflags(client, pkg, &cflags, 2);
+ pkgconf_pkg_libs(client, pkg, &libs, 2);
+
+ pkgconf_fragment_render_buf(&cflags, &render, true, NULL, ' ');
+ pkgconf_fragment_render_buf(&libs, &render, true, NULL, ' ');
+
+ pkgconf_buffer_finalize(&render);
+ pkgconf_fragment_free(&cflags);
+ pkgconf_fragment_free(&libs);
+ pkgconf_pkg_free(client, pkg);
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size == 0)
+ return 0;
+
+ pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
+ pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, environ_lookup_handler);
+ if (client == NULL)
+ return 0;
+
+ char path[] = "/tmp/pkgconf-fuzz-XXXXXX.pc";
+ int fd = mkstemps(path, 3); // keep ".pc"
+ if (fd < 0)
+ {
+ pkgconf_client_free(client);
+ return 0;
+ }
+
+ if (write_all(fd, data, size) != 0)
+ {
+ close(fd);
+ unlink(path);
+ pkgconf_client_free(client);
+ return 0;
+ }
+
+ close(fd);
+
+ /* baseline run with all allocations succeeding */
+ run_once(client, path);
+
+ /* then fail each allocation site reachable by this input, one at a time */
+ for (unsigned long i = 1; i <= ALLOC_FAIL_MAX; i++)
+ {
+ alloc_inject_arm(i);
+ run_once(client, path);
+ alloc_inject_disarm();
+
+ /* this input made fewer than i allocations; no point going further */
+ if (!alloc_inject_fired())
+ break;
+ }
+
+ unlink(path);
+ pkgconf_client_free(client);
+ pkgconf_cross_personality_deinit(pers);
+
+ return 0;
+}