From d2602418362812f8970616b4dc708d8847291df9 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 26 Jun 2018 10:38:07 +0800 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/1116626 Reviewed-by: Jonathan Metzman --- fuzz/build.mk | 3 +- fuzz/fuzz_config.h | 8 ++ fuzz/usb_pd_fuzz.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++ fuzz/usb_pd_fuzz.tasklist | 19 ++++ 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 fuzz/usb_pd_fuzz.c create mode 100644 fuzz/usb_pd_fuzz.tasklist (limited to 'fuzz') 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 -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 +#include + +#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) -- cgit v1.2.1