summaryrefslogtreecommitdiff
path: root/fuzz
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@chromium.org>2018-06-26 10:38:07 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-12-05 01:13:38 -0800
commitd2602418362812f8970616b4dc708d8847291df9 (patch)
tree358a4d978da34859ffa284002f79e9129c6569be /fuzz
parent45b4bec661a2504ef030104903d3b526b5efafa2 (diff)
downloadchrome-ec-d2602418362812f8970616b4dc708d8847291df9.tar.gz
test/usb_pd_fuzz: Fuzzing of USB PD data
Setup CC lines, then send up to 8 PD messages, in an attempt to cause errors while parsing PDO and other messages. BRANCH=none BUG=chromium:854975 TEST=make -j buildfuzztests && \ ./build/host/usb_pd_fuzz/usb_pd_fuzz.exe > /dev/null Change-Id: Ibb575ea8d464945390d1663dd6fff279bd9d77ea Signed-off-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1116626 Reviewed-by: Jonathan Metzman <metzman@chromium.org>
Diffstat (limited to 'fuzz')
-rw-r--r--fuzz/build.mk3
-rw-r--r--fuzz/fuzz_config.h8
-rw-r--r--fuzz/usb_pd_fuzz.c217
-rw-r--r--fuzz/usb_pd_fuzz.tasklist19
4 files changed, 246 insertions, 1 deletions
diff --git a/fuzz/build.mk b/fuzz/build.mk
index cf2ca5412e..d9788d91e0 100644
--- a/fuzz/build.mk
+++ b/fuzz/build.mk
@@ -6,7 +6,7 @@
# fuzzer binaries
#
-fuzz-test-list-host = cr50_fuzz host_command_fuzz
+fuzz-test-list-host = cr50_fuzz host_command_fuzz usb_pd_fuzz
# For fuzzing targets libec.a is built from the ro objects and hides functions
# that collide with stdlib. The rw only objects are then linked against libec.a
@@ -22,6 +22,7 @@ fuzz-test-list-host = cr50_fuzz host_command_fuzz
# Otherwise use <obj_name>-y
cr50_fuzz-rw = cr50_fuzz.o pinweaver_model.o mem_hash_tree.o
host_command_fuzz-y = host_command_fuzz.o
+usb_pd_fuzz-y = usb_pd_fuzz.o
CR50_PROTO_HEADERS := $(out)/gen/fuzz/cr50_fuzz.pb.h \
$(out)/gen/fuzz/pinweaver/pinweaver.pb.h
diff --git a/fuzz/fuzz_config.h b/fuzz/fuzz_config.h
index bcf7284ac4..362df69c56 100644
--- a/fuzz/fuzz_config.h
+++ b/fuzz/fuzz_config.h
@@ -85,5 +85,13 @@ enum nvmem_users {
#endif /* ! FUZZ_HOSTCMD_VERBOSE */
#endif /* TEST_HOST_COMMAND_FUZZ */
+#if defined(TEST_USB_PD_FUZZ)
+#define CONFIG_USB_POWER_DELIVERY
+#define CONFIG_USB_PD_DUAL_ROLE
+#define CONFIG_USB_PD_PORT_COUNT 2
+#define CONFIG_SHA256
+#define CONFIG_SW_CRC
+#endif /* TEST_USB_PD_FUZZ */
+
#endif /* TEST_FUZZ */
#endif /* __TEST_TEST_CONFIG_H */
diff --git a/fuzz/usb_pd_fuzz.c b/fuzz/usb_pd_fuzz.c
new file mode 100644
index 0000000000..ea385de2dc
--- /dev/null
+++ b/fuzz/usb_pd_fuzz.c
@@ -0,0 +1,217 @@
+/* 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.
+ *
+ * Test USB PD module.
+ */
+#include "common.h"
+#include "task.h"
+#include "tcpm.h"
+#include "test_util.h"
+#include "timer.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "util.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#define TASK_EVENT_FUZZ TASK_EVENT_CUSTOM(1)
+
+#define PORT0 0
+
+static int mock_tcpm_init(int port) { return EC_SUCCESS; }
+static int mock_tcpm_release(int port) { return EC_SUCCESS; }
+
+static int mock_tcpm_select_rp_value(int port, int rp)
+{
+ return EC_SUCCESS;
+}
+
+static int mock_tcpm_set_cc(int port, int pull) { return EC_SUCCESS; }
+static int mock_tcpm_set_polarity(int port, int polarity) { return EC_SUCCESS; }
+static int mock_tcpm_set_vconn(int port, int enable) { return EC_SUCCESS; }
+static int mock_tcpm_set_msg_header(int port,
+ int power_role, int data_role) { return EC_SUCCESS; }
+static int mock_tcpm_set_rx_enable(int port, int enable) { return EC_SUCCESS; }
+static int mock_tcpm_transmit(int port, enum tcpm_transmit_type type,
+ uint16_t header, const uint32_t *data) { return EC_SUCCESS; }
+static void mock_tcpc_alert(int port) {}
+static int mock_tcpci_get_chip_info(int port, int renew,
+ struct ec_response_pd_chip_info_v1 **info)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+#define MAX_TCPC_PAYLOAD 28
+
+struct message {
+ uint8_t cnt;
+ uint16_t header;
+ uint8_t payload[MAX_TCPC_PAYLOAD];
+} __packed;
+
+struct tcpc_state {
+ enum tcpc_cc_voltage_status cc1, cc2;
+ struct message message;
+};
+
+static struct tcpc_state mock_tcpc_state[CONFIG_USB_PD_PORT_COUNT];
+
+static int mock_tcpm_get_cc(int port, int *cc1, int *cc2)
+{
+ *cc1 = mock_tcpc_state[port].cc1;
+ *cc2 = mock_tcpc_state[port].cc2;
+
+ return EC_SUCCESS;
+}
+
+static int pending;
+
+int tcpm_has_pending_message(const int port)
+{
+ return pending;
+}
+
+int tcpm_dequeue_message(const int port, uint32_t *const payload,
+ int *const header)
+{
+ struct message *m = &mock_tcpc_state[port].message;
+
+ ccprints("%s", __func__);
+
+ /* Force a segfault, if no message is actually pending. */
+ if (pending == 0)
+ m = NULL;
+
+ *header = m->header;
+
+ memcpy(payload, m->payload, m->cnt - 3);
+
+ pending--;
+ return EC_SUCCESS;
+}
+
+/* Note this method can be called from an interrupt context. */
+int tcpm_enqueue_message(const int port)
+{
+ pending = 1;
+
+ /* Wake PD task up so it can process incoming RX messages */
+ task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0);
+
+ return EC_SUCCESS;
+}
+
+void tcpm_clear_pending_messages(int port) {}
+
+static const struct tcpm_drv mock_tcpm_drv = {
+ .init = &mock_tcpm_init,
+ .release = &mock_tcpm_release,
+ .get_cc = &mock_tcpm_get_cc,
+#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
+ .get_vbus_level = &mock_tcpm_get_vbus_level,
+#endif
+ .select_rp_value = &mock_tcpm_select_rp_value,
+ .set_cc = &mock_tcpm_set_cc,
+ .set_polarity = &mock_tcpm_set_polarity,
+ .set_vconn = &mock_tcpm_set_vconn,
+ .set_msg_header = &mock_tcpm_set_msg_header,
+ .set_rx_enable = &mock_tcpm_set_rx_enable,
+ /* The core calls tcpm_dequeue_message. */
+ .get_message_raw = NULL,
+ .transmit = &mock_tcpm_transmit,
+ .tcpc_alert = &mock_tcpc_alert,
+ .get_chip_info = &mock_tcpci_get_chip_info,
+};
+
+/* TCPC mux configuration */
+const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
+ {
+ .drv = &mock_tcpm_drv,
+ },
+ {
+ .drv = &mock_tcpm_drv,
+ }
+};
+
+static pthread_cond_t done_cond;
+static pthread_mutex_t lock;
+
+enum tcpc_cc_voltage_status next_cc1, next_cc2;
+const int MAX_MESSAGES = 8;
+static struct message messages[MAX_MESSAGES];
+
+void run_test(void)
+{
+ uint8_t port = PORT0;
+ int i;
+
+ ccprints("Fuzzing task started");
+ wait_for_task_started();
+
+ while (1) {
+ task_wait_event_mask(TASK_EVENT_FUZZ, -1);
+
+ memset(&mock_tcpc_state[port],
+ 0, sizeof(mock_tcpc_state[port]));
+
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_TCPC_RESET, 0);
+ task_wait_event(250 * MSEC);
+
+ mock_tcpc_state[port].cc1 = next_cc1;
+ mock_tcpc_state[port].cc2 = next_cc2;
+
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0);
+ task_wait_event(50 * MSEC);
+
+ /* Fake RX messages, one by one. */
+ for (i = 0; i < MAX_MESSAGES && messages[i].cnt; i++) {
+ memcpy(&mock_tcpc_state[port].message, &messages[i],
+ sizeof(messages[i]));
+
+ tcpm_enqueue_message(port);
+ task_wait_event(50 * MSEC);
+ }
+
+ pthread_cond_signal(&done_cond);
+ }
+}
+
+int test_fuzz_one_input(const uint8_t *data, unsigned int size)
+{
+ int i;
+
+ if (size < 1)
+ return 0;
+
+ next_cc1 = data[0] & 0x0f;
+ next_cc2 = (data[0] & 0xf0) >> 4;
+ data++; size--;
+
+ memset(messages, 0, sizeof(messages));
+
+ for (i = 0; i < MAX_MESSAGES && size > 0; i++) {
+ int cnt = data[0];
+
+ if (cnt < 3 || cnt > MAX_TCPC_PAYLOAD+3 || cnt > size) {
+ /* Invalid count, or out of bounds. */
+ return 0;
+ }
+
+ memcpy(&messages[i], data, cnt);
+
+ data += cnt; size -= cnt;
+ }
+
+ if (size != 0) {
+ /* Useless extra data in buffer, skip. */
+ return 0;
+ }
+
+ task_set_event(TASK_ID_TEST_RUNNER, TASK_EVENT_FUZZ, 0);
+ pthread_cond_wait(&done_cond, &lock);
+
+ return 0;
+}
diff --git a/fuzz/usb_pd_fuzz.tasklist b/fuzz/usb_pd_fuzz.tasklist
new file mode 100644
index 0000000000..3d7a91254b
--- /dev/null
+++ b/fuzz/usb_pd_fuzz.tasklist
@@ -0,0 +1,19 @@
+/* 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 \
+ TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_TEST(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE)