summaryrefslogtreecommitdiffstats
path: root/src/fuzz
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 13:00:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 13:00:47 +0000
commit2cb7e0aaedad73b076ea18c6900b0e86c5760d79 (patch)
treeda68ca54bb79f4080079bf0828acda937593a4e1 /src/fuzz
parentInitial commit. (diff)
downloadsystemd-2cb7e0aaedad73b076ea18c6900b0e86c5760d79.tar.xz
systemd-2cb7e0aaedad73b076ea18c6900b0e86c5760d79.zip
Adding upstream version 247.3.upstream/247.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/fuzz/fuzz-bus-label.c18
-rw-r--r--src/fuzz/fuzz-bus-message.c45
-rw-r--r--src/fuzz/fuzz-calendarspec.c24
-rw-r--r--src/fuzz/fuzz-catalog.c26
-rw-r--r--src/fuzz/fuzz-compress.c80
-rw-r--r--src/fuzz/fuzz-dhcp-server.c56
-rw-r--r--src/fuzz/fuzz-dhcp-server.options2
-rw-r--r--src/fuzz/fuzz-dhcp6-client.c62
-rw-r--r--src/fuzz/fuzz-dhcp6-client.options2
-rw-r--r--src/fuzz/fuzz-dns-packet.c25
-rw-r--r--src/fuzz/fuzz-dns-packet.options2
-rw-r--r--src/fuzz/fuzz-env-file.c32
-rw-r--r--src/fuzz/fuzz-env-file.options2
-rw-r--r--src/fuzz/fuzz-fido-id-desc.dict6
-rw-r--r--src/fuzz/fuzz-hostname-util.c27
-rw-r--r--src/fuzz/fuzz-journal-remote.c78
-rw-r--r--src/fuzz/fuzz-journal-remote.options2
-rw-r--r--src/fuzz/fuzz-journald-audit.c15
-rw-r--r--src/fuzz/fuzz-journald-kmsg.c18
-rw-r--r--src/fuzz/fuzz-journald-native-fd.c47
-rw-r--r--src/fuzz/fuzz-journald-native.c10
-rw-r--r--src/fuzz/fuzz-journald-stream.c37
-rw-r--r--src/fuzz/fuzz-journald-stream.options2
-rw-r--r--src/fuzz/fuzz-journald-syslog.c10
-rw-r--r--src/fuzz/fuzz-journald.c46
-rw-r--r--src/fuzz/fuzz-journald.h12
-rw-r--r--src/fuzz/fuzz-json.c31
-rw-r--r--src/fuzz/fuzz-lldp.c43
-rw-r--r--src/fuzz/fuzz-lldp.options2
-rw-r--r--src/fuzz/fuzz-main.c45
-rw-r--r--src/fuzz/fuzz-ndisc-rs.c61
-rw-r--r--src/fuzz/fuzz-ndisc-rs.options2
-rw-r--r--src/fuzz/fuzz-nspawn-oci.c27
-rw-r--r--src/fuzz/fuzz-nspawn-oci.options2
-rw-r--r--src/fuzz/fuzz-nspawn-settings.c27
-rw-r--r--src/fuzz/fuzz-nspawn-settings.options2
-rw-r--r--src/fuzz/fuzz-time-util.c28
-rw-r--r--src/fuzz/fuzz-udev-database.c26
-rw-r--r--src/fuzz/fuzz-udev-rule-parse-value.c31
-rw-r--r--src/fuzz/fuzz-udev-rules.c36
-rw-r--r--src/fuzz/fuzz-udev-rules.options2
-rw-r--r--src/fuzz/fuzz-unit-file.c88
-rw-r--r--src/fuzz/fuzz-unit-file.options2
-rw-r--r--src/fuzz/fuzz-varlink.c131
-rw-r--r--src/fuzz/fuzz-xdg-desktop.c36
-rw-r--r--src/fuzz/fuzz.h8
-rw-r--r--src/fuzz/meson.build159
47 files changed, 1475 insertions, 0 deletions
diff --git a/src/fuzz/fuzz-bus-label.c b/src/fuzz/fuzz-bus-label.c
new file mode 100644
index 0000000..93bac9a
--- /dev/null
+++ b/src/fuzz/fuzz-bus-label.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+
+#include "alloc-util.h"
+#include "bus-label.h"
+#include "fuzz.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *unescaped = NULL, *escaped = NULL;
+
+ unescaped = bus_label_unescape_n((const char*)data, size);
+ assert_se(unescaped != NULL);
+ escaped = bus_label_escape(unescaped);
+ assert_se(escaped != NULL);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-bus-message.c b/src/fuzz/fuzz-bus-message.c
new file mode 100644
index 0000000..af3dbf4
--- /dev/null
+++ b/src/fuzz/fuzz-bus-message.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "bus-dump.h"
+#include "bus-message.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fuzz.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *out = NULL; /* out should be freed after g */
+ size_t out_size;
+ _cleanup_fclose_ FILE *g = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_free_ void *buffer = NULL;
+ int r;
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ r = sd_bus_new(&bus);
+ assert_se(r >= 0);
+
+ assert_se(buffer = memdup(data, size));
+
+ r = bus_message_from_malloc(bus, buffer, size, NULL, 0, NULL, &m);
+ if (r == -EBADMSG)
+ return 0;
+ assert_se(r >= 0);
+ TAKE_PTR(buffer);
+
+ if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
+ assert_se(g = open_memstream_unlocked(&out, &out_size));
+
+ sd_bus_message_dump(m, g ?: stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+
+ r = sd_bus_message_rewind(m, true);
+ assert_se(r >= 0);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-calendarspec.c b/src/fuzz/fuzz-calendarspec.c
new file mode 100644
index 0000000..8080172
--- /dev/null
+++ b/src/fuzz/fuzz-calendarspec.c
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "calendarspec.h"
+#include "fd-util.h"
+#include "fuzz.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(calendar_spec_freep) CalendarSpec *cspec = NULL;
+ _cleanup_free_ char *str = NULL, *p = NULL;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ str = memdup_suffix0(data, size);
+
+ if (calendar_spec_from_string(str, &cspec) >= 0) {
+ (void) calendar_spec_valid(cspec);
+ (void) calendar_spec_normalize(cspec);
+ (void) calendar_spec_to_string(cspec, &p);
+ }
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-catalog.c b/src/fuzz/fuzz-catalog.c
new file mode 100644
index 0000000..f013455
--- /dev/null
+++ b/src/fuzz/fuzz-catalog.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "catalog.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-catalog.XXXXXX";
+ _cleanup_close_ int fd = -1;
+ _cleanup_ordered_hashmap_free_free_free_ OrderedHashmap *h = NULL;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(h = ordered_hashmap_new(&catalog_hash_ops));
+
+ fd = mkostemp_safe(name);
+ assert_se(fd >= 0);
+ assert_se(write(fd, data, size) == (ssize_t) size);
+
+ (void) catalog_import_file(h, name);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-compress.c b/src/fuzz/fuzz-compress.c
new file mode 100644
index 0000000..f94fd06
--- /dev/null
+++ b/src/fuzz/fuzz-compress.c
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+
+#include "alloc-util.h"
+#include "compress.h"
+#include "fuzz.h"
+
+static int compress(int alg,
+ const void *src, uint64_t src_size,
+ void *dst, size_t dst_alloc_size, size_t *dst_size) {
+
+ if (alg == OBJECT_COMPRESSED_LZ4)
+ return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
+ if (alg == OBJECT_COMPRESSED_XZ)
+ return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
+ return -EOPNOTSUPP;
+}
+
+typedef struct header {
+ uint32_t alg:2; /* We have only two compression algorithms so far, but we might add
+ * more in the future. Let's make this a bit wider so our fuzzer
+ * cases remain stable in the future. */
+ uint32_t sw_len;
+ uint32_t sw_alloc;
+ uint32_t reserved[3]; /* Extra space to keep fuzz cases stable in case we need to
+ * add stuff in the future. */
+ uint8_t data[];
+} header;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ void *buf = NULL, *buf2 = NULL;
+ int r;
+
+ if (size < offsetof(header, data) + 1)
+ return 0;
+
+ const header *h = (struct header*) data;
+ const size_t data_len = size - offsetof(header, data);
+
+ int alg = h->alg;
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ log_info("Using compression %s, data size=%zu",
+ object_compressed_to_string(alg) ?: "(none)",
+ data_len);
+
+ buf = malloc(MAX(size, 128u)); /* Make the buffer a bit larger for very small data */
+ if (!buf) {
+ log_oom();
+ return 0;
+ }
+
+ size_t csize;
+ r = compress(alg, h->data, data_len, buf, size, &csize);
+ if (r < 0) {
+ log_error_errno(r, "Compression failed: %m");
+ return 0;
+ }
+
+ log_debug("Compressed %zu bytes to → %zu bytes", data_len, csize);
+
+ size_t sw_alloc = MAX(h->sw_alloc, 1u);
+ buf2 = malloc(sw_alloc);
+ if (!buf) {
+ log_oom();
+ return 0;
+ }
+
+ size_t sw_len = MIN(data_len - 1, h->sw_len);
+
+ r = decompress_startswith(alg, buf, csize, &buf2, &sw_alloc, h->data, sw_len, h->data[sw_len]);
+ assert_se(r > 0);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dhcp-server.c b/src/fuzz/fuzz-dhcp-server.c
new file mode 100644
index 0000000..c854d92
--- /dev/null
+++ b/src/fuzz/fuzz-dhcp-server.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "fuzz.h"
+
+#include "sd-dhcp-server.c"
+
+/* stub out network so that the server doesn't send */
+ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
+ return len;
+}
+
+ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
+ struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
+ static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
+ uint8_t *client_id;
+ DHCPLease *lease;
+ int pool_offset;
+
+ if (size < sizeof(DHCPMessage))
+ return 0;
+
+ assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+ server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
+ assert_se(server->fd >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
+
+ /* add a lease to the pool to expose additional code paths */
+ client_id = malloc(2);
+ assert_se(client_id);
+ client_id[0] = 2;
+ client_id[1] = 2;
+ lease = new0(DHCPLease, 1);
+ assert_se(lease);
+ lease->client_id.length = 2;
+ lease->client_id.data = client_id;
+ lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2));
+ lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
+ lease->expiration = UINT64_MAX;
+ memcpy(lease->chaddr, chaddr, 16);
+ pool_offset = get_pool_offset(server, lease->address);
+ server->bound_leases[pool_offset] = lease;
+ assert_se(hashmap_put(server->leases_by_client_id, &lease->client_id, lease) >= 0);
+
+ (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dhcp-server.options b/src/fuzz/fuzz-dhcp-server.options
new file mode 100644
index 0000000..5c330e5
--- /dev/null
+++ b/src/fuzz/fuzz-dhcp-server.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 600
diff --git a/src/fuzz/fuzz-dhcp6-client.c b/src/fuzz/fuzz-dhcp6-client.c
new file mode 100644
index 0000000..e5e70dd
--- /dev/null
+++ b/src/fuzz/fuzz-dhcp6-client.c
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "sd-dhcp6-client.h"
+#include "sd-event.h"
+
+#include "dhcp6-internal.h"
+#include "dhcp6-protocol.h"
+#include "fd-util.h"
+#include "fuzz.h"
+
+static int test_dhcp_fd[2] = { -1, -1 };
+
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
+ const void *packet, size_t len) {
+ return len;
+}
+
+int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
+ return test_dhcp_fd[0];
+}
+
+static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) {
+ _cleanup_(sd_event_unrefp) sd_event *e;
+ _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_dhcp6_client_new(&client) >= 0);
+ assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+ assert_se(sd_dhcp6_client_set_information_request(client, is_information_request_enabled) == 0);
+
+ assert_se(sd_dhcp6_client_start(client) >= 0);
+
+ if (size >= sizeof(DHCP6Message))
+ assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0);
+
+ assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size);
+
+ sd_event_run(e, (uint64_t) -1);
+
+ assert_se(sd_dhcp6_client_stop(client) >= 0);
+
+ test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size > 65536)
+ return 0;
+
+ /* This triggers client_receive_advertise */
+ fuzz_client(data, size, false);
+
+ /* This triggers client_receive_reply */
+ fuzz_client(data, size, true);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dhcp6-client.options b/src/fuzz/fuzz-dhcp6-client.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-dhcp6-client.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-dns-packet.c b/src/fuzz/fuzz-dns-packet.c
new file mode 100644
index 0000000..b9a0aa1
--- /dev/null
+++ b/src/fuzz/fuzz-dns-packet.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fuzz.h"
+#include "memory-util.h"
+#include "resolved-dns-packet.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
+ if (size > DNS_PACKET_SIZE_MAX)
+ return 0;
+
+ assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
+ p->size = 0; /* by default append starts after the header, undo that */
+ assert_se(dns_packet_append_blob(p, data, size, NULL) >= 0);
+ if (size < DNS_PACKET_HEADER_SIZE) {
+ /* make sure we pad the packet back up to the minimum header size */
+ assert_se(p->allocated >= DNS_PACKET_HEADER_SIZE);
+ memzero(DNS_PACKET_DATA(p) + size, DNS_PACKET_HEADER_SIZE - size);
+ p->size = DNS_PACKET_HEADER_SIZE;
+ }
+ (void) dns_packet_extract(p);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-dns-packet.options b/src/fuzz/fuzz-dns-packet.options
new file mode 100644
index 0000000..0824b19
--- /dev/null
+++ b/src/fuzz/fuzz-dns-packet.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65535
diff --git a/src/fuzz/fuzz-env-file.c b/src/fuzz/fuzz-env-file.c
new file mode 100644
index 0000000..e0dac26
--- /dev/null
+++ b/src/fuzz/fuzz-env-file.c
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "env-file.h"
+#include "fileio.h"
+#include "fd-util.h"
+#include "fuzz.h"
+#include "strv.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **rl = NULL, **rlp = NULL;
+
+ if (size == 0 || size > 65535)
+ return 0;
+
+ f = fmemopen_unlocked((char*) data, size, "re");
+ assert_se(f);
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ (void) load_env_file(f, NULL, &rl);
+ assert_se(fseek(f, 0, SEEK_SET) == 0);
+ (void) load_env_file_pairs(f, NULL, &rlp);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-env-file.options b/src/fuzz/fuzz-env-file.options
new file mode 100644
index 0000000..0824b19
--- /dev/null
+++ b/src/fuzz/fuzz-env-file.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65535
diff --git a/src/fuzz/fuzz-fido-id-desc.dict b/src/fuzz/fuzz-fido-id-desc.dict
new file mode 100644
index 0000000..d2d2679
--- /dev/null
+++ b/src/fuzz/fuzz-fido-id-desc.dict
@@ -0,0 +1,6 @@
+"\xfe"
+"\x00"
+"\x01"
+"\xf1"
+"\xd0"
+"\xf1\xd0\x00\x01"
diff --git a/src/fuzz/fuzz-hostname-util.c b/src/fuzz/fuzz-hostname-util.c
new file mode 100644
index 0000000..0a81e74
--- /dev/null
+++ b/src/fuzz/fuzz-hostname-util.c
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fuzz.h"
+#include "hostname-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *ret = NULL;
+
+ if (size == 0)
+ return 0;
+
+ f = fmemopen_unlocked((char*) data, size, "re");
+ assert_se(f);
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ (void) read_etc_hostname_stream(f, &ret);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journal-remote.c b/src/fuzz/fuzz-journal-remote.c
new file mode 100644
index 0000000..9adbd43
--- /dev/null
+++ b/src/fuzz/fuzz-journal-remote.c
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fuzz.h"
+
+#include <sys/mman.h>
+
+#include "sd-journal.h"
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "journal-remote.h"
+#include "logs-show.h"
+#include "memfd-util.h"
+#include "strv.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_fclose_ FILE *dev_null = NULL;
+ RemoteServer s = {};
+ char name[] = "/tmp/fuzz-journal-remote.XXXXXX.journal";
+ void *mem;
+ int fdin; /* will be closed by journal_remote handler after EOF */
+ _cleanup_close_ int fdout = -1;
+ sd_journal *j;
+ OutputMode mode;
+ int r;
+
+ if (size <= 2)
+ return 0;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se((fdin = memfd_new_and_map("fuzz-journal-remote", size, &mem)) >= 0);
+ memcpy(mem, data, size);
+ assert_se(munmap(mem, size) == 0);
+
+ fdout = mkostemps(name, STRLEN(".journal"), O_CLOEXEC);
+ assert_se(fdout >= 0);
+
+ /* In */
+
+ assert_se(journal_remote_server_init(&s, name, JOURNAL_WRITE_SPLIT_NONE, false, false) >= 0);
+
+ assert_se(journal_remote_add_source(&s, fdin, (char*) "fuzz-data", false) > 0);
+
+ while (s.active) {
+ r = journal_remote_handle_raw_source(NULL, fdin, 0, &s);
+ assert_se(r >= 0);
+ }
+
+ journal_remote_server_destroy(&s);
+ assert_se(close(fdin) < 0 && errno == EBADF); /* Check that the fd is closed already */
+
+ /* Out */
+
+ r = sd_journal_open_files(&j, (const char**) STRV_MAKE(name), 0);
+ assert_se(r >= 0);
+
+ if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
+ assert_se(dev_null = fopen("/dev/null", "we"));
+
+ for (mode = 0; mode < _OUTPUT_MODE_MAX; mode++) {
+ if (!dev_null)
+ log_info("/* %s */", output_mode_to_string(mode));
+ r = show_journal(dev_null ?: stdout, j, mode, 0, 0, -1, 0, NULL);
+ assert_se(r >= 0);
+
+ r = sd_journal_seek_head(j);
+ assert_se(r >= 0);
+ }
+
+ sd_journal_close(j);
+ unlink(name);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journal-remote.options b/src/fuzz/fuzz-journal-remote.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-journal-remote.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-journald-audit.c b/src/fuzz/fuzz-journald-audit.c
new file mode 100644
index 0000000..6e8e180
--- /dev/null
+++ b/src/fuzz/fuzz-journald-audit.c
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-audit.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+
+ dummy_server_init(&s, data, size);
+ process_audit_string(&s, 0, s.buffer, size);
+ server_done(&s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-kmsg.c b/src/fuzz/fuzz-journald-kmsg.c
new file mode 100644
index 0000000..1b423d5
--- /dev/null
+++ b/src/fuzz/fuzz-journald-kmsg.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-kmsg.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+
+ if (size == 0)
+ return 0;
+
+ dummy_server_init(&s, data, size);
+ dev_kmsg_record(&s, s.buffer, size);
+ server_done(&s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-native-fd.c b/src/fuzz/fuzz-journald-native-fd.c
new file mode 100644
index 0000000..fcfc5df
--- /dev/null
+++ b/src/fuzz/fuzz-journald-native-fd.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz-journald.h"
+#include "fuzz.h"
+#include "journald-native.h"
+#include "memfd-util.h"
+#include "process-util.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+ _cleanup_close_ int sealed_fd = -1, unsealed_fd = -1;
+ _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-journald-native-fd.XXXXXX";
+ char *label = NULL;
+ size_t label_len = 0;
+ struct ucred ucred;
+ struct timeval *tv = NULL;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ dummy_server_init(&s, NULL, 0);
+
+ sealed_fd = memfd_new(NULL);
+ assert_se(sealed_fd >= 0);
+ assert_se(write(sealed_fd, data, size) == (ssize_t) size);
+ assert_se(memfd_set_sealed(sealed_fd) >= 0);
+ assert_se(lseek(sealed_fd, 0, SEEK_SET) == 0);
+ ucred = (struct ucred) {
+ .pid = getpid_cached(),
+ .uid = geteuid(),
+ .gid = getegid(),
+ };
+ server_process_native_file(&s, sealed_fd, &ucred, tv, label, label_len);
+
+ unsealed_fd = mkostemp_safe(name);
+ assert_se(unsealed_fd >= 0);
+ assert_se(write(unsealed_fd, data, size) == (ssize_t) size);
+ assert_se(lseek(unsealed_fd, 0, SEEK_SET) == 0);
+ server_process_native_file(&s, unsealed_fd, &ucred, tv, label, label_len);
+
+ server_done(&s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-native.c b/src/fuzz/fuzz-journald-native.c
new file mode 100644
index 0000000..6531c4f
--- /dev/null
+++ b/src/fuzz/fuzz-journald-native.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-native.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ fuzz_journald_processing_function(data, size, server_process_native_message);
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-stream.c b/src/fuzz/fuzz-journald-stream.c
new file mode 100644
index 0000000..038b335
--- /dev/null
+++ b/src/fuzz/fuzz-journald-stream.c
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/sockios.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-stream.h"
+
+static int stream_fds[2] = { -1, -1 };
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ Server s;
+ StdoutStream *stream;
+ int v;
+
+ if (size == 0 || size > 65536)
+ return 0;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, stream_fds) >= 0);
+ dummy_server_init(&s, NULL, 0);
+ assert_se(stdout_stream_install(&s, stream_fds[0], &stream) >= 0);
+ assert_se(write(stream_fds[1], data, size) == (ssize_t) size);
+ while (ioctl(stream_fds[0], SIOCINQ, &v) == 0 && v)
+ sd_event_run(s.event, (uint64_t) -1);
+ if (s.n_stdout_streams)
+ stdout_stream_destroy(stream);
+ server_done(&s);
+ stream_fds[1] = safe_close(stream_fds[1]);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald-stream.options b/src/fuzz/fuzz-journald-stream.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-journald-stream.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-journald-syslog.c b/src/fuzz/fuzz-journald-syslog.c
new file mode 100644
index 0000000..72ec610
--- /dev/null
+++ b/src/fuzz/fuzz-journald-syslog.c
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fuzz.h"
+#include "fuzz-journald.h"
+#include "journald-syslog.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ fuzz_journald_processing_function(data, size, server_process_syslog_message);
+ return 0;
+}
diff --git a/src/fuzz/fuzz-journald.c b/src/fuzz/fuzz-journald.c
new file mode 100644
index 0000000..e2f73ff
--- /dev/null
+++ b/src/fuzz/fuzz-journald.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fuzz-journald.h"
+#include "journald-server.h"
+#include "sd-event.h"
+
+void dummy_server_init(Server *s, const uint8_t *buffer, size_t size) {
+ *s = (Server) {
+ .syslog_fd = -1,
+ .native_fd = -1,
+ .stdout_fd = -1,
+ .dev_kmsg_fd = -1,
+ .audit_fd = -1,
+ .hostname_fd = -1,
+ .notify_fd = -1,
+ .storage = STORAGE_NONE,
+ .line_max = 64,
+ };
+ assert_se(sd_event_default(&s->event) >= 0);
+
+ if (buffer) {
+ s->buffer = memdup_suffix0(buffer, size);
+ assert_se(s->buffer);
+ s->buffer_size = size + 1;
+ }
+}
+
+void fuzz_journald_processing_function(
+ const uint8_t *data,
+ size_t size,
+ void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len)
+ ) {
+ Server s;
+ char *label = NULL;
+ size_t label_len = 0;
+ struct ucred *ucred = NULL;
+ struct timeval *tv = NULL;
+
+ if (size == 0)
+ return;
+
+ dummy_server_init(&s, data, size);
+ (*f)(&s, s.buffer, size, ucred, tv, label, label_len);
+ server_done(&s);
+}
diff --git a/src/fuzz/fuzz-journald.h b/src/fuzz/fuzz-journald.h
new file mode 100644
index 0000000..4abb100
--- /dev/null
+++ b/src/fuzz/fuzz-journald.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "journald-server.h"
+
+void dummy_server_init(Server *s, const uint8_t *buffer, size_t size);
+
+void fuzz_journald_processing_function(
+ const uint8_t *data,
+ size_t size,
+ void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len)
+);
diff --git a/src/fuzz/fuzz-json.c b/src/fuzz/fuzz-json.c
new file mode 100644
index 0000000..f9a0e81
--- /dev/null
+++ b/src/fuzz/fuzz-json.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "fd-util.h"
+#include "fuzz.h"
+#include "json.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *out = NULL; /* out should be freed after g */
+ size_t out_size;
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ if (size == 0)
+ return 0;
+
+ f = fmemopen_unlocked((char*) data, size, "re");
+ assert_se(f);
+
+ if (json_parse_file(f, NULL, 0, &v, NULL, NULL) < 0)
+ return 0;
+
+ g = open_memstream_unlocked(&out, &out_size);
+ assert_se(g);
+
+ json_variant_dump(v, 0, g, NULL);
+ json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, g, NULL);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-lldp.c b/src/fuzz/fuzz-lldp.c
new file mode 100644
index 0000000..5747135
--- /dev/null
+++ b/src/fuzz/fuzz-lldp.c
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sd-event.h"
+#include "sd-lldp.h"
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "lldp-network.h"
+
+static int test_fd[2] = { -1, -1 };
+
+int lldp_network_bind_raw_socket(int ifindex) {
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
+
+ if (size > 2048)
+ return 0;
+
+ assert_se(sd_event_new(&e) == 0);
+ assert_se(sd_lldp_new(&lldp) >= 0);
+ assert_se(sd_lldp_set_ifindex(lldp, 42) >= 0);
+ assert_se(sd_lldp_attach_event(lldp, e, 0) >= 0);
+ assert_se(sd_lldp_start(lldp) >= 0);
+
+ assert_se(write(test_fd[1], data, size) == (ssize_t) size);
+ assert_se(sd_event_run(e, 0) >= 0);
+
+ assert_se(sd_lldp_stop(lldp) >= 0);
+ assert_se(sd_lldp_detach_event(lldp) >= 0);
+ test_fd[1] = safe_close(test_fd[1]);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-lldp.options b/src/fuzz/fuzz-lldp.options
new file mode 100644
index 0000000..60bd9b0
--- /dev/null
+++ b/src/fuzz/fuzz-lldp.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 2048
diff --git a/src/fuzz/fuzz-main.c b/src/fuzz/fuzz-main.c
new file mode 100644
index 0000000..2df2993
--- /dev/null
+++ b/src/fuzz/fuzz-main.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "log.h"
+#include "fileio.h"
+#include "fuzz.h"
+#include "tests.h"
+
+/* This is a test driver for the systemd fuzzers that provides main function
+ * for regression testing outside of oss-fuzz (https://github.com/google/oss-fuzz)
+ *
+ * It reads files named on the command line and passes them one by one into the
+ * fuzzer that it is compiled into. */
+
+/* This one was borrowed from
+ * https://github.com/google/oss-fuzz/blob/646fca1b506b056db3a60d32c4a1a7398f171c94/infra/base-images/base-runner/bad_build_check#L19
+ */
+#define MIN_NUMBER_OF_RUNS 4
+
+int main(int argc, char **argv) {
+ int i, r;
+ size_t size;
+ char *name;
+
+ test_setup_logging(LOG_DEBUG);
+
+ for (i = 1; i < argc; i++) {
+ _cleanup_free_ char *buf = NULL;
+
+ name = argv[i];
+ r = read_full_file(name, &buf, &size);
+ if (r < 0) {
+ log_error_errno(r, "Failed to open '%s': %m", name);
+ return EXIT_FAILURE;
+ }
+ printf("%s... ", name);
+ fflush(stdout);
+ for (int j = 0; j < MIN_NUMBER_OF_RUNS; j++)
+ if (LLVMFuzzerTestOneInput((uint8_t*)buf, size) == EXIT_TEST_SKIP)
+ return EXIT_TEST_SKIP;
+ printf("ok\n");
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/fuzz/fuzz-ndisc-rs.c b/src/fuzz/fuzz-ndisc-rs.c
new file mode 100644
index 0000000..d74cd2f
--- /dev/null
+++ b/src/fuzz/fuzz-ndisc-rs.c
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <arpa/inet.h>
+#include <netinet/icmp6.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "icmp6-util.h"
+#include "fuzz.h"
+#include "sd-ndisc.h"
+#include "socket-util.h"
+#include "ndisc-internal.h"
+
+static int test_fd[2] = { -1, -1 };
+
+int icmp6_bind_router_solicitation(int index) {
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
+ return test_fd[0];
+}
+
+int icmp6_bind_router_advertisement(int index) {
+ return -ENOSYS;
+}
+
+int icmp6_receive(int fd, void *iov_base, size_t iov_len,
+ struct in6_addr *dst, triple_timestamp *timestamp) {
+ assert_se(read(fd, iov_base, iov_len) == (ssize_t) iov_len);
+
+ if (timestamp)
+ triple_timestamp_get(timestamp);
+
+ return 0;
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+ };
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ if (size > 2048)
+ return 0;
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_start(nd) >= 0);
+ assert_se(write(test_fd[1], data, size) == (ssize_t) size);
+ (void) sd_event_run(e, (uint64_t) -1);
+ assert_se(sd_ndisc_stop(nd) >= 0);
+ close(test_fd[1]);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-ndisc-rs.options b/src/fuzz/fuzz-ndisc-rs.options
new file mode 100644
index 0000000..60bd9b0
--- /dev/null
+++ b/src/fuzz/fuzz-ndisc-rs.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 2048
diff --git a/src/fuzz/fuzz-nspawn-oci.c b/src/fuzz/fuzz-nspawn-oci.c
new file mode 100644
index 0000000..cfebf65
--- /dev/null
+++ b/src/fuzz/fuzz-nspawn-oci.c
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fuzz.h"
+#include "nspawn-oci.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(settings_freep) Settings *s = NULL;
+
+ if (size == 0)
+ return 0;
+
+ f = fmemopen_unlocked((char*) data, size, "re");
+ assert_se(f);
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ (void) oci_load(f, "/dev/null", &s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-nspawn-oci.options b/src/fuzz/fuzz-nspawn-oci.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-nspawn-oci.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-nspawn-settings.c b/src/fuzz/fuzz-nspawn-settings.c
new file mode 100644
index 0000000..bd98ed2
--- /dev/null
+++ b/src/fuzz/fuzz-nspawn-settings.c
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fuzz.h"
+#include "nspawn-settings.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(settings_freep) Settings *s = NULL;
+
+ if (size == 0)
+ return 0;
+
+ f = fmemopen_unlocked((char*) data, size, "re");
+ assert_se(f);
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ (void) settings_load(f, "/dev/null", &s);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-nspawn-settings.options b/src/fuzz/fuzz-nspawn-settings.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-nspawn-settings.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-time-util.c b/src/fuzz/fuzz-time-util.c
new file mode 100644
index 0000000..bf2a663
--- /dev/null
+++ b/src/fuzz/fuzz-time-util.c
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fuzz.h"
+#include "time-util.h"
+#include "util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *str = NULL;
+ usec_t usec;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ str = memdup_suffix0(data, size);
+
+ (void) parse_timestamp(str, &usec);
+ (void) parse_sec(str, &usec);
+ (void) parse_sec_fix_0(str, &usec);
+ (void) parse_sec_def_infinity(str, &usec);
+ (void) parse_time(str, &usec, USEC_PER_SEC);
+ (void) parse_nsec(str, &usec);
+
+ (void) timezone_is_valid(str, LOG_DEBUG);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-udev-database.c b/src/fuzz/fuzz-udev-database.c
new file mode 100644
index 0000000..2a48c14
--- /dev/null
+++ b/src/fuzz/fuzz-udev-database.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "device-internal.h"
+#include "device-private.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "tmpfile-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-udev-database.XXXXXX";
+ _cleanup_fclose_ FILE *f = NULL;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(fmkostemp_safe(filename, "r+", &f) == 0);
+ if (size != 0)
+ assert_se(fwrite(data, size, 1, f) == 1);
+
+ fflush(f);
+ assert_se(device_new_aux(&dev) >= 0);
+ (void) device_read_db_internal_filename(dev, filename);
+ return 0;
+}
diff --git a/src/fuzz/fuzz-udev-rule-parse-value.c b/src/fuzz/fuzz-udev-rule-parse-value.c
new file mode 100644
index 0000000..404d0cd
--- /dev/null
+++ b/src/fuzz/fuzz-udev-rule-parse-value.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <string.h>
+
+#include "alloc-util.h"
+#include "fuzz.h"
+#include "udev-util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *str = NULL;
+ int r;
+ char *value = UINT_TO_PTR(0x12345678U);
+ char *endpos = UINT_TO_PTR(0x87654321U);
+
+ assert_se(str = malloc(size + 1));
+ memcpy(str, data, size);
+ str[size] = '\0';
+
+ r = udev_rule_parse_value(str, &value, &endpos);
+
+ if (r < 0) {
+ /* not modified on failure */
+ assert_se(value == UINT_TO_PTR(0x12345678U));
+ assert_se(endpos == UINT_TO_PTR(0x87654321U));
+ } else {
+ assert_se(endpos <= str + size);
+ assert_se(endpos > str + 1);
+ }
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-udev-rules.c b/src/fuzz/fuzz-udev-rules.c
new file mode 100644
index 0000000..e1140bc
--- /dev/null
+++ b/src/fuzz/fuzz-udev-rules.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdio.h>
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "udev-rules.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-udev-rules.XXXXXX";
+ int r;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL")) {
+ log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT);
+ log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT);
+ }
+
+ assert_se(fmkostemp_safe(filename, "r+", &f) == 0);
+ if (size != 0)
+ assert_se(fwrite(data, size, 1, f) == 1);
+ fflush(f);
+
+ assert_se(rules = udev_rules_new(RESOLVE_NAME_EARLY));
+ r = udev_rules_parse_file(rules, filename);
+ log_info_errno(r, "Parsing %s: %m", filename);
+ assert_se(IN_SET(r,
+ 0, /* OK */
+ -ENOBUFS /* line length exceeded */));
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-udev-rules.options b/src/fuzz/fuzz-udev-rules.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-udev-rules.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-unit-file.c b/src/fuzz/fuzz-unit-file.c
new file mode 100644
index 0000000..e67f6e9
--- /dev/null
+++ b/src/fuzz/fuzz-unit-file.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "conf-parser.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fuzz.h"
+#include "install.h"
+#include "load-fragment.h"
+#include "string-util.h"
+#include "unit.h"
+#include "utf8.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *out = NULL; /* out should be freed after g */
+ size_t out_size;
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
+ _cleanup_free_ char *p = NULL;
+ UnitType t;
+ _cleanup_(manager_freep) Manager *m = NULL;
+ Unit *u;
+ const char *name;
+ long offset;
+
+ if (size == 0)
+ return 0;
+
+ f = fmemopen_unlocked((char*) data, size, "re");
+ assert_se(f);
+
+ if (read_line(f, LINE_MAX, &p) < 0)
+ return 0;
+
+ t = unit_type_from_string(p);
+ if (t < 0)
+ return 0;
+
+ if (!unit_vtable[t]->load)
+ return 0;
+
+ offset = ftell(f);
+ assert_se(offset >= 0);
+
+ for (;;) {
+ _cleanup_free_ char *l = NULL;
+ const char *ll;
+
+ if (read_line(f, LONG_LINE_MAX, &l) <= 0)
+ break;
+
+ ll = startswith(l, UTF8_BYTE_ORDER_MARK) ?: l;
+ ll = ll + strspn(ll, WHITESPACE);
+
+ if (HAS_FEATURE_MEMORY_SANITIZER && startswith(ll, "ListenNetlink")) {
+ /* ListenNetlink causes a false positive in msan,
+ * let's skip this for now. */
+ log_notice("Skipping test because ListenNetlink= is present");
+ return 0;
+ }
+ }
+
+ assert_se(fseek(f, offset, SEEK_SET) == 0);
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(manager_new(UNIT_FILE_SYSTEM, MANAGER_TEST_RUN_MINIMAL, &m) >= 0);
+
+ name = strjoina("a.", unit_type_to_string(t));
+ assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0);
+
+ (void) config_parse(
+ name, name, f,
+ UNIT_VTABLE(u)->sections,
+ config_item_perf_lookup, load_fragment_gperf_lookup,
+ 0,
+ u,
+ NULL);
+
+ g = open_memstream_unlocked(&out, &out_size);
+ assert_se(g);
+
+ unit_dump(u, g, "");
+ manager_dump(m, g, ">>>");
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-unit-file.options b/src/fuzz/fuzz-unit-file.options
new file mode 100644
index 0000000..678d526
--- /dev/null
+++ b/src/fuzz/fuzz-unit-file.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65536
diff --git a/src/fuzz/fuzz-varlink.c b/src/fuzz/fuzz-varlink.c
new file mode 100644
index 0000000..f26050c
--- /dev/null
+++ b/src/fuzz/fuzz-varlink.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fuzz.h"
+#include "hexdecoct.h"
+#include "io-util.h"
+#include "varlink.h"
+#include "log.h"
+
+static FILE *null = NULL;
+
+static int method_something(Varlink *v, JsonVariant *p, VarlinkMethodFlags flags, void *userdata) {
+ json_variant_dump(p, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, null, NULL);
+ return 0;
+}
+
+static int reply_callback(Varlink *v, JsonVariant *p, const char *error_id, VarlinkReplyFlags flags, void *userdata) {
+ json_variant_dump(p, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, null, NULL);
+ return 0;
+}
+
+static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ struct iovec *iov = userdata;
+ bool write_eof = false, read_eof = false;
+
+ assert(s);
+ assert(fd >= 0);
+ assert(iov);
+
+ if ((revents & (EPOLLOUT|EPOLLHUP|EPOLLERR)) && iov->iov_len > 0) {
+ ssize_t n;
+
+ /* never write more than 143 bytes a time, to make broken up recv()s on the other side more
+ * likely, and thus test some additional code paths. */
+ n = send(fd, iov->iov_base, MIN(iov->iov_len, 143U), MSG_NOSIGNAL|MSG_DONTWAIT);
+ if (n < 0) {
+ if (ERRNO_IS_DISCONNECT(errno))
+ write_eof = true;
+ else
+ assert_se(errno == EAGAIN);
+ } else
+ IOVEC_INCREMENT(iov, 1, n);
+ }
+
+ if (revents & EPOLLIN) {
+ char c[137];
+ ssize_t n;
+
+ n = recv(fd, c, sizeof(c), MSG_DONTWAIT);
+ if (n < 0) {
+ if (ERRNO_IS_DISCONNECT(errno))
+ read_eof = true;
+ else
+ assert_se(errno == EAGAIN);
+ } else if (n == 0)
+ read_eof = true;
+ else
+ hexdump(null, c, (size_t) n);
+ }
+
+ /* After we wrote everything we could turn off EPOLLOUT. And if we reached read EOF too turn off the
+ * whole thing. */
+ if (write_eof || iov->iov_len == 0) {
+
+ if (read_eof)
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
+ else
+ assert_se(sd_event_source_set_io_events(s, EPOLLIN) >= 0);
+ }
+
+ return 0;
+}
+
+static int idle_callback(sd_event_source *s, void *userdata) {
+ assert(s);
+
+ /* Called as idle callback when there's nothing else to do anymore */
+ sd_event_exit(sd_event_source_get_event(s), 0);
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ struct iovec server_iov = IOVEC_MAKE((void*) data, size), client_iov = IOVEC_MAKE((void*) data, size);
+ /* Important: the declaration order matters here! we want that the fds are closed on return after the
+ * event sources, hence we declare the fds first, the event sources second */
+ _cleanup_close_pair_ int server_pair[2] = { -1, -1 }, client_pair[2] = { -1, -1 };
+ _cleanup_(sd_event_source_unrefp) sd_event_source *idle_event_source = NULL,
+ *server_event_source = NULL, *client_event_source = NULL;
+ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+ _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+
+ log_set_max_level(LOG_CRIT);
+ log_parse_environment();
+
+ assert_se(null = fopen("/dev/null", "we"));
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ /* Test one: write the data as method call to a server */
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, server_pair) >= 0);
+ assert_se(varlink_server_new(&s, 0) >= 0);
+ assert_se(varlink_server_set_description(s, "myserver") >= 0);
+ assert_se(varlink_server_attach_event(s, e, 0) >= 0);
+ assert_se(varlink_server_add_connection(s, server_pair[0], NULL) >= 0);
+ TAKE_FD(server_pair[0]);
+ assert_se(varlink_server_bind_method(s, "io.test.DoSomething", method_something) >= 0);
+ assert_se(sd_event_add_io(e, &server_event_source, server_pair[1], EPOLLIN|EPOLLOUT, io_callback, &server_iov) >= 0);
+
+ /* Test two: write the data as method response to a client */
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, client_pair) >= 0);
+ assert_se(varlink_connect_fd(&c, client_pair[0]) >= 0);
+ TAKE_FD(client_pair[0]);
+ assert_se(varlink_set_description(c, "myclient") >= 0);
+ assert_se(varlink_attach_event(c, e, 0) >= 0);
+ assert_se(varlink_bind_reply(c, reply_callback) >= 0);
+ assert_se(varlink_invoke(c, "io.test.DoSomething", NULL) >= 0);
+ assert_se(sd_event_add_io(e, &client_event_source, client_pair[1], EPOLLIN|EPOLLOUT, io_callback, &client_iov) >= 0);
+
+ assert_se(sd_event_add_defer(e, &idle_event_source, idle_callback, NULL) >= 0);
+ assert_se(sd_event_source_set_priority(idle_event_source, SD_EVENT_PRIORITY_IDLE) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ null = safe_fclose(null);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz-xdg-desktop.c b/src/fuzz/fuzz-xdg-desktop.c
new file mode 100644
index 0000000..23077e4
--- /dev/null
+++ b/src/fuzz/fuzz-xdg-desktop.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "fuzz.h"
+#include "xdg-autostart-service.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-xdg-desktop.XXXXXX";
+ _cleanup_close_ int fd = -1;
+ _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
+
+ /* We don't want to fill the logs with messages about parse errors.
+ * Disable most logging if not running standalone */
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ assert_se(mkdtemp_malloc("/tmp/fuzz-xdg-desktop-XXXXXX", &tmpdir) >= 0);
+
+ fd = mkostemp_safe(name);
+ assert_se(fd >= 0);
+ assert_se(write(fd, data, size) == (ssize_t) size);
+
+ assert_se(service = xdg_autostart_service_parse_desktop(name));
+ assert_se(service->name = strdup("fuzz-xdg-desktop.service"));
+ (void) xdg_autostart_service_generate_unit(service, tmpdir);
+
+ return 0;
+}
diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h
new file mode 100644
index 0000000..579b0ee
--- /dev/null
+++ b/src/fuzz/fuzz.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* The entry point into the fuzzer */
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build
new file mode 100644
index 0000000..a5fac59
--- /dev/null
+++ b/src/fuzz/meson.build
@@ -0,0 +1,159 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+fuzzers += [
+ [['src/fuzz/fuzz-bus-message.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-catalog.c'],
+ [libjournal_core,
+ libshared],
+ []],
+
+ [['src/fuzz/fuzz-dns-packet.c',
+ dns_type_headers],
+ [libsystemd_resolve_core,
+ libshared],
+ [libgcrypt,
+ libgpg_error,
+ libm]],
+
+ [['src/fuzz/fuzz-dhcp6-client.c',
+ 'src/libsystemd-network/dhcp-identifier.h',
+ 'src/libsystemd-network/dhcp-identifier.c',
+ 'src/libsystemd-network/dhcp6-internal.h',
+ 'src/systemd/sd-dhcp6-client.h'],
+ [libshared,
+ libsystemd_network],
+ []],
+
+ [['src/fuzz/fuzz-dhcp-server.c'],
+ [libsystemd_network,
+ libshared],
+ []],
+
+ [['src/fuzz/fuzz-lldp.c'],
+ [libshared,
+ libsystemd_network],
+ []],
+
+ [['src/fuzz/fuzz-ndisc-rs.c',
+ 'src/libsystemd-network/dhcp-identifier.h',
+ 'src/libsystemd-network/dhcp-identifier.c',
+ 'src/libsystemd-network/icmp6-util.h',
+ 'src/systemd/sd-dhcp6-client.h',
+ 'src/systemd/sd-ndisc.h'],
+ [libshared,
+ libsystemd_network],
+ []],
+
+ [['src/fuzz/fuzz-json.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-varlink.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-unit-file.c'],
+ [libcore,
+ libshared],
+ [libmount]],
+
+ [['src/fuzz/fuzz-journald-audit.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-kmsg.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-native.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-native-fd.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-stream.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journald-syslog.c',
+ 'src/fuzz/fuzz-journald.c'],
+ [libjournal_core,
+ libshared],
+ [libselinux]],
+
+ [['src/fuzz/fuzz-journal-remote.c'],
+ [libsystemd_journal_remote,
+ libshared],
+ []],
+
+ [['src/fuzz/fuzz-udev-database.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-udev-rules.c'],
+ [libudev_core,
+ libudev_static,
+ libsystemd_network,
+ libshared],
+ [threads,
+ libacl]],
+
+ [['src/fuzz/fuzz-compress.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-bus-label.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-env-file.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-hostname-util.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-nspawn-settings.c'],
+ [libshared,
+ libnspawn_core],
+ [libseccomp]],
+
+ [['src/fuzz/fuzz-nspawn-oci.c'],
+ [libshared,
+ libnspawn_core],
+ [libseccomp]],
+
+ [['src/fuzz/fuzz-calendarspec.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-time-util.c'],
+ [libshared],
+ []],
+
+ [['src/fuzz/fuzz-xdg-desktop.c',
+ 'src/xdg-autostart-generator/xdg-autostart-service.h',
+ 'src/xdg-autostart-generator/xdg-autostart-service.c'],
+ [],
+ []],
+
+ [['src/fuzz/fuzz-udev-rule-parse-value.c'],
+ [libshared],
+ []],
+]