diff options
author | Nicolas Boichat <drinkcat@chromium.org> | 2018-06-20 14:21:43 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-08-16 00:30:08 -0700 |
commit | 4a4e2c71a0f6aaa50e0728922f84a7d54c14380a (patch) | |
tree | 4bbc05387f7b479bc4a3dcb925174676f03d9356 /test | |
parent | 165ee29673b058ba5f4550d5b6e1dfecb179bb22 (diff) | |
download | chrome-ec-4a4e2c71a0f6aaa50e0728922f84a7d54c14380a.tar.gz |
test: host_command_fuzz: fuzzing test
Writing fuzzing tests is a little tricky, as clang takes over the main
function. Instead, we start the test main function in a thread, and
have LLVMFuzzerTestOneInput prepare the host command buffer, and
wake the TEST_RUNNER task.
To make fuzzing faster, we only send somehow correctly formed requests,
with a valid checksum and length (this can be disabled with an option).
We also make sure that the emulator does not hibernate, reboot or jump
to a different image when fuzzing is enabled.
BRANCH=none
BUG=chromium:854975
TEST=make buildfuzztests -j
ASAN_OPTIONS="log_path=stderr" \
build/host/host_command_fuzz/host_command_fuzz.exe -timeout=5
Change-Id: I27b25e44c405f118dfc1296247479245e15e54b4
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1107523
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Jonathan Metzman <metzman@chromium.org>
Diffstat (limited to 'test')
-rw-r--r-- | test/build.mk | 4 | ||||
-rw-r--r-- | test/host_command_fuzz.c | 169 | ||||
-rw-r--r-- | test/host_command_fuzz.tasklist | 17 | ||||
-rw-r--r-- | test/test_config.h | 18 |
4 files changed, 208 insertions, 0 deletions
diff --git a/test/build.mk b/test/build.mk index d1ca94b7dc..967231684e 100644 --- a/test/build.mk +++ b/test/build.mk @@ -65,6 +65,9 @@ test-list-host += vboot test-list-host += x25519 endif +# Fuzzing tests +fuzz-test-list-host = host_command_fuzz + base32-y=base32.o battery_get_params_smart-y=battery_get_params_smart.o bklight_lid-y=bklight_lid.o @@ -81,6 +84,7 @@ fan-y=fan.o flash-y=flash.o hooks-y=hooks.o host_command-y=host_command.o +host_command_fuzz-y=host_command_fuzz.o inductive_charging-y=inductive_charging.o interrupt-scale=10 interrupt-y=interrupt.o diff --git a/test/host_command_fuzz.c b/test/host_command_fuzz.c new file mode 100644 index 0000000000..7f0bfad7a5 --- /dev/null +++ b/test/host_command_fuzz.c @@ -0,0 +1,169 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Fuzz host command. + */ + +#include <pthread.h> +#include <sys/time.h> + +#include "common.h" +#include "console.h" +#include "host_command.h" +#include "host_test.h" +#include "task.h" +#include "test_util.h" +#include "timer.h" +#include "util.h" + +/* Only test requests with valid size and checksum (makes fuzzing faster) */ +#define VALID_REQUEST_ONLY + +#define TASK_EVENT_FUZZ TASK_EVENT_CUSTOM(1) +#define TASK_EVENT_HOSTCMD_DONE TASK_EVENT_CUSTOM(2) + +/* Request/response buffer size (and maximum command length) */ +#define BUFFER_SIZE 128 + +struct host_packet pkt; +static uint8_t resp_buf[BUFFER_SIZE]; +struct ec_host_response *resp = (struct ec_host_response *)resp_buf; +static uint8_t req_buf[BUFFER_SIZE]; +static struct ec_host_request *req = (struct ec_host_request *)req_buf; + +static void hostcmd_respond(struct host_packet *pkt) +{ + task_set_event(TASK_ID_TEST_RUNNER, TASK_EVENT_HOSTCMD_DONE, 0); +} + +static char calculate_checksum(const char *buf, int size) +{ + int c = 0; + int i; + + for (i = 0; i < size; ++i) + c += buf[i]; + + return -c; +} + +struct chunk { + int start; + int size; +}; + +static int hostcmd_fill(const uint8_t *data, size_t size) +{ + static int first = 1; + +#ifdef VALID_REQUEST_ONLY + const int checksum_offset = offsetof(struct ec_host_request, checksum); + const int checksum_size = sizeof(req->checksum); + const int data_len_offset = offsetof(struct ec_host_request, data_len); + const int data_len_size = sizeof(req->data_len); + + struct chunk chunks[3]; + + chunks[0].start = 0; + chunks[0].size = checksum_offset; + chunks[1].start = chunks[0].start + chunks[0].size + checksum_size; + chunks[1].size = data_len_offset - chunks[1].start; + chunks[2].start = chunks[1].start + chunks[1].size + data_len_size; + chunks[2].size = sizeof(req_buf) - chunks[2].start; +#else + struct chunk chunks[1] = { {0, sizeof(req_buf)} }; +#endif + + int ipos = 0; + int i; + int req_size = 0; + + /* + * TODO(chromium:854975): We should probably malloc req_buf with the + * correct size, to make we do not read uninitialized req_buf data. + */ + memset(req_buf, 0, sizeof(req_buf)); + + /* + * Fill in req_buf, according to chunks defined above (i.e. skipping + * over checksum and data_len. + */ + for (i = 0; i < ARRAY_SIZE(chunks) && ipos < size; i++) { + int cp_size = MIN(chunks[i].size, size-ipos); + + memcpy(req_buf + chunks[i].start, data + ipos, cp_size); + + ipos += cp_size; + + req_size = chunks[i].start + cp_size; + } + + /* Not enough space in req_buf. */ + if (ipos != size) + return -1; + + pkt.request_size = req_size; + req->data_len = req_size - sizeof(*req); + req->checksum = calculate_checksum(req_buf, req_size); + + /* + * Print the full request on the first fuzzing attempt: useful to + * report bugs, and write up commit messages when reproducing + * issues. + */ + if (first) { + ccprintf("Request: cmd=%04x data=%.*h\n", + req->command, req_size, req_buf); + first = 0; + } + + pkt.send_response = hostcmd_respond; + pkt.request = (const void *)req_buf; + pkt.request_max = BUFFER_SIZE; + pkt.response = (void *)resp_buf; + pkt.response_max = BUFFER_SIZE; + pkt.driver_result = 0; + + return 0; +} + +static pthread_cond_t done_cond; +static pthread_mutex_t lock; + +void run_test(void) +{ + ccprints("Fuzzing task started"); + wait_for_task_started(); + + while (1) { + task_wait_event_mask(TASK_EVENT_FUZZ, -1); + /* Send the host command (pkt prepared by main thread). */ + host_packet_receive(&pkt); + task_wait_event_mask(TASK_EVENT_HOSTCMD_DONE, -1); + pthread_cond_signal(&done_cond); + } +} + +int test_fuzz_one_input(const uint8_t *data, unsigned int size) +{ + /* Fill in req_buf. */ + if (hostcmd_fill(data, size) < 0) + return 0; + + task_set_event(TASK_ID_TEST_RUNNER, TASK_EVENT_FUZZ, 0); + pthread_cond_wait(&done_cond, &lock); + +#ifdef VALID_REQUEST_ONLY + /* + * We carefully crafted all our requests to have a valid checksum, so + * we should never receive an invalid checksum error. (but ignore + * EC_CMD_TEST_PROTOCOL, as it can lead to arbitrary result values). + */ + ASSERT(req->command == EC_CMD_TEST_PROTOCOL || + resp->result != EC_RES_INVALID_CHECKSUM); +#endif + + return 0; +} + diff --git a/test/host_command_fuzz.tasklist b/test/host_command_fuzz.tasklist new file mode 100644 index 0000000000..de4df33e13 --- /dev/null +++ b/test/host_command_fuzz.tasklist @@ -0,0 +1,17 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST diff --git a/test/test_config.h b/test/test_config.h index 76e071b556..8efe426e38 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -294,5 +294,23 @@ enum nvmem_vars { #define CONFIG_CURVE25519 #endif /* TEST_X25519 */ +#ifdef TEST_FUZZ +/* Disable hibernate: We never want to exit while fuzzing. */ +#undef CONFIG_HIBERNATE +#endif + +#ifdef TEST_HOST_COMMAND_FUZZ +#undef CONFIG_HOSTCMD_DEBUG_MODE + +/* Defining this make fuzzing slower, but exercises additional code paths. */ +#define FUZZ_HOSTCMD_VERBOSE + +#ifdef FUZZ_HOSTCMD_VERBOSE +#define CONFIG_HOSTCMD_DEBUG_MODE HCDEBUG_PARAMS +#else +#define CONFIG_HOSTCMD_DEBUG_MODE HCDEBUG_OFF +#endif /* ! FUZZ_HOSTCMD_VERBOSE */ +#endif /* TEST_HOST_COMMAND_FUZZ */ + #endif /* TEST_BUILD */ #endif /* __TEST_TEST_CONFIG_H */ |