diff options
author | Nicolas Boichat <drinkcat@chromium.org> | 2018-06-26 10:38:07 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-12-05 01:13:38 -0800 |
commit | d2602418362812f8970616b4dc708d8847291df9 (patch) | |
tree | 358a4d978da34859ffa284002f79e9129c6569be | |
parent | 45b4bec661a2504ef030104903d3b526b5efafa2 (diff) | |
download | chrome-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>
-rw-r--r-- | chip/host/build.mk | 2 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 7 | ||||
-rw-r--r-- | core/host/main.c | 2 | ||||
-rw-r--r-- | fuzz/build.mk | 3 | ||||
-rw-r--r-- | fuzz/fuzz_config.h | 8 | ||||
-rw-r--r-- | fuzz/usb_pd_fuzz.c | 217 | ||||
-rw-r--r-- | fuzz/usb_pd_fuzz.tasklist | 19 |
7 files changed, 255 insertions, 3 deletions
diff --git a/chip/host/build.mk b/chip/host/build.mk index 92284b072a..04c74a5d50 100644 --- a/chip/host/build.mk +++ b/chip/host/build.mk @@ -11,7 +11,7 @@ CORE:=host chip-y=system.o gpio.o uart.o persistence.o flash.o lpc.o reboot.o i2c.o \ clock.o chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o -chip-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_phy.o +chip-$(CONFIG_USB_PD_TCPC)+=usb_pd_phy.o ifeq ($(CONFIG_DCRYPTO),y) CPPFLAGS += -I$(abspath ./chip/g) diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index f58bdda679..7a8634e174 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -550,7 +550,9 @@ static int get_bbram_idx(int port) static int pd_get_saved_port_flags(int port, uint8_t *flags) { if (system_get_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { +#ifndef CHIP_HOST CPRINTS("PD NVRAM FAIL"); +#endif return EC_ERROR_UNKNOWN; } @@ -559,8 +561,11 @@ static int pd_get_saved_port_flags(int port, uint8_t *flags) static void pd_set_saved_port_flags(int port, uint8_t flags) { - if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) + if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { +#ifndef CHIP_HOST CPRINTS("PD NVRAM FAIL"); +#endif + } } static void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t val) diff --git a/core/host/main.c b/core/host/main.c index 45cfbfdfb4..822ca460e1 100644 --- a/core/host/main.c +++ b/core/host/main.c @@ -93,6 +93,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) initialized = 1; /* We can't sleep yet, busy loop waiting for tasks to start. */ wait_for_task_started_nosleep(); + /* Let tasks settle. */ + msleep(50 * MSEC); } return test_fuzz_one_input(data, size); 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) |