From 61b914935441a4afdb72afdd9ee47103b6270294 Mon Sep 17 00:00:00 2001 From: Xuan Xing Date: Wed, 25 Jan 2023 01:38:28 +0000 Subject: [PATCH] Add LKL fuzzer for HID. This fuzzer fuzzes Linux kernel HID subsystem via /dev/uhid device. To build the fuzzer: make -C tools/lkl LKL_FUZZING=1 fuzzers Signed-off-by: Xuan Xing --- arch/lkl/scripts/headers_install.py | 2 + tools/lkl/Targets | 3 +- tools/lkl/fuzzers/hid/Build | 2 + tools/lkl/fuzzers/hid/hid-fuzzer.c | 348 ++++++++++++++++++++++++++++ tools/lkl/fuzzers/hid/seeds/1 | Bin 0 -> 127 bytes tools/lkl/fuzzers/hid/seeds/2 | Bin 0 -> 88 bytes tools/lkl/fuzzers/hid/seeds/3 | Bin 0 -> 50 bytes tools/lkl/fuzzers/hid/seeds/4 | Bin 0 -> 137 bytes tools/lkl/fuzzers/hid/seeds/5 | Bin 0 -> 38 bytes tools/lkl/fuzzers/hid/seeds/6 | Bin 0 -> 67 bytes 10 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 tools/lkl/fuzzers/hid/Build create mode 100644 tools/lkl/fuzzers/hid/hid-fuzzer.c create mode 100644 tools/lkl/fuzzers/hid/seeds/1 create mode 100644 tools/lkl/fuzzers/hid/seeds/2 create mode 100644 tools/lkl/fuzzers/hid/seeds/3 create mode 100644 tools/lkl/fuzzers/hid/seeds/4 create mode 100644 tools/lkl/fuzzers/hid/seeds/5 create mode 100644 tools/lkl/fuzzers/hid/seeds/6 diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py index e22457d46a7924..36e965098a3a1c 100755 --- a/arch/lkl/scripts/headers_install.py +++ b/arch/lkl/scripts/headers_install.py @@ -128,6 +128,8 @@ def replace(h): find_headers("arch/lkl/include/uapi/asm/syscalls.h") headers.add("arch/lkl/include/uapi/asm/host_ops.h") +find_headers("include/uapi/linux/uhid.h") +find_headers("include/uapi/linux/input-event-codes.h") if 'LKL_INSTALL_ADDITIONAL_HEADERS' in os.environ: with open(os.environ['LKL_INSTALL_ADDITIONAL_HEADERS'], 'rU') as f: diff --git a/tools/lkl/Targets b/tools/lkl/Targets index d7eafcf3a714b5..7da425e774da91 100644 --- a/tools/lkl/Targets +++ b/tools/lkl/Targets @@ -28,4 +28,5 @@ progs-y += tests/net-test progs-y += tests/config # LKL fuzzers -fuzzers-y += +fuzzers-y += fuzzers/hid/hid-fuzzer + diff --git a/tools/lkl/fuzzers/hid/Build b/tools/lkl/fuzzers/hid/Build new file mode 100644 index 00000000000000..b9fb6a4e0d6ed5 --- /dev/null +++ b/tools/lkl/fuzzers/hid/Build @@ -0,0 +1,2 @@ +hid-fuzzer-y += hid-fuzzer.o +CFLAGS_hid-fuzzer.o += -O0 diff --git a/tools/lkl/fuzzers/hid/hid-fuzzer.c b/tools/lkl/fuzzers/hid/hid-fuzzer.c new file mode 100644 index 00000000000000..8e2c0904728d98 --- /dev/null +++ b/tools/lkl/fuzzers/hid/hid-fuzzer.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX_EVENT_SIZE 128 + +#define LKL_CALL(op) lkl_sys_##op + +#define LOG(fmt, ...) \ + do { \ + if (g_log_enabled) {\ + printf(fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_BYTES(title, data, size) \ + do { \ + if (g_log_enabled) {\ + dump_bytes(title, data, size); \ + } \ + } while (0) + +bool g_log_enabled = true; + +void dump_bytes(const char *title, const void *data, size_t size) +{ + const int kBytesPerLine = 16; // 16 bytes per line + const int kCharPerByte = 3; // format string: " %02X" + const int kAddrWidth = 9; // format string: "%08X:" + + const uint8_t *p = (const uint8_t *)data; + char line[kAddrWidth + kCharPerByte * kBytesPerLine + kBytesPerLine + 2]; + + printf("%s:size=%zu\n", title, size); + for (size_t i = 0; i < size; i++) { + size_t col = i % kBytesPerLine; + + if (col == 0) { + memset(line, ' ', sizeof(line)); + snprintf(line, sizeof(line), "%08zX: ", i); + } + + // Hex code display + snprintf(&line[kAddrWidth + col * kCharPerByte], + sizeof(line) - (kAddrWidth + col * kCharPerByte), " %02X", *p); + + // Printable display + line[kAddrWidth + kCharPerByte * kBytesPerLine + col + 1] = isprint(*p) ? *p : '.'; + + // Line ending + if (col == (kBytesPerLine - 1) || i == (size - 1)) { + // This erases the '\0' added by snprintf right after hex code + line[kAddrWidth + (col + 1) * kCharPerByte] = ' '; + + // This adds the '\0' at the end of entire line + line[kAddrWidth + kCharPerByte * kBytesPerLine + kBytesPerLine + 1] = '\0'; + printf("%s\n", line); + } + + p++; + } + printf("\n"); +} + +static int uhid_write(int fd, const struct lkl_uhid_event *ev) +{ + int size = sizeof(*ev); + ssize_t ret = LKL_CALL(write)(fd, (const char *)ev, size); + + if (ret < 0) { + LOG("Cannot write to uhid: %d\n", errno); + return ret; + } else if (ret != sizeof(*ev)) { + LOG("Wrong size written to uhid: %ld != %lu\n", ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int uhid_create(int fd, uint16_t vid, uint16_t pid, + const void *data, size_t size) +{ + struct lkl_uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = LKL_UHID_CREATE; + strcpy((char *)ev.u.create.name, "test-uhid-device"); + ev.u.create.rd_data = (void *)data; + ev.u.create.rd_size = size; + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = vid; + ev.u.create.product = pid; + ev.u.create.version = 0; + ev.u.create.country = 0; + + return uhid_write(fd, &ev); +} + +static uint32_t fix_uhid_event_type(uint32_t type) +{ + static uint32_t event_map[] = { + LKL_UHID_INPUT, + LKL_UHID_INPUT2, + LKL_UHID_GET_REPORT_REPLY, + LKL_UHID_SET_REPORT_REPLY + }; + + int index = type % (sizeof(event_map) / sizeof(event_map[0])); + return event_map[index]; +} + +static const char *event_type_to_name(uint32_t type) +{ + if (type == LKL_UHID_INPUT) + return "UHID_INPUT"; + + if (type == LKL_UHID_INPUT2) + return "UHID_INPUT2"; + + if (type == LKL_UHID_GET_REPORT_REPLY) + return "UHID_GET_REPORT_REPLY"; + + if (type == LKL_UHID_SET_REPORT_REPLY) + return "UHID_SET_REPORT_REPLY"; + + return "UNKNOWN"; +} + +// return consumed data size +static int init_uhid_message(const uint8_t *data, size_t size, struct lkl_uhid_event *ev) +{ + memset(ev, 0, sizeof(*ev)); + + size_t ev_size = MIN(MAX_EVENT_SIZE, sizeof(*ev)); + + ev_size = MIN(ev_size, size); + + if (ev_size < sizeof(ev->type)) // no enough data + return 0; + + memcpy(ev, data, ev_size); + ev->type = fix_uhid_event_type(ev->type); + + return ev_size; +} + +static void uhid_destroy(int fd) +{ + struct lkl_uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +struct fuzz_data_t { + uint8_t rdesc_sz; + uint32_t msg_sz; + uint16_t vid; + uint16_t pid; + uint8_t payload[0]; +}; + +static int fuzz_data_fixup(struct fuzz_data_t *data, size_t data_sz) +{ + if (data_sz < sizeof(struct fuzz_data_t)) + return 0; + + if (data->rdesc_sz > 255) + data->rdesc_sz = 255; + + size_t sz = data_sz - sizeof(struct fuzz_data_t); + + if (data->rdesc_sz > sz) + data->rdesc_sz = sz; + + sz -= data->rdesc_sz; + + if (data->msg_sz > sz) + data->msg_sz = sz; + + return 1; +} + +static int uhid_fuzz(struct fuzz_data_t *data) +{ + if (data->rdesc_sz == 0) { + LOG("Empty fuzz dat\n"); + return 0; + } + + LOG("VID=%04X, PID=%04X, RDESC: %u bytes, msg: %u byetes\n", + data->vid, data->pid, data->rdesc_sz, data->msg_sz); + + LOG_BYTES("RDESC:", data->payload, data->rdesc_sz); + + int fd = LKL_CALL(open)("/dev/uhid", O_RDWR | O_CLOEXEC, 0); + + if (fd < 0) { + LOG("Cannot open /dev/uhid\n"); + return -1; + } + + int ret = uhid_create(fd, data->vid, data->pid, + data->payload, data->rdesc_sz); + + if (ret) { + close(fd); + LOG("Creating uhid device failed, %d\n", ret); + return -1; + } + + uint8_t *msg_data = data->payload + data->rdesc_sz; + uint32_t msg_data_size = data->msg_sz; + + while (msg_data_size > 0) { + struct lkl_uhid_event ev; + int consume = init_uhid_message(msg_data, msg_data_size, &ev); + + if (consume == 0) { // no more data + break; + } + + LOG("TYPE: %s ", event_type_to_name(ev.type)); + LOG_BYTES("DATA:", msg_data, consume); + msg_data += consume; + msg_data_size -= consume; + + uhid_write(fd, &ev); + } + + uhid_destroy(fd); + LKL_CALL(close)(fd); + return 0; +} + +static int initialize_lkl(void) +{ + if (!g_log_enabled) + lkl_host_ops.print = NULL; + + int ret = lkl_init(&lkl_host_ops); + + if (ret) { + LOG("lkl_init failed\n"); + return -1; + } + + ret = lkl_start_kernel("mem=50M kasan.fault=panic"); + if (ret) { + LOG("lkl_start_kernel failed\n"); + lkl_cleanup(); + return -1; + } + + lkl_mount_fs("sysfs"); + lkl_mount_fs("proc"); + lkl_mount_fs("dev"); + + // This is defined in miscdevice.h which is, however, not visible to + // userspace code. + #define UHID_MINOR 239 + + dev_t dev = makedev(MISC_MAJOR, UHID_MINOR); + int mknod_result = LKL_CALL(mknodat)(AT_FDCWD, "/dev/uhid", + S_IFCHR | S_IRUSR | S_IWUSR, dev); + + if (mknod_result != 0) { + LOG("Create device file failed\n"); + return -1; + } + + return 0; +} + +void __llvm_profile_initialize_file(void); +int __llvm_profile_write_file(void); + +void flush_coverage(void) +{ + LOG("Flushing coverage data...\n"); + __llvm_profile_write_file(); + LOG("Done...\n"); +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + for (int i = 0; i < *argc; i++) { + if (strcmp((*argv)[i], "-quiet=1") == 0) { + g_log_enabled = false; + break; + } + } + + initialize_lkl(); + + __llvm_profile_initialize_file(); + atexit(flush_coverage); + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + static int iter; + uint8_t data[sizeof(struct fuzz_data_t) + 128] = {0}; + + if (Size > sizeof(data)) + Size = sizeof(data); + + memcpy(data, Data, Size); + + struct fuzz_data_t *fuzz_data = (struct fuzz_data_t *)data; + int success = fuzz_data_fixup(fuzz_data, Size); + + if (success) { + uhid_fuzz(fuzz_data); + + iter++; + if (iter > 1000) { + flush_coverage(); + iter = 0; + } + } + + return 0; +} + diff --git a/tools/lkl/fuzzers/hid/seeds/1 b/tools/lkl/fuzzers/hid/seeds/1 new file mode 100644 index 0000000000000000000000000000000000000000..d9897995467b6ab2d45d9a5b044b9e3b162cd0f0 GIT binary patch literal 127 zcmdOvVrArHTgb@DF8M(7xhR7wV=3cQFLc0gfPta@|Ns9>x+Kx0R8z}2rZP4%9Dp06 f`2c1N2T%tLG%`(PEahlqlw{CM1=^BY3eg1ss)s7P literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/2 b/tools/lkl/fuzzers/hid/seeds/2 new file mode 100644 index 0000000000000000000000000000000000000000..572b7839b6833ceaefa893f1ded370532078cbeb GIT binary patch literal 88 zcmdOvVrArHTgb@DF8M(7xhR7wV=3cQjz*@bjHMinjFJqRsX(#RQjV#NjSL4^8M)aO LGN3EX!KM@dLEsa% literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/3 b/tools/lkl/fuzzers/hid/seeds/3 new file mode 100644 index 0000000000000000000000000000000000000000..6deff109f3f11189d806765fcda65f8fb64c3bd8 GIT binary patch literal 50 xcmdNEYC6Em$jP>l@jxyMh+=)9Darm^lz~yTlyNGDwp0^LoL%yP=JQ6TsQ^9-4N?FA literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/4 b/tools/lkl/fuzzers/hid/seeds/4 new file mode 100644 index 0000000000000000000000000000000000000000..f0e7c8d763d6fc31f02b8eb3cbc4ec0aa312ef55 GIT binary patch literal 137 zcmdPqVrArHTgb@DF8M(7xhR7wqqbDjfm{~2ST2ipDMurtB!gxuP&Sp7eJW!k!vR*H zGN48V)qd7e#;F{QOjG~;2ZP_LjI0o|8kriIBT&FpR#p#5Mopj{U=^CFr5qqbIN2Dd HvM~VwU4|on literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/5 b/tools/lkl/fuzzers/hid/seeds/5 new file mode 100644 index 0000000000000000000000000000000000000000..1ba1fcb6034d207a48944afd9aee2d476f23665e GIT binary patch literal 38 mcmZ?d=qhCt<(SIY$Z&v_k&|s9BkN(w2bv6Ua6D1#KNkR@90)o9 literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/6 b/tools/lkl/fuzzers/hid/seeds/6 new file mode 100644 index 0000000000000000000000000000000000000000..da78f8a83821b547924b8038227d39f9d564b6f3 GIT binary patch literal 67 zcmcC!U|`5)(LTz`$jP>laVkeCqbP$aBP+Y)1I_1+OpVM_Sy?$G88ulaGEHSHWuC}5 Wm93ORltJx315gQr)_;aZh64c0TMoeh literal 0 HcmV?d00001