diff options
Diffstat (limited to 'fuzz/usb_pd_fuzz.c')
-rw-r--r-- | fuzz/usb_pd_fuzz.c | 217 |
1 files changed, 217 insertions, 0 deletions
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; +} |