170 lines
4.1 KiB
C
170 lines
4.1 KiB
C
/* 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 <pthread.h>
|
|
#include <sys/time.h>
|
|
|
|
#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_BIT(0)
|
|
#define TASK_EVENT_HOSTCMD_DONE TASK_EVENT_CUSTOM_BIT(1)
|
|
|
|
/* 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;
|
|
}
|
|
|