summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@chromium.org>2018-06-20 14:21:43 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-08-16 00:30:08 -0700
commit4a4e2c71a0f6aaa50e0728922f84a7d54c14380a (patch)
tree4bbc05387f7b479bc4a3dcb925174676f03d9356 /test
parent165ee29673b058ba5f4550d5b6e1dfecb179bb22 (diff)
downloadchrome-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.mk4
-rw-r--r--test/host_command_fuzz.c169
-rw-r--r--test/host_command_fuzz.tasklist17
-rw-r--r--test/test_config.h18
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 */