per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
1094 lines
31 KiB
C
1094 lines
31 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
* Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
|
|
*/
|
|
|
|
#include "spdk/stdinc.h"
|
|
#include "spdk/env.h"
|
|
#include "spdk/json.h"
|
|
#include "spdk/event.h"
|
|
#include "spdk/likely.h"
|
|
#include "spdk/util.h"
|
|
#include "spdk/string.h"
|
|
#include "spdk_internal/virtio.h"
|
|
#include "spdk_internal/vhost_user.h"
|
|
|
|
#include "fuzz_common.h"
|
|
#include "vhost_fuzz.h"
|
|
|
|
#include <linux/virtio_blk.h>
|
|
#include <linux/virtio_scsi.h>
|
|
|
|
/* Features desired/implemented by virtio blk. */
|
|
#define VIRTIO_BLK_DEV_SUPPORTED_FEATURES \
|
|
(1ULL << VIRTIO_BLK_F_BLK_SIZE | \
|
|
1ULL << VIRTIO_BLK_F_TOPOLOGY | \
|
|
1ULL << VIRTIO_BLK_F_MQ | \
|
|
1ULL << VIRTIO_BLK_F_RO | \
|
|
1ULL << VIRTIO_BLK_F_DISCARD | \
|
|
1ULL << VIRTIO_RING_F_EVENT_IDX | \
|
|
1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
|
|
|
|
/* Features desired/implemented by virtio scsi. */
|
|
#define VIRTIO_SCSI_DEV_SUPPORTED_FEATURES \
|
|
(1ULL << VIRTIO_SCSI_F_INOUT | \
|
|
1ULL << VIRTIO_SCSI_F_HOTPLUG | \
|
|
1ULL << VIRTIO_RING_F_EVENT_IDX | \
|
|
1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
|
|
|
|
#define VIRTIO_DEV_FIXED_QUEUES 2
|
|
#define VIRTIO_SCSI_CONTROLQ 0
|
|
#define VIRTIO_SCSI_EVENTQ 1
|
|
#define VIRTIO_REQUESTQ 2
|
|
#define FUZZ_MAX_QUEUES 3
|
|
|
|
#define FUZZ_QUEUE_DEPTH 128
|
|
|
|
#define BLK_IO_NAME "vhost_blk_cmd"
|
|
#define SCSI_IO_NAME "vhost_scsi_cmd"
|
|
#define SCSI_MGMT_NAME "vhost_scsi_mgmt_cmd"
|
|
|
|
struct fuzz_vhost_iov_ctx {
|
|
struct iovec iov_req;
|
|
struct iovec iov_data;
|
|
struct iovec iov_resp;
|
|
};
|
|
|
|
struct fuzz_vhost_io_ctx {
|
|
struct fuzz_vhost_iov_ctx iovs;
|
|
union {
|
|
struct virtio_blk_outhdr blk_req;
|
|
struct virtio_scsi_cmd_req scsi_req;
|
|
struct virtio_scsi_ctrl_tmf_req scsi_tmf_req;
|
|
} req;
|
|
union {
|
|
uint8_t blk_resp;
|
|
struct virtio_scsi_cmd_resp scsi_resp;
|
|
union {
|
|
struct virtio_scsi_ctrl_tmf_resp scsi_tmf_resp;
|
|
struct virtio_scsi_ctrl_an_resp an_resp;
|
|
} scsi_tmf_resp;
|
|
} resp;
|
|
|
|
TAILQ_ENTRY(fuzz_vhost_io_ctx) link;
|
|
};
|
|
|
|
struct fuzz_vhost_dev_ctx {
|
|
struct virtio_dev virtio_dev;
|
|
struct spdk_thread *thread;
|
|
struct spdk_poller *poller;
|
|
|
|
struct fuzz_vhost_io_ctx *io_ctx_array;
|
|
TAILQ_HEAD(, fuzz_vhost_io_ctx) free_io_ctx;
|
|
TAILQ_HEAD(, fuzz_vhost_io_ctx) outstanding_io_ctx;
|
|
|
|
unsigned int random_seed;
|
|
|
|
uint64_t submitted_io;
|
|
uint64_t completed_io;
|
|
uint64_t successful_io;
|
|
uint64_t timeout_tsc;
|
|
|
|
bool socket_is_blk;
|
|
bool test_scsi_tmf;
|
|
bool valid_lun;
|
|
bool use_bogus_buffer;
|
|
bool use_valid_buffer;
|
|
bool timed_out;
|
|
|
|
TAILQ_ENTRY(fuzz_vhost_dev_ctx) link;
|
|
};
|
|
|
|
/* Global run state */
|
|
uint64_t g_runtime_ticks;
|
|
int g_runtime;
|
|
int g_num_active_threads;
|
|
bool g_run = true;
|
|
bool g_verbose_mode = false;
|
|
|
|
/* Global resources */
|
|
TAILQ_HEAD(, fuzz_vhost_dev_ctx) g_dev_list = TAILQ_HEAD_INITIALIZER(g_dev_list);
|
|
struct spdk_poller *g_run_poller;
|
|
void *g_valid_buffer;
|
|
unsigned int g_random_seed;
|
|
|
|
|
|
/* Global parameters and resources for parsed commands */
|
|
bool g_keep_iov_pointers = false;
|
|
char *g_json_file = NULL;
|
|
struct fuzz_vhost_io_ctx *g_blk_cmd_array = NULL;
|
|
struct fuzz_vhost_io_ctx *g_scsi_cmd_array = NULL;
|
|
struct fuzz_vhost_io_ctx *g_scsi_mgmt_cmd_array = NULL;
|
|
|
|
size_t g_blk_cmd_array_size;
|
|
size_t g_scsi_cmd_array_size;
|
|
size_t g_scsi_mgmt_cmd_array_size;
|
|
|
|
static void
|
|
cleanup(void)
|
|
{
|
|
struct fuzz_vhost_dev_ctx *dev_ctx, *tmp;
|
|
printf("Fuzzing completed.\n");
|
|
TAILQ_FOREACH_SAFE(dev_ctx, &g_dev_list, link, tmp) {
|
|
printf("device %p stats: Completed I/O: %lu, Successful I/O: %lu\n", dev_ctx,
|
|
dev_ctx->completed_io, dev_ctx->successful_io);
|
|
virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_REQUESTQ);
|
|
if (!dev_ctx->socket_is_blk) {
|
|
virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_SCSI_EVENTQ);
|
|
virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_SCSI_CONTROLQ);
|
|
}
|
|
virtio_dev_stop(&dev_ctx->virtio_dev);
|
|
virtio_dev_destruct(&dev_ctx->virtio_dev);
|
|
if (dev_ctx->io_ctx_array) {
|
|
spdk_free(dev_ctx->io_ctx_array);
|
|
}
|
|
free(dev_ctx);
|
|
}
|
|
|
|
spdk_free(g_valid_buffer);
|
|
|
|
if (g_blk_cmd_array) {
|
|
free(g_blk_cmd_array);
|
|
}
|
|
if (g_scsi_cmd_array) {
|
|
free(g_scsi_cmd_array);
|
|
}
|
|
if (g_scsi_mgmt_cmd_array) {
|
|
free(g_scsi_mgmt_cmd_array);
|
|
}
|
|
}
|
|
|
|
/* Get a memory address that is random and not located in our hugepage memory. */
|
|
static void *
|
|
get_invalid_mem_address(uint64_t length)
|
|
{
|
|
uint64_t chosen_address = 0x0;
|
|
|
|
while (true) {
|
|
chosen_address = rand();
|
|
chosen_address = (chosen_address << 32) | rand();
|
|
if (spdk_vtophys((void *)chosen_address, &length) == SPDK_VTOPHYS_ERROR) {
|
|
return (void *)chosen_address;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* dev initialization code begin. */
|
|
static int
|
|
virtio_dev_init(struct virtio_dev *vdev, const char *socket_path, uint64_t flags,
|
|
uint16_t max_queues)
|
|
{
|
|
int rc;
|
|
|
|
rc = virtio_user_dev_init(vdev, "dev_ctx", socket_path, 1024);
|
|
if (rc != 0) {
|
|
fprintf(stderr, "Failed to initialize virtual bdev\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = virtio_dev_reset(vdev, flags);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
|
|
rc = virtio_dev_start(vdev, max_queues, VIRTIO_DEV_FIXED_QUEUES);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
|
|
rc = virtio_dev_acquire_queue(vdev, VIRTIO_REQUESTQ);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Couldn't get an unused queue for the io_channel.\n");
|
|
virtio_dev_stop(vdev);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
blk_dev_init(struct virtio_dev *vdev, const char *socket_path, uint16_t max_queues)
|
|
{
|
|
return virtio_dev_init(vdev, socket_path, VIRTIO_BLK_DEV_SUPPORTED_FEATURES, max_queues);
|
|
}
|
|
|
|
static int
|
|
scsi_dev_init(struct virtio_dev *vdev, const char *socket_path, uint16_t max_queues)
|
|
{
|
|
int rc;
|
|
|
|
rc = virtio_dev_init(vdev, socket_path, VIRTIO_SCSI_DEV_SUPPORTED_FEATURES, max_queues);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
|
|
rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_CONTROLQ);
|
|
if (rc != 0) {
|
|
SPDK_ERRLOG("Failed to acquire the controlq.\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_EVENTQ);
|
|
if (rc != 0) {
|
|
SPDK_ERRLOG("Failed to acquire the eventq.\n");
|
|
virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fuzz_vhost_dev_init(const char *socket_path, bool is_blk_dev, bool use_bogus_buffer,
|
|
bool use_valid_buffer, bool valid_lun, bool test_scsi_tmf)
|
|
{
|
|
struct fuzz_vhost_dev_ctx *dev_ctx;
|
|
int rc = 0, i;
|
|
|
|
dev_ctx = calloc(1, sizeof(*dev_ctx));
|
|
if (dev_ctx == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dev_ctx->socket_is_blk = is_blk_dev;
|
|
dev_ctx->use_bogus_buffer = use_bogus_buffer;
|
|
dev_ctx->use_valid_buffer = use_valid_buffer;
|
|
dev_ctx->valid_lun = valid_lun;
|
|
dev_ctx->test_scsi_tmf = test_scsi_tmf;
|
|
|
|
TAILQ_INIT(&dev_ctx->free_io_ctx);
|
|
TAILQ_INIT(&dev_ctx->outstanding_io_ctx);
|
|
|
|
assert(sizeof(*dev_ctx->io_ctx_array) <= UINT64_MAX / FUZZ_QUEUE_DEPTH);
|
|
dev_ctx->io_ctx_array = spdk_malloc(sizeof(*dev_ctx->io_ctx_array) * FUZZ_QUEUE_DEPTH, 0x0, NULL,
|
|
SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE);
|
|
if (dev_ctx->io_ctx_array == NULL) {
|
|
free(dev_ctx);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < FUZZ_QUEUE_DEPTH; i++) {
|
|
TAILQ_INSERT_HEAD(&dev_ctx->free_io_ctx, &dev_ctx->io_ctx_array[i], link);
|
|
}
|
|
|
|
dev_ctx->thread = spdk_thread_create(NULL, NULL);
|
|
if (dev_ctx->thread == NULL) {
|
|
fprintf(stderr, "Unable to allocate a thread for a fuzz device.\n");
|
|
rc = -ENOMEM;
|
|
goto error_out;
|
|
}
|
|
|
|
if (is_blk_dev) {
|
|
rc = blk_dev_init(&dev_ctx->virtio_dev, socket_path, FUZZ_MAX_QUEUES);
|
|
} else {
|
|
rc = scsi_dev_init(&dev_ctx->virtio_dev, socket_path, FUZZ_MAX_QUEUES);
|
|
}
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "Unable to prepare the device to perform I/O.\n");
|
|
goto error_out;
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(&g_dev_list, dev_ctx, link);
|
|
return 0;
|
|
|
|
error_out:
|
|
spdk_free(dev_ctx->io_ctx_array);
|
|
free(dev_ctx);
|
|
return rc;
|
|
}
|
|
/* dev initialization code end */
|
|
|
|
/* data dumping functions begin */
|
|
static int
|
|
dump_virtio_cmd(void *ctx, const void *data, size_t size)
|
|
{
|
|
fprintf(stderr, "%s\n", (const char *)data);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
print_blk_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
spdk_json_write_named_uint32(w, "type", io_ctx->req.blk_req.type);
|
|
spdk_json_write_named_uint32(w, "ioprio", io_ctx->req.blk_req.ioprio);
|
|
spdk_json_write_named_uint64(w, "sector", io_ctx->req.blk_req.sector);
|
|
}
|
|
|
|
static void
|
|
print_scsi_tmf_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
char *lun_data;
|
|
|
|
lun_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_tmf_req.lun,
|
|
sizeof(io_ctx->req.scsi_tmf_req.lun));
|
|
|
|
spdk_json_write_named_uint32(w, "type", io_ctx->req.scsi_tmf_req.type);
|
|
spdk_json_write_named_uint32(w, "subtype", io_ctx->req.scsi_tmf_req.subtype);
|
|
spdk_json_write_named_string(w, "lun", lun_data);
|
|
spdk_json_write_named_uint64(w, "tag", io_ctx->req.scsi_tmf_req.tag);
|
|
|
|
free(lun_data);
|
|
}
|
|
|
|
static void
|
|
print_scsi_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
char *lun_data;
|
|
char *cdb_data;
|
|
|
|
lun_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_req.lun,
|
|
sizeof(io_ctx->req.scsi_req.lun));
|
|
cdb_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_req.cdb,
|
|
sizeof(io_ctx->req.scsi_req.cdb));
|
|
|
|
spdk_json_write_named_string(w, "lun", lun_data);
|
|
spdk_json_write_named_uint64(w, "tag", io_ctx->req.scsi_req.tag);
|
|
spdk_json_write_named_uint32(w, "task_attr", io_ctx->req.scsi_req.task_attr);
|
|
spdk_json_write_named_uint32(w, "prio", io_ctx->req.scsi_req.prio);
|
|
spdk_json_write_named_uint32(w, "crn", io_ctx->req.scsi_req.crn);
|
|
spdk_json_write_named_string(w, "cdb", cdb_data);
|
|
|
|
free(lun_data);
|
|
free(cdb_data);
|
|
}
|
|
|
|
static void
|
|
print_iov_obj(struct spdk_json_write_ctx *w, const char *iov_name, struct iovec *iov)
|
|
{
|
|
/* "0x" + up to 16 digits + null terminator */
|
|
char hex_addr[19];
|
|
int rc;
|
|
|
|
rc = snprintf(hex_addr, 19, "%lx", (uintptr_t)iov->iov_base);
|
|
|
|
/* default to 0. */
|
|
if (rc < 0 || rc >= 19) {
|
|
hex_addr[0] = '0';
|
|
hex_addr[1] = '\0';
|
|
}
|
|
|
|
spdk_json_write_named_object_begin(w, iov_name);
|
|
spdk_json_write_named_string(w, "iov_base", hex_addr);
|
|
spdk_json_write_named_uint64(w, "iov_len", iov->iov_len);
|
|
spdk_json_write_object_end(w);
|
|
}
|
|
|
|
static void
|
|
print_iovs(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
print_iov_obj(w, "req_iov", &io_ctx->iovs.iov_req);
|
|
print_iov_obj(w, "data_iov", &io_ctx->iovs.iov_data);
|
|
print_iov_obj(w, "resp_iov", &io_ctx->iovs.iov_resp);
|
|
}
|
|
|
|
static void
|
|
print_req_obj(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
|
|
struct spdk_json_write_ctx *w;
|
|
|
|
w = spdk_json_write_begin(dump_virtio_cmd, NULL, SPDK_JSON_WRITE_FLAG_FORMATTED);
|
|
|
|
if (dev_ctx->socket_is_blk) {
|
|
spdk_json_write_named_object_begin(w, BLK_IO_NAME);
|
|
print_iovs(w, io_ctx);
|
|
print_blk_io_data(w, io_ctx);
|
|
} else if (dev_ctx->test_scsi_tmf) {
|
|
spdk_json_write_named_object_begin(w, SCSI_MGMT_NAME);
|
|
print_iovs(w, io_ctx);
|
|
print_scsi_tmf_io_data(w, io_ctx);
|
|
} else {
|
|
spdk_json_write_named_object_begin(w, SCSI_IO_NAME);
|
|
print_iovs(w, io_ctx);
|
|
print_scsi_io_data(w, io_ctx);
|
|
}
|
|
spdk_json_write_object_end(w);
|
|
spdk_json_write_end(w);
|
|
}
|
|
|
|
static void
|
|
dump_outstanding_io(struct fuzz_vhost_dev_ctx *dev_ctx)
|
|
{
|
|
struct fuzz_vhost_io_ctx *io_ctx, *tmp;
|
|
|
|
TAILQ_FOREACH_SAFE(io_ctx, &dev_ctx->outstanding_io_ctx, link, tmp) {
|
|
print_req_obj(dev_ctx, io_ctx);
|
|
TAILQ_REMOVE(&dev_ctx->outstanding_io_ctx, io_ctx, link);
|
|
TAILQ_INSERT_TAIL(&dev_ctx->free_io_ctx, io_ctx, link);
|
|
}
|
|
}
|
|
/* data dumping functions end */
|
|
|
|
/* data parsing functions begin */
|
|
static int
|
|
hex_value(uint8_t c)
|
|
{
|
|
#define V(x, y) [x] = y + 1
|
|
static const int8_t val[256] = {
|
|
V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
|
|
V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
|
|
V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
|
|
V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
|
|
};
|
|
#undef V
|
|
|
|
return val[c] - 1;
|
|
}
|
|
|
|
static int
|
|
fuzz_json_decode_hex_uint64(const struct spdk_json_val *val, void *out)
|
|
{
|
|
uint64_t *out_val = out;
|
|
size_t i;
|
|
char *val_pointer = val->start;
|
|
int current_val;
|
|
|
|
if (val->len > 16) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*out_val = 0;
|
|
for (i = 0; i < val->len; i++) {
|
|
*out_val = *out_val << 4;
|
|
current_val = hex_value(*val_pointer);
|
|
if (current_val < 0) {
|
|
return -EINVAL;
|
|
}
|
|
*out_val += current_val;
|
|
val_pointer++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct spdk_json_object_decoder fuzz_vhost_iov_decoders[] = {
|
|
{"iov_base", offsetof(struct iovec, iov_base), fuzz_json_decode_hex_uint64},
|
|
{"iov_len", offsetof(struct iovec, iov_len), spdk_json_decode_uint64},
|
|
};
|
|
|
|
static size_t
|
|
parse_iov_struct(struct iovec *iovec, struct spdk_json_val *value)
|
|
{
|
|
int rc;
|
|
|
|
if (value->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
|
|
return -1;
|
|
}
|
|
|
|
rc = spdk_json_decode_object(value,
|
|
fuzz_vhost_iov_decoders,
|
|
SPDK_COUNTOF(fuzz_vhost_iov_decoders),
|
|
iovec);
|
|
if (rc) {
|
|
return -1;
|
|
}
|
|
|
|
while (value->type != SPDK_JSON_VAL_OBJECT_END) {
|
|
value++;
|
|
rc++;
|
|
}
|
|
|
|
/* The +1 instructs the calling function to skip over the OBJECT_END function. */
|
|
rc += 1;
|
|
return rc;
|
|
}
|
|
|
|
static bool
|
|
parse_vhost_blk_cmds(void *item, struct spdk_json_val *value, size_t num_values)
|
|
{
|
|
struct fuzz_vhost_io_ctx *io_ctx = item;
|
|
struct spdk_json_val *prev_value;
|
|
int nested_object_size;
|
|
uint64_t tmp_val;
|
|
size_t i = 0;
|
|
|
|
while (i < num_values) {
|
|
nested_object_size = 1;
|
|
if (value->type == SPDK_JSON_VAL_NAME) {
|
|
prev_value = value;
|
|
value++;
|
|
i++;
|
|
if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
|
|
} else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
|
|
} else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
|
|
} else if (!strncmp(prev_value->start, "type", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.blk_req.type = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "ioprio", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.blk_req.ioprio = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "sector", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.blk_req.sector = tmp_val;
|
|
}
|
|
}
|
|
}
|
|
if (nested_object_size < 0) {
|
|
fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
|
|
(char *)prev_value->start, value->len, (char *)value->start);
|
|
return false;
|
|
}
|
|
value += nested_object_size;
|
|
i += nested_object_size;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
parse_vhost_scsi_cmds(void *item, struct spdk_json_val *value, size_t num_values)
|
|
{
|
|
struct fuzz_vhost_io_ctx *io_ctx = item;
|
|
struct spdk_json_val *prev_value;
|
|
int nested_object_size;
|
|
uint64_t tmp_val;
|
|
size_t i = 0;
|
|
|
|
while (i < num_values) {
|
|
nested_object_size = 1;
|
|
if (value->type == SPDK_JSON_VAL_NAME) {
|
|
prev_value = value;
|
|
value++;
|
|
i++;
|
|
if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
|
|
} else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
|
|
} else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
|
|
} else if (!strncmp(prev_value->start, "lun", prev_value->len)) {
|
|
if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_req.lun,
|
|
sizeof(io_ctx->req.scsi_req.lun),
|
|
(char *)value->start,
|
|
value->len)) {
|
|
nested_object_size = -1;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "tag", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_req.tag = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "task_attr", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_req.task_attr = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "prio", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_req.prio = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "crn", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_req.crn = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "cdb", prev_value->len)) {
|
|
if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_req.cdb,
|
|
sizeof(io_ctx->req.scsi_req.cdb),
|
|
(char *)value->start,
|
|
value->len)) {
|
|
nested_object_size = -1;
|
|
}
|
|
}
|
|
}
|
|
if (nested_object_size < 0) {
|
|
fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
|
|
(char *)prev_value->start, value->len, (char *)value->start);
|
|
return false;
|
|
}
|
|
value += nested_object_size;
|
|
i += nested_object_size;
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
static bool
|
|
parse_vhost_scsi_mgmt_cmds(void *item, struct spdk_json_val *value, size_t num_values)
|
|
{
|
|
struct fuzz_vhost_io_ctx *io_ctx = item;
|
|
struct spdk_json_val *prev_value;
|
|
int nested_object_size;
|
|
uint64_t tmp_val;
|
|
size_t i = 0;
|
|
|
|
while (i < num_values) {
|
|
nested_object_size = 1;
|
|
if (value->type == SPDK_JSON_VAL_NAME) {
|
|
prev_value = value;
|
|
value++;
|
|
i++;
|
|
if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
|
|
} else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
|
|
} else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
|
|
nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
|
|
} else if (!strncmp(prev_value->start, "type", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_tmf_req.type = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "subtype", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_tmf_req.subtype = tmp_val;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "lun", prev_value->len)) {
|
|
if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_tmf_req.lun,
|
|
sizeof(io_ctx->req.scsi_tmf_req.lun),
|
|
(char *)value->start,
|
|
value->len)) {
|
|
nested_object_size = -1;
|
|
}
|
|
} else if (!strncmp(prev_value->start, "tag", prev_value->len)) {
|
|
if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
|
|
nested_object_size = -1;
|
|
} else {
|
|
io_ctx->req.scsi_tmf_req.tag = tmp_val;
|
|
}
|
|
}
|
|
}
|
|
if (nested_object_size < 0) {
|
|
fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
|
|
(char *)prev_value->start, value->len, (char *)value->start);
|
|
return false;
|
|
}
|
|
value += nested_object_size;
|
|
i += nested_object_size;
|
|
}
|
|
return true;
|
|
}
|
|
/* data parsing functions end */
|
|
|
|
/* build requests begin */
|
|
static void
|
|
craft_io_from_array(struct fuzz_vhost_io_ctx *src_ctx, struct fuzz_vhost_io_ctx *dest_ctx)
|
|
{
|
|
if (g_keep_iov_pointers) {
|
|
dest_ctx->iovs = src_ctx->iovs;
|
|
}
|
|
dest_ctx->req = src_ctx->req;
|
|
}
|
|
|
|
static void
|
|
craft_virtio_scsi_req(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.scsi_req);
|
|
io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.scsi_resp);
|
|
fuzz_fill_random_bytes((char *)&io_ctx->req.scsi_req, sizeof(io_ctx->req.scsi_req),
|
|
&dev_ctx->random_seed);
|
|
/* TODO: set up the logic to find all luns on the target. Right now we are just assuming the first is OK. */
|
|
if (dev_ctx->valid_lun) {
|
|
io_ctx->req.scsi_req.lun[0] = 1;
|
|
io_ctx->req.scsi_req.lun[1] = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
craft_virtio_scsi_tmf_req(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.scsi_tmf_req);
|
|
io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.scsi_tmf_resp);
|
|
fuzz_fill_random_bytes((char *)&io_ctx->req.scsi_tmf_req, sizeof(io_ctx->req.scsi_tmf_req),
|
|
&dev_ctx->random_seed);
|
|
/* TODO: set up the logic to find all luns on the target. Right now we are just assuming the first is OK. */
|
|
if (dev_ctx->valid_lun) {
|
|
io_ctx->req.scsi_tmf_req.lun[0] = 1;
|
|
io_ctx->req.scsi_tmf_req.lun[1] = 0;
|
|
}
|
|
|
|
/* Valid controlq commands have to be of type 0, 1, or 2. Any others just return immediately from the target. */
|
|
/* Try to only test the opcodes that will exercise extra paths in the target side. But allow for at least one invalid value. */
|
|
io_ctx->req.scsi_tmf_req.type = rand() % 4;
|
|
}
|
|
|
|
static void
|
|
craft_virtio_blk_req(struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.blk_req);
|
|
io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.blk_resp);
|
|
io_ctx->req.blk_req.type = rand();
|
|
io_ctx->req.blk_req.sector = rand();
|
|
}
|
|
|
|
static void
|
|
craft_virtio_req_rsp_pair(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
struct fuzz_vhost_iov_ctx *iovs = &io_ctx->iovs;
|
|
|
|
/*
|
|
* Always set these buffer values up front.
|
|
* If the user wants to override this with the json values,
|
|
* they can specify -k when starting the app. */
|
|
iovs->iov_req.iov_base = &io_ctx->req;
|
|
if (dev_ctx->use_bogus_buffer) {
|
|
iovs->iov_data.iov_len = rand();
|
|
iovs->iov_data.iov_base = get_invalid_mem_address(iovs->iov_data.iov_len);
|
|
} else if (dev_ctx->use_valid_buffer) {
|
|
iovs->iov_data.iov_len = 1024;
|
|
iovs->iov_data.iov_base = g_valid_buffer;
|
|
}
|
|
iovs->iov_resp.iov_base = &io_ctx->resp;
|
|
|
|
if (dev_ctx->socket_is_blk && g_blk_cmd_array) {
|
|
craft_io_from_array(&g_blk_cmd_array[dev_ctx->submitted_io], io_ctx);
|
|
return;
|
|
} else if (dev_ctx->test_scsi_tmf && g_scsi_mgmt_cmd_array) {
|
|
craft_io_from_array(&g_scsi_mgmt_cmd_array[dev_ctx->submitted_io], io_ctx);
|
|
return;
|
|
} else if (g_scsi_cmd_array) {
|
|
craft_io_from_array(&g_scsi_cmd_array[dev_ctx->submitted_io], io_ctx);
|
|
return;
|
|
}
|
|
|
|
if (dev_ctx->socket_is_blk) {
|
|
craft_virtio_blk_req(io_ctx);
|
|
} else if (dev_ctx->test_scsi_tmf) {
|
|
craft_virtio_scsi_tmf_req(dev_ctx, io_ctx);
|
|
} else {
|
|
craft_virtio_scsi_req(dev_ctx, io_ctx);
|
|
}
|
|
}
|
|
/* build requests end */
|
|
|
|
/* submit requests begin */
|
|
static uint64_t
|
|
get_max_num_io(struct fuzz_vhost_dev_ctx *dev_ctx)
|
|
{
|
|
if (dev_ctx->socket_is_blk) {
|
|
return g_blk_cmd_array_size;
|
|
} else if (dev_ctx->test_scsi_tmf) {
|
|
return g_scsi_mgmt_cmd_array_size;
|
|
} else {
|
|
return g_scsi_cmd_array_size;
|
|
}
|
|
}
|
|
|
|
static int
|
|
submit_virtio_req_rsp_pair(struct fuzz_vhost_dev_ctx *dev_ctx, struct virtqueue *vq,
|
|
struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
struct fuzz_vhost_iov_ctx *iovs = &io_ctx->iovs;
|
|
int num_iovs = 2, rc;
|
|
|
|
num_iovs += dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer ? 1 : 0;
|
|
|
|
rc = virtqueue_req_start(vq, io_ctx, num_iovs);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
virtqueue_req_add_iovs(vq, &iovs->iov_req, 1, SPDK_VIRTIO_DESC_RO);
|
|
/* blk and scsi requests favor different orders for the iov objects. */
|
|
if (dev_ctx->socket_is_blk) {
|
|
if (dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer) {
|
|
virtqueue_req_add_iovs(vq, &iovs->iov_data, 1, SPDK_VIRTIO_DESC_WR);
|
|
}
|
|
virtqueue_req_add_iovs(vq, &iovs->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
|
|
} else {
|
|
virtqueue_req_add_iovs(vq, &iovs->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
|
|
if (dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer) {
|
|
virtqueue_req_add_iovs(vq, &iovs->iov_data, 1, SPDK_VIRTIO_DESC_WR);
|
|
}
|
|
}
|
|
virtqueue_req_flush(vq);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
dev_submit_requests(struct fuzz_vhost_dev_ctx *dev_ctx, struct virtqueue *vq,
|
|
uint64_t max_io_to_submit)
|
|
{
|
|
struct fuzz_vhost_io_ctx *io_ctx;
|
|
int rc;
|
|
|
|
while (!TAILQ_EMPTY(&dev_ctx->free_io_ctx) && dev_ctx->submitted_io < max_io_to_submit) {
|
|
io_ctx = TAILQ_FIRST(&dev_ctx->free_io_ctx);
|
|
craft_virtio_req_rsp_pair(dev_ctx, io_ctx);
|
|
rc = submit_virtio_req_rsp_pair(dev_ctx, vq, io_ctx);
|
|
if (rc == 0) {
|
|
TAILQ_REMOVE(&dev_ctx->free_io_ctx, io_ctx, link);
|
|
TAILQ_INSERT_TAIL(&dev_ctx->outstanding_io_ctx, io_ctx, link);
|
|
dev_ctx->submitted_io++;
|
|
} else if (rc == -ENOMEM) {
|
|
/* There are just not enough available buffers right now. try later. */
|
|
return;
|
|
} else if (rc == -EINVAL) {
|
|
/* The virtqueue must be broken. We know we can fit at least three descriptors */
|
|
fprintf(stderr, "One of the virtqueues for dev %p is broken. stopping all devices.\n", dev_ctx);
|
|
g_run = 0;
|
|
}
|
|
}
|
|
}
|
|
/* submit requests end */
|
|
|
|
/* complete requests begin */
|
|
static void
|
|
check_successful_op(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
bool is_successful = false;
|
|
|
|
if (dev_ctx->socket_is_blk) {
|
|
if (io_ctx->resp.blk_resp == 0) {
|
|
is_successful = true;
|
|
}
|
|
} else if (dev_ctx->test_scsi_tmf) {
|
|
if (io_ctx->resp.scsi_tmf_resp.scsi_tmf_resp.response == 0 &&
|
|
io_ctx->resp.scsi_tmf_resp.an_resp.response == 0) {
|
|
is_successful = true;
|
|
}
|
|
} else {
|
|
if (io_ctx->resp.scsi_resp.status == 0) {
|
|
is_successful = true;
|
|
}
|
|
}
|
|
|
|
if (is_successful) {
|
|
fprintf(stderr, "An I/O completed without an error status. This could be worth looking into.\n");
|
|
fprintf(stderr,
|
|
"There is also a good chance that the target just failed before setting a status.\n");
|
|
dev_ctx->successful_io++;
|
|
print_req_obj(dev_ctx, io_ctx);
|
|
} else if (g_verbose_mode) {
|
|
fprintf(stderr, "The following I/O failed as expected.\n");
|
|
print_req_obj(dev_ctx, io_ctx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
complete_io(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
|
|
{
|
|
TAILQ_REMOVE(&dev_ctx->outstanding_io_ctx, io_ctx, link);
|
|
TAILQ_INSERT_HEAD(&dev_ctx->free_io_ctx, io_ctx, link);
|
|
check_successful_op(dev_ctx, io_ctx);
|
|
dev_ctx->completed_io++;
|
|
dev_ctx->timeout_tsc = fuzz_refresh_timeout();
|
|
}
|
|
|
|
static int
|
|
poll_dev(void *ctx)
|
|
{
|
|
struct fuzz_vhost_dev_ctx *dev_ctx = ctx;
|
|
struct virtqueue *vq;
|
|
struct fuzz_vhost_io_ctx *io_ctx[FUZZ_QUEUE_DEPTH];
|
|
int num_active_threads;
|
|
uint64_t max_io_to_complete = UINT64_MAX;
|
|
uint64_t current_ticks;
|
|
uint32_t len[FUZZ_QUEUE_DEPTH];
|
|
uint16_t num_cpl, i;
|
|
|
|
if (g_json_file) {
|
|
max_io_to_complete = get_max_num_io(dev_ctx);
|
|
}
|
|
|
|
if (!dev_ctx->socket_is_blk && dev_ctx->test_scsi_tmf) {
|
|
vq = dev_ctx->virtio_dev.vqs[VIRTIO_SCSI_CONTROLQ];
|
|
} else {
|
|
vq = dev_ctx->virtio_dev.vqs[VIRTIO_REQUESTQ];
|
|
}
|
|
|
|
num_cpl = virtio_recv_pkts(vq, (void **)io_ctx, len, FUZZ_QUEUE_DEPTH);
|
|
|
|
for (i = 0; i < num_cpl; i++) {
|
|
complete_io(dev_ctx, io_ctx[i]);
|
|
}
|
|
|
|
current_ticks = spdk_get_ticks();
|
|
|
|
if (current_ticks > dev_ctx->timeout_tsc) {
|
|
dev_ctx->timed_out = true;
|
|
g_run = false;
|
|
fprintf(stderr, "The VQ on device %p timed out. Dumping contents now.\n", dev_ctx);
|
|
dump_outstanding_io(dev_ctx);
|
|
}
|
|
|
|
if (current_ticks > g_runtime_ticks) {
|
|
g_run = 0;
|
|
}
|
|
|
|
if (!g_run || dev_ctx->completed_io >= max_io_to_complete) {
|
|
if (TAILQ_EMPTY(&dev_ctx->outstanding_io_ctx)) {
|
|
spdk_poller_unregister(&dev_ctx->poller);
|
|
num_active_threads = __sync_sub_and_fetch(&g_num_active_threads, 1);
|
|
if (num_active_threads == 0) {
|
|
g_run = 0;
|
|
}
|
|
spdk_thread_exit(dev_ctx->thread);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
dev_submit_requests(dev_ctx, vq, max_io_to_complete);
|
|
return 0;
|
|
}
|
|
/* complete requests end */
|
|
|
|
static void
|
|
start_io(void *ctx)
|
|
{
|
|
struct fuzz_vhost_dev_ctx *dev_ctx = ctx;
|
|
|
|
if (g_random_seed) {
|
|
dev_ctx->random_seed = g_random_seed;
|
|
} else {
|
|
dev_ctx->random_seed = spdk_get_ticks();
|
|
}
|
|
|
|
dev_ctx->timeout_tsc = fuzz_refresh_timeout();
|
|
|
|
dev_ctx->poller = SPDK_POLLER_REGISTER(poll_dev, dev_ctx, 0);
|
|
if (dev_ctx->poller == NULL) {
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
static int
|
|
end_fuzz(void *ctx)
|
|
{
|
|
if (!g_run && !g_num_active_threads) {
|
|
spdk_poller_unregister(&g_run_poller);
|
|
cleanup();
|
|
spdk_app_stop(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
begin_fuzz(void *ctx)
|
|
{
|
|
struct fuzz_vhost_dev_ctx *dev_ctx;
|
|
|
|
g_runtime_ticks = spdk_get_ticks() + spdk_get_ticks_hz() * g_runtime;
|
|
|
|
g_valid_buffer = spdk_malloc(0x1000, 0x200, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE);
|
|
if (g_valid_buffer == NULL) {
|
|
fprintf(stderr, "Failed to allocate a valid buffer for I/O\n");
|
|
goto out;
|
|
}
|
|
|
|
g_run_poller = SPDK_POLLER_REGISTER(end_fuzz, NULL, 0);
|
|
if (g_run_poller == NULL) {
|
|
fprintf(stderr, "Failed to register a poller for test completion checking.\n");
|
|
}
|
|
|
|
TAILQ_FOREACH(dev_ctx, &g_dev_list, link) {
|
|
assert(dev_ctx->thread != NULL);
|
|
spdk_thread_send_msg(dev_ctx->thread, start_io, dev_ctx);
|
|
__sync_add_and_fetch(&g_num_active_threads, 1);
|
|
}
|
|
|
|
return;
|
|
out:
|
|
cleanup();
|
|
spdk_app_stop(0);
|
|
}
|
|
|
|
static void
|
|
fuzz_vhost_usage(void)
|
|
{
|
|
fprintf(stderr, " -j <path> Path to a json file containing named objects.\n");
|
|
fprintf(stderr,
|
|
" -k Keep the iov pointer addresses from the json file. only valid with -j.\n");
|
|
fprintf(stderr, " -S <integer> Seed value for test.\n");
|
|
fprintf(stderr, " -t <integer> Time in seconds to run the fuzz test.\n");
|
|
fprintf(stderr, " -V Enable logging of each submitted command.\n");
|
|
}
|
|
|
|
static int
|
|
fuzz_vhost_parse(int ch, char *arg)
|
|
{
|
|
int64_t error_test;
|
|
|
|
switch (ch) {
|
|
case 'j':
|
|
g_json_file = optarg;
|
|
break;
|
|
case 'k':
|
|
g_keep_iov_pointers = true;
|
|
break;
|
|
case 'S':
|
|
error_test = spdk_strtol(arg, 10);
|
|
if (error_test < 0) {
|
|
fprintf(stderr, "Invalid value supplied for the random seed.\n");
|
|
return -1;
|
|
} else {
|
|
g_random_seed = spdk_strtol(arg, 10);
|
|
}
|
|
break;
|
|
case 't':
|
|
g_runtime = spdk_strtol(arg, 10);
|
|
if (g_runtime < 0 || g_runtime > MAX_RUNTIME_S) {
|
|
fprintf(stderr, "You must supply a positive runtime value less than 86401.\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 'V':
|
|
g_verbose_mode = true;
|
|
break;
|
|
case '?':
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct spdk_app_opts opts = {};
|
|
int rc;
|
|
|
|
spdk_app_opts_init(&opts, sizeof(opts));
|
|
opts.name = "vhost_fuzz";
|
|
g_runtime = DEFAULT_RUNTIME;
|
|
|
|
rc = spdk_app_parse_args(argc, argv, &opts, "j:kS:t:V", NULL, fuzz_vhost_parse, fuzz_vhost_usage);
|
|
if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) {
|
|
fprintf(stderr, "Unable to parse the application arguments.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (g_json_file != NULL) {
|
|
g_blk_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
|
|
(void **)&g_blk_cmd_array,
|
|
sizeof(struct fuzz_vhost_io_ctx),
|
|
BLK_IO_NAME, parse_vhost_blk_cmds);
|
|
g_scsi_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
|
|
(void **)&g_scsi_cmd_array,
|
|
sizeof(struct fuzz_vhost_io_ctx),
|
|
SCSI_IO_NAME, parse_vhost_scsi_cmds);
|
|
g_scsi_mgmt_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
|
|
(void **)&g_scsi_mgmt_cmd_array,
|
|
sizeof(struct fuzz_vhost_io_ctx),
|
|
SCSI_IO_NAME, parse_vhost_scsi_mgmt_cmds);
|
|
if (g_blk_cmd_array_size == 0 && g_scsi_cmd_array_size == 0 && g_scsi_mgmt_cmd_array_size == 0) {
|
|
fprintf(stderr, "The provided json file did not contain any valid commands. Exiting.\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
rc = spdk_app_start(&opts, begin_fuzz, NULL);
|
|
|
|
spdk_app_fini();
|
|
return rc;
|
|
}
|