/* 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 #include #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; }