]> git.ipfire.org Git - thirdparty/wireguard-tools.git/commitdiff
ipc: simplify inflatable buffer and add fuzzer
authorJason A. Donenfeld <Jason@zx2c4.com>
Sat, 4 Jan 2020 13:46:27 +0000 (14:46 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Sat, 4 Jan 2020 14:07:10 +0000 (15:07 +0100)
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
src/fuzz/Makefile
src/fuzz/config.c
src/fuzz/stringlist.c [new file with mode: 0644]
src/fuzz/uapi.c
src/ipc.c

index 98a16dd6b9cc6ade6ad221ce3c05e9d2e8b6296e..3fb2970019019ec183053fd2a1cccab9ba304695 100644 (file)
@@ -2,10 +2,10 @@
 #
 # Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 
-all: config uapi
+all: config uapi stringlist
 
 CFLAGS ?= -O3 -march=native -g
-CFLAGS += -fsanitize=fuzzer -std=gnu11 -idirafter ../uapi
+CFLAGS += -fsanitize=fuzzer -fsanitize=address -std=gnu11 -idirafter ../uapi
 CC := clang
 
 config: config.c ../config.c ../encoding.c
@@ -14,7 +14,10 @@ config: config.c ../config.c ../encoding.c
 uapi: uapi.c ../ipc.c ../curve25519.c ../encoding.c
        $(CC) $(CFLAGS) -o $@ $<
 
+stringlist: stringlist.c ../ipc.c ../curve25519.c ../encoding.c
+       $(CC) $(CFLAGS) -o $@ $<
+
 clean:
-       rm -f config uapi
+       rm -f config uapi stringlist
 
 .PHONY: all clean
index 49c87b4ac935ca7e66ad846b051109824f1e7e27..5812b4c8ea36930b0249832a21633e979122b933 100644 (file)
@@ -18,7 +18,7 @@
 
 const char *__asan_default_options()
 {
-        return "verbosity=1";
+       return "verbosity=1";
 }
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)
diff --git a/src/fuzz/stringlist.c b/src/fuzz/stringlist.c
new file mode 100644 (file)
index 0000000..85f7330
--- /dev/null
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#define RUNSTATEDIR "/var/empty"
+#undef __linux__
+#include "../ipc.c"
+#include "../curve25519.c"
+#include "../encoding.c"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+       return "verbosity=1";
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_len)
+{
+       struct string_list list = { 0 };
+       char *interfaces;
+
+       if (!data_len)
+               return 0;
+
+       interfaces = malloc(data_len);
+       assert(interfaces);
+       memcpy(interfaces, data, data_len);
+       interfaces[data_len - 1] = '\0';
+
+       for (char *interface = interfaces; interface - interfaces < data_len; interface += strlen(interface) + 1)
+               assert(string_list_add(&list, interface) == 0);
+
+       for (char *interface = interfaces, *interface2 = list.buffer;;) {
+               size_t len;
+
+               if (interface - interfaces >= data_len) {
+                       assert(!interface2 || !strlen(interface2));
+                       break;
+               }
+               len = strlen(interface);
+               if (!len) {
+                       ++interface;
+                       continue;
+               }
+               assert(strlen(interface2) == len);
+               assert(!memcmp(interface, interface2, len + 1));
+               interface += len + 1;
+               interface2 += len + 1;
+       }
+       free(list.buffer);
+       free(interfaces);
+       return 0;
+}
index 90b2ba6d6fd48e6be89107d58d952b4223538bb9..a387125e4bc0219e0253e102ce1349046e8de7ac 100644 (file)
@@ -20,7 +20,7 @@ static FILE *hacked_userspace_interface_file(const char *iface);
 
 const char *__asan_default_options()
 {
-        return "verbosity=1";
+       return "verbosity=1";
 }
 
 union hackiface {
index 9833b3351f9777e783ece299627c4a5ddfbfa9f9..11106702480522a2bf8807cf0397e814dc0609e3 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
 #define SOCKET_BUFFER_SIZE 8192
 #endif
 
-struct inflatable_buffer {
+struct string_list {
        char *buffer;
-       char *next;
-       bool good;
        size_t len;
-       size_t pos;
+       size_t cap;
 };
 
-#define max(a, b) ((a) > (b) ? (a) : (b))
-static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
+static int string_list_add(struct string_list *list, const char *str)
 {
-       size_t len, expand_to;
-       char *new_buffer;
+       size_t len = strlen(str) + 1;
 
-       if (!buffer->good || !buffer->next) {
-               free(buffer->next);
-               buffer->good = false;
+       if (len == 1)
                return 0;
-       }
-
-       len = strlen(buffer->next) + 1;
 
-       if (len == 1) {
-               free(buffer->next);
-               buffer->good = false;
-               return 0;
-       }
+       if (len >= list->cap - list->len) {
+               char *new_buffer;
+               size_t new_cap = list->cap * 2;
 
-       if (buffer->len - buffer->pos <= len) {
-               expand_to = max(buffer->len * 2, buffer->len + len + 1);
-               new_buffer = realloc(buffer->buffer, expand_to);
-               if (!new_buffer) {
-                       free(buffer->next);
-                       buffer->good = false;
+               if (new_cap <  list->len +len + 1)
+                       new_cap = list->len + len + 1;
+               new_buffer = realloc(list->buffer, new_cap);
+               if (!new_buffer)
                        return -errno;
-               }
-               memset(&new_buffer[buffer->len], 0, expand_to - buffer->len);
-               buffer->buffer = new_buffer;
-               buffer->len = expand_to;
-       }
-       memcpy(&buffer->buffer[buffer->pos], buffer->next, len);
-       free(buffer->next);
-       buffer->good = false;
-       buffer->pos += len;
+               list->buffer = new_buffer;
+               list->cap = new_cap;
+       }
+       memcpy(list->buffer + list->len, str, len);
+       list->len += len;
+       list->buffer[list->len] = '\0';
        return 0;
 }
 
@@ -167,7 +151,7 @@ static bool userspace_has_wireguard_interface(const char *iface)
        return true;
 }
 
-static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer)
+static int userspace_get_wireguard_interfaces(struct string_list *list)
 {
        DIR *dir;
        struct dirent *ent;
@@ -188,9 +172,7 @@ static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer)
                *end = '\0';
                if (!userspace_has_wireguard_interface(ent->d_name))
                        continue;
-               buffer->next = strdup(ent->d_name);
-               buffer->good = true;
-               ret = add_next_to_inflatable_buffer(buffer);
+               ret = string_list_add(list, ent->d_name);
                if (ret < 0)
                        goto out;
        }
@@ -451,37 +433,42 @@ err:
 
 #ifdef __linux__
 
+struct interface {
+       const char *name;
+       bool is_wireguard;
+};
+
 static int parse_linkinfo(const struct nlattr *attr, void *data)
 {
-       struct inflatable_buffer *buffer = data;
+       struct interface *interface = data;
 
-       if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp("wireguard", mnl_attr_get_str(attr)))
-               buffer->good = true;
+       if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
+               interface->is_wireguard = true;
        return MNL_CB_OK;
 }
 
 static int parse_infomsg(const struct nlattr *attr, void *data)
 {
-       struct inflatable_buffer *buffer = data;
+       struct interface *interface = data;
 
        if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
                return mnl_attr_parse_nested(attr, parse_linkinfo, data);
        else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
-               buffer->next = strdup(mnl_attr_get_str(attr));
+               interface->name = mnl_attr_get_str(attr);
        return MNL_CB_OK;
 }
 
 static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
 {
-       struct inflatable_buffer *buffer = data;
+       struct string_list *list = data;
+       struct interface interface = { 0 };
        int ret;
 
-       buffer->good = false;
-       buffer->next = NULL;
-       ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, data);
+       ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
        if (ret != MNL_CB_OK)
                return ret;
-       ret = add_next_to_inflatable_buffer(buffer);
+       if (interface.name && interface.is_wireguard)
+               ret = string_list_add(list, interface.name);
        if (ret < 0)
                return ret;
        if (nlh->nlmsg_type != NLMSG_DONE)
@@ -489,7 +476,7 @@ static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
        return MNL_CB_OK;
 }
 
-static int kernel_get_wireguard_interfaces(struct inflatable_buffer *buffer)
+static int kernel_get_wireguard_interfaces(struct string_list *list)
 {
        struct mnl_socket *nl = NULL;
        char *rtnl_buffer = NULL;
@@ -536,7 +523,7 @@ another:
                ret = -errno;
                goto cleanup;
        }
-       if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, buffer)) < 0) {
+       if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {
                /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
                 * during the dump. That's unfortunate, but is pretty common on busy
                 * systems that are adding and removing tunnels all the time. Rather
@@ -940,30 +927,25 @@ out:
 /* first\0second\0third\0forth\0last\0\0 */
 char *ipc_list_devices(void)
 {
-       struct inflatable_buffer buffer = { .len = SOCKET_BUFFER_SIZE };
+       struct string_list list = { 0 };
        int ret;
 
-       ret = -ENOMEM;
-       buffer.buffer = calloc(1, buffer.len);
-       if (!buffer.buffer)
-               goto cleanup;
-
 #ifdef __linux__
-       ret = kernel_get_wireguard_interfaces(&buffer);
+       ret = kernel_get_wireguard_interfaces(&list);
        if (ret < 0)
                goto cleanup;
 #endif
-       ret = userspace_get_wireguard_interfaces(&buffer);
+       ret = userspace_get_wireguard_interfaces(&list);
        if (ret < 0)
                goto cleanup;
 
 cleanup:
        errno = -ret;
        if (errno) {
-               free(buffer.buffer);
+               free(list.buffer);
                return NULL;
        }
-       return buffer.buffer;
+       return list.buffer ?: strdup("\0");
 }
 
 int ipc_get_device(struct wgdevice **dev, const char *iface)