ipc: simplify inflatable buffer and add fuzzer
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
f59f63f462
commit
1d2d6200b8
|
@ -2,10 +2,10 @@
|
||||||
#
|
#
|
||||||
# Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
# 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 ?= -O3 -march=native -g
|
||||||
CFLAGS += -fsanitize=fuzzer -std=gnu11 -idirafter ../uapi
|
CFLAGS += -fsanitize=fuzzer -fsanitize=address -std=gnu11 -idirafter ../uapi
|
||||||
CC := clang
|
CC := clang
|
||||||
|
|
||||||
config: config.c ../config.c ../encoding.c
|
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
|
uapi: uapi.c ../ipc.c ../curve25519.c ../encoding.c
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
stringlist: stringlist.c ../ipc.c ../curve25519.c ../encoding.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f config uapi
|
rm -f config uapi stringlist
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
104
src/ipc.c
104
src/ipc.c
|
@ -48,50 +48,34 @@
|
||||||
#define SOCKET_BUFFER_SIZE 8192
|
#define SOCKET_BUFFER_SIZE 8192
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct inflatable_buffer {
|
struct string_list {
|
||||||
char *buffer;
|
char *buffer;
|
||||||
char *next;
|
|
||||||
bool good;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t pos;
|
size_t cap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
static int string_list_add(struct string_list *list, const char *str)
|
||||||
static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
|
|
||||||
{
|
{
|
||||||
size_t len, expand_to;
|
size_t len = strlen(str) + 1;
|
||||||
|
|
||||||
|
if (len == 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (len >= list->cap - list->len) {
|
||||||
char *new_buffer;
|
char *new_buffer;
|
||||||
|
size_t new_cap = list->cap * 2;
|
||||||
|
|
||||||
if (!buffer->good || !buffer->next) {
|
if (new_cap < list->len +len + 1)
|
||||||
free(buffer->next);
|
new_cap = list->len + len + 1;
|
||||||
buffer->good = false;
|
new_buffer = realloc(list->buffer, new_cap);
|
||||||
return 0;
|
if (!new_buffer)
|
||||||
}
|
|
||||||
|
|
||||||
len = strlen(buffer->next) + 1;
|
|
||||||
|
|
||||||
if (len == 1) {
|
|
||||||
free(buffer->next);
|
|
||||||
buffer->good = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
return -errno;
|
return -errno;
|
||||||
|
list->buffer = new_buffer;
|
||||||
|
list->cap = new_cap;
|
||||||
}
|
}
|
||||||
memset(&new_buffer[buffer->len], 0, expand_to - buffer->len);
|
memcpy(list->buffer + list->len, str, len);
|
||||||
buffer->buffer = new_buffer;
|
list->len += len;
|
||||||
buffer->len = expand_to;
|
list->buffer[list->len] = '\0';
|
||||||
}
|
|
||||||
memcpy(&buffer->buffer[buffer->pos], buffer->next, len);
|
|
||||||
free(buffer->next);
|
|
||||||
buffer->good = false;
|
|
||||||
buffer->pos += len;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +151,7 @@ static bool userspace_has_wireguard_interface(const char *iface)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer)
|
static int userspace_get_wireguard_interfaces(struct string_list *list)
|
||||||
{
|
{
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
|
@ -188,9 +172,7 @@ static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer)
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
if (!userspace_has_wireguard_interface(ent->d_name))
|
if (!userspace_has_wireguard_interface(ent->d_name))
|
||||||
continue;
|
continue;
|
||||||
buffer->next = strdup(ent->d_name);
|
ret = string_list_add(list, ent->d_name);
|
||||||
buffer->good = true;
|
|
||||||
ret = add_next_to_inflatable_buffer(buffer);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -451,37 +433,42 @@ err:
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
|
struct interface {
|
||||||
|
const char *name;
|
||||||
|
bool is_wireguard;
|
||||||
|
};
|
||||||
|
|
||||||
static int parse_linkinfo(const struct nlattr *attr, void *data)
|
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)))
|
if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
|
||||||
buffer->good = true;
|
interface->is_wireguard = true;
|
||||||
return MNL_CB_OK;
|
return MNL_CB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_infomsg(const struct nlattr *attr, void *data)
|
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)
|
if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
|
||||||
return mnl_attr_parse_nested(attr, parse_linkinfo, data);
|
return mnl_attr_parse_nested(attr, parse_linkinfo, data);
|
||||||
else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
|
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;
|
return MNL_CB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
|
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;
|
int ret;
|
||||||
|
|
||||||
buffer->good = false;
|
ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
|
||||||
buffer->next = NULL;
|
|
||||||
ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, data);
|
|
||||||
if (ret != MNL_CB_OK)
|
if (ret != MNL_CB_OK)
|
||||||
return ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
if (nlh->nlmsg_type != NLMSG_DONE)
|
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;
|
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;
|
struct mnl_socket *nl = NULL;
|
||||||
char *rtnl_buffer = NULL;
|
char *rtnl_buffer = NULL;
|
||||||
|
@ -536,7 +523,7 @@ another:
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto cleanup;
|
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
|
/* 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
|
* during the dump. That's unfortunate, but is pretty common on busy
|
||||||
* systems that are adding and removing tunnels all the time. Rather
|
* systems that are adding and removing tunnels all the time. Rather
|
||||||
|
@ -940,30 +927,25 @@ out:
|
||||||
/* first\0second\0third\0forth\0last\0\0 */
|
/* first\0second\0third\0forth\0last\0\0 */
|
||||||
char *ipc_list_devices(void)
|
char *ipc_list_devices(void)
|
||||||
{
|
{
|
||||||
struct inflatable_buffer buffer = { .len = SOCKET_BUFFER_SIZE };
|
struct string_list list = { 0 };
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = -ENOMEM;
|
|
||||||
buffer.buffer = calloc(1, buffer.len);
|
|
||||||
if (!buffer.buffer)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
ret = kernel_get_wireguard_interfaces(&buffer);
|
ret = kernel_get_wireguard_interfaces(&list);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
#endif
|
#endif
|
||||||
ret = userspace_get_wireguard_interfaces(&buffer);
|
ret = userspace_get_wireguard_interfaces(&list);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
errno = -ret;
|
errno = -ret;
|
||||||
if (errno) {
|
if (errno) {
|
||||||
free(buffer.buffer);
|
free(list.buffer);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return buffer.buffer;
|
return list.buffer ?: strdup("\0");
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipc_get_device(struct wgdevice **dev, const char *iface)
|
int ipc_get_device(struct wgdevice **dev, const char *iface)
|
||||||
|
|
Loading…
Reference in New Issue