/* usbredirparserfuzz.cc -- fuzzing for usbredirparser
Copyright 2021 Michael Hanselmann
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "usbredirfilter.h"
#include "usbredirparser.h"
namespace {
struct ParserDeleter {
void operator()(struct usbredirparser *p) {
usbredirparser_destroy(p);
}
};
std::unique_ptr parser;
std::unique_ptr fdp;
void log(const char *format, ...)
{
#if 0
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
#endif
}
void parser_log(void *priv, int level, const char *msg)
{
log("[%d] %s\n", level, msg);
}
int parser_read(void *priv, uint8_t *data, int count)
{
log("%s: %d bytes\n", __func__, count);
return fdp->ConsumeData(data, count);
}
int parser_write(void *priv, uint8_t *data, int count)
{
log("%s: %d bytes\n", __func__, count);
// Read over complete source buffer to detect buffer overflows on write
void *buf = malloc(count);
memcpy(buf, data, count);
free(buf);
return count;
}
void parser_device_connect(void *priv,
struct usb_redir_device_connect_header *device_connect)
{
log("%s: speed=%d, class=%d, subclass=%d, protocol=%d, vendor=%04x,"
" product=%04x\n",
__func__,
device_connect->speed,
device_connect->device_class,
device_connect->device_subclass,
device_connect->device_protocol,
device_connect->vendor_id,
device_connect->product_id);
}
void parser_device_disconnect(void *priv)
{
log("%s\n", __func__);
}
void parser_reset(void *priv)
{
log("%s\n", __func__);
}
void parser_interface_info(void *priv,
struct usb_redir_interface_info_header *info)
{
uint32_t i;
log("%s:", __func__);
for (i = 0; i < info->interface_count; i++) {
log(" [interface %d, class %d, subclass %d, protocol %d]",
info->interface[i], info->interface_class[i],
info->interface_subclass[i], info->interface_protocol[i]);
}
log("\n");
}
void parser_ep_info(void *priv,
struct usb_redir_ep_info_header *ep_info)
{
int i;
log("%s:", __func__);
for (i = 0; i < 32; i++) {
if (ep_info->type[i] != usb_redir_type_invalid) {
log(" [index %d, type %d, interval %d, interface %d,"
" max-packetsize %d]",
i, (int)ep_info->type[i], (int)ep_info->interval[i],
(int)ep_info->interface[i], ep_info->max_packet_size[i]);
}
}
log("\n");
}
void parser_set_configuration(void *priv, uint64_t id,
struct usb_redir_set_configuration_header *set_configuration)
{
}
void parser_get_configuration(void *priv, uint64_t id)
{
}
void parser_configuration_status(void *priv, uint64_t id,
struct usb_redir_configuration_status_header *config_status)
{
log("%s: id=%" PRIu64 ", status=%d, configuration=%d\n",
__func__, id, config_status->status, config_status->configuration);
}
void parser_set_alt_setting(void *priv, uint64_t id,
struct usb_redir_set_alt_setting_header *set_alt_setting)
{
}
void parser_get_alt_setting(void *priv, uint64_t id,
struct usb_redir_get_alt_setting_header *get_alt_setting)
{
}
void parser_alt_setting_status(void *priv, uint64_t id,
struct usb_redir_alt_setting_status_header *alt_setting_status)
{
log("%s: id=%" PRIu64 ", status=%d, interface=%d, alt=%d\n",
__func__, id,
alt_setting_status->status,
alt_setting_status->interface,
alt_setting_status->alt);
}
void parser_start_iso_stream(void *priv, uint64_t id,
struct usb_redir_start_iso_stream_header *start_iso_stream)
{
}
void parser_stop_iso_stream(void *priv, uint64_t id,
struct usb_redir_stop_iso_stream_header *stop_iso_stream)
{
}
void parser_iso_stream_status(void *priv, uint64_t id,
struct usb_redir_iso_stream_status_header *iso_stream_status)
{
}
void parser_start_interrupt_receiving(void *priv, uint64_t id,
struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving)
{
}
void parser_stop_interrupt_receiving(void *priv, uint64_t id,
struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving)
{
}
void parser_interrupt_receiving_status(void *priv, uint64_t id,
struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status)
{
}
void parser_alloc_bulk_streams(void *priv, uint64_t id,
struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams)
{
}
void parser_free_bulk_streams(void *priv, uint64_t id,
struct usb_redir_free_bulk_streams_header *free_bulk_streams)
{
}
void parser_bulk_streams_status(void *priv, uint64_t id,
struct usb_redir_bulk_streams_status_header *bulk_streams_status)
{
}
void parser_cancel_data_packet(void *priv, uint64_t id)
{
}
void parser_filter_reject(void *priv)
{
}
void parser_filter_filter(void *priv,
struct usbredirfilter_rule *rules, int rules_count)
{
usbredirfilter_free(rules);
}
void dump_data(const uint8_t *data, const int len)
{
int i;
if (len == 0) {
return;
}
log(" ");
for (i = 0; i < len; i++) {
log(" %02X", (unsigned int)data[i]);
}
log("\n");
}
void parser_control_packet(void *priv, uint64_t id,
struct usb_redir_control_packet_header *control_packet,
uint8_t *data, int data_len)
{
log("%s: id=%" PRIu64 ", endpoint=%d, request=%d, requesttype=%d,"
" status=%d, value=%d, index=%d, length=%d\n",
__func__, id,
control_packet->endpoint,
control_packet->request,
control_packet->requesttype,
control_packet->status,
control_packet->value,
control_packet->index,
control_packet->length);
dump_data(data, data_len);
usbredirparser_free_packet_data(parser.get(), data);
}
void parser_bulk_packet(void *priv, uint64_t id,
struct usb_redir_bulk_packet_header *bulk_packet,
uint8_t *data, int data_len)
{
log("%s: id=%" PRIu64 ", endpoint=%d, status=%d, length=%d, stream_id=%d,"
" length_high=%d\n",
__func__, id,
bulk_packet->endpoint,
bulk_packet->status,
bulk_packet->length,
bulk_packet->stream_id,
bulk_packet->length_high);
dump_data(data, data_len);
usbredirparser_free_packet_data(parser.get(), data);
}
void parser_iso_packet(void *priv, uint64_t id,
struct usb_redir_iso_packet_header *iso_packet,
uint8_t *data, int data_len)
{
log("%s: id=%" PRIu64 ", endpoint=%d, status=%d, length=%d\n",
__func__, id,
iso_packet->endpoint,
iso_packet->status,
iso_packet->length);
dump_data(data, data_len);
usbredirparser_free_packet_data(parser.get(), data);
}
void parser_interrupt_packet(void *priv, uint64_t id,
struct usb_redir_interrupt_packet_header *interrupt_packet,
uint8_t *data, int data_len)
{
log("%s: id=%" PRIu64 ", endpoint=%d, status=%d, length=%d\n",
__func__, id,
interrupt_packet->endpoint,
interrupt_packet->status,
interrupt_packet->length);
dump_data(data, data_len);
usbredirparser_free_packet_data(parser.get(), data);
}
void parser_buffered_bulk_packet(void *priv, uint64_t id,
struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header,
uint8_t *data, int data_len)
{
log("%s: stream_id=%d, length=%d, endpoint=%d, status=%d\n",
__func__, id,
buffered_bulk_header->stream_id,
buffered_bulk_header->length,
buffered_bulk_header->endpoint,
buffered_bulk_header->status);
dump_data(data, data_len);
usbredirparser_free_packet_data(parser.get(), data);
}
void *parser_alloc_lock()
{
return nullptr;
}
void parser_lock(void *lock)
{
}
void parser_unlock(void *lock)
{
}
void parser_free_lock(void *lock)
{
}
void parser_hello(void *priv, struct usb_redir_hello_header *h)
{
log("%s: %s\n", __func__, h->version);
}
void parser_device_disconnect_ack(void *priv)
{
}
void parser_start_bulk_receiving(void *priv, uint64_t id,
struct usb_redir_start_bulk_receiving_header *start_bulk_receiving)
{
}
void parser_stop_bulk_receiving(void *priv, uint64_t id,
struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving)
{
}
void parser_bulk_receiving_status(void *priv, uint64_t id,
struct usb_redir_bulk_receiving_status_header *bulk_receiving_status)
{
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::array caps = {0};
int ret;
fdp = std::make_unique(data, size);
parser.reset(usbredirparser_create());
if (parser == nullptr) {
return 1;
}
parser->log_func = parser_log;
parser->read_func = parser_read;
parser->write_func = parser_write;
parser->device_connect_func = parser_device_connect;
parser->device_disconnect_func = parser_device_disconnect;
parser->reset_func = parser_reset;
parser->interface_info_func = parser_interface_info;
parser->ep_info_func = parser_ep_info;
parser->set_configuration_func = parser_set_configuration;
parser->get_configuration_func = parser_get_configuration;
parser->configuration_status_func = parser_configuration_status;
parser->set_alt_setting_func = parser_set_alt_setting;
parser->get_alt_setting_func = parser_get_alt_setting;
parser->alt_setting_status_func = parser_alt_setting_status;
parser->start_iso_stream_func = parser_start_iso_stream;
parser->stop_iso_stream_func = parser_stop_iso_stream;
parser->iso_stream_status_func = parser_iso_stream_status;
parser->start_interrupt_receiving_func = parser_start_interrupt_receiving;
parser->stop_interrupt_receiving_func = parser_stop_interrupt_receiving;
parser->interrupt_receiving_status_func = parser_interrupt_receiving_status;
parser->alloc_bulk_streams_func = parser_alloc_bulk_streams;
parser->free_bulk_streams_func = parser_free_bulk_streams;
parser->bulk_streams_status_func = parser_bulk_streams_status;
parser->cancel_data_packet_func = parser_cancel_data_packet;
parser->control_packet_func = parser_control_packet;
parser->bulk_packet_func = parser_bulk_packet;
parser->iso_packet_func = parser_iso_packet;
parser->interrupt_packet_func = parser_interrupt_packet;
parser->alloc_lock_func = parser_alloc_lock;
parser->lock_func = parser_lock;
parser->unlock_func = parser_unlock;
parser->free_lock_func = parser_free_lock;
parser->hello_func = parser_hello;
parser->filter_reject_func = parser_filter_reject;
parser->filter_filter_func = parser_filter_filter;
parser->device_disconnect_ack_func = parser_device_disconnect_ack;
parser->start_bulk_receiving_func = parser_start_bulk_receiving;
parser->stop_bulk_receiving_func = parser_stop_bulk_receiving;
parser->bulk_receiving_status_func = parser_bulk_receiving_status;
parser->buffered_bulk_packet_func = parser_buffered_bulk_packet;
for (uint32_t &cap : caps) {
cap = fdp->ConsumeIntegral();
}
int init_flags = 0;
if (fdp->ConsumeBool()) {
init_flags |= usbredirparser_fl_usb_host;
}
usbredirparser_init(parser.get(), "fuzzer", caps.data(), caps.size(),
init_flags);
while (fdp->remaining_bytes() > 0) {
ret = usbredirparser_do_read(parser.get());
if (ret != 0) {
log("usbredirparser_do_read failed: %d\n", ret);
goto out;
}
while (usbredirparser_has_data_to_write(parser.get())) {
ret = usbredirparser_do_write(parser.get());
if (ret != 0) {
log("usbredirparser_do_write failed: %d\n", ret);
goto out;
}
}
}
out:
parser.reset();
return 0;
}
/* vim: set sw=4 sts=4 et : */