diff --git a/src/fuzz/fuzz-varlink.c b/src/fuzz/fuzz-varlink.c new file mode 100644 index 0000000000..31c13e2bea --- /dev/null +++ b/src/fuzz/fuzz-varlink.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#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/meson.build b/src/fuzz/meson.build index a6c6db372b..c88812d1de 100644 --- a/src/fuzz/meson.build +++ b/src/fuzz/meson.build @@ -51,6 +51,10 @@ fuzzers += [ [libshared], []], + [['src/fuzz/fuzz-varlink.c'], + [libshared], + []], + [['src/fuzz/fuzz-unit-file.c'], [libcore, libshared], diff --git a/test/fuzz/fuzz-varlink/array b/test/fuzz/fuzz-varlink/array new file mode 100644 index 0000000000..f3ee40bcca Binary files /dev/null and b/test/fuzz/fuzz-varlink/array differ diff --git a/test/fuzz/fuzz-varlink/do-something b/test/fuzz/fuzz-varlink/do-something new file mode 100644 index 0000000000..3b124cb7d4 Binary files /dev/null and b/test/fuzz/fuzz-varlink/do-something differ diff --git a/test/fuzz/fuzz-varlink/huge-method b/test/fuzz/fuzz-varlink/huge-method new file mode 100644 index 0000000000..a480e417fc --- /dev/null +++ b/test/fuzz/fuzz-varlink/huge-method @@ -0,0 +1 @@ +{"method":" "} \ No newline at end of file diff --git a/test/fuzz/fuzz-varlink/method-call b/test/fuzz/fuzz-varlink/method-call new file mode 100644 index 0000000000..8654a7c06d Binary files /dev/null and b/test/fuzz/fuzz-varlink/method-call differ diff --git a/test/fuzz/fuzz-varlink/method-error b/test/fuzz/fuzz-varlink/method-error new file mode 100644 index 0000000000..9ce68d8e4b Binary files /dev/null and b/test/fuzz/fuzz-varlink/method-error differ diff --git a/test/fuzz/fuzz-varlink/method-reply b/test/fuzz/fuzz-varlink/method-reply new file mode 100644 index 0000000000..cd4bd947ab Binary files /dev/null and b/test/fuzz/fuzz-varlink/method-reply differ diff --git a/test/fuzz/fuzz-varlink/timeout-d8a88bf4adea54537d21e3afb396e1a55c5b58bf b/test/fuzz/fuzz-varlink/timeout-d8a88bf4adea54537d21e3afb396e1a55c5b58bf new file mode 100644 index 0000000000..b0d8618298 Binary files /dev/null and b/test/fuzz/fuzz-varlink/timeout-d8a88bf4adea54537d21e3afb396e1a55c5b58bf differ