diff options
Diffstat (limited to '')
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], + []], +] |