LCOV - code coverage report
Current view: top level - spdk/test/app/fuzz/vhost_fuzz - vhost_fuzz.c (source / functions) Hit Total Coverage
Test: Combined Lines: 412 545 75.6 %
Date: 2024-07-15 20:55:59 Functions: 34 37 91.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 259 484 53.5 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2019 Intel Corporation. All rights reserved.
       3                 :            :  *   Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "spdk/stdinc.h"
       7                 :            : #include "spdk/env.h"
       8                 :            : #include "spdk/json.h"
       9                 :            : #include "spdk/event.h"
      10                 :            : #include "spdk/likely.h"
      11                 :            : #include "spdk/util.h"
      12                 :            : #include "spdk/string.h"
      13                 :            : #include "spdk_internal/virtio.h"
      14                 :            : #include "spdk_internal/vhost_user.h"
      15                 :            : 
      16                 :            : #include "fuzz_common.h"
      17                 :            : #include "vhost_fuzz.h"
      18                 :            : 
      19                 :            : #include <linux/virtio_blk.h>
      20                 :            : #include <linux/virtio_scsi.h>
      21                 :            : 
      22                 :            : /* Features desired/implemented by virtio blk. */
      23                 :            : #define VIRTIO_BLK_DEV_SUPPORTED_FEATURES               \
      24                 :            :         (1ULL << VIRTIO_BLK_F_BLK_SIZE            |       \
      25                 :            :          1ULL << VIRTIO_BLK_F_TOPOLOGY            |       \
      26                 :            :          1ULL << VIRTIO_BLK_F_MQ          |       \
      27                 :            :          1ULL << VIRTIO_BLK_F_RO          |       \
      28                 :            :          1ULL << VIRTIO_BLK_F_DISCARD             |       \
      29                 :            :          1ULL << VIRTIO_RING_F_EVENT_IDX  |       \
      30                 :            :          1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
      31                 :            : 
      32                 :            : /* Features desired/implemented by virtio scsi. */
      33                 :            : #define VIRTIO_SCSI_DEV_SUPPORTED_FEATURES              \
      34                 :            :         (1ULL << VIRTIO_SCSI_F_INOUT              |       \
      35                 :            :          1ULL << VIRTIO_SCSI_F_HOTPLUG            |       \
      36                 :            :          1ULL << VIRTIO_RING_F_EVENT_IDX  |       \
      37                 :            :          1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
      38                 :            : 
      39                 :            : #define VIRTIO_DEV_FIXED_QUEUES 2
      40                 :            : #define VIRTIO_SCSI_CONTROLQ    0
      41                 :            : #define VIRTIO_SCSI_EVENTQ      1
      42                 :            : #define VIRTIO_REQUESTQ         2
      43                 :            : #define FUZZ_MAX_QUEUES         3
      44                 :            : 
      45                 :            : #define FUZZ_QUEUE_DEPTH        128
      46                 :            : 
      47                 :            : #define BLK_IO_NAME             "vhost_blk_cmd"
      48                 :            : #define SCSI_IO_NAME            "vhost_scsi_cmd"
      49                 :            : #define SCSI_MGMT_NAME          "vhost_scsi_mgmt_cmd"
      50                 :            : 
      51                 :            : struct fuzz_vhost_iov_ctx {
      52                 :            :         struct iovec                    iov_req;
      53                 :            :         struct iovec                    iov_data;
      54                 :            :         struct iovec                    iov_resp;
      55                 :            : };
      56                 :            : 
      57                 :            : struct fuzz_vhost_io_ctx {
      58                 :            :         struct fuzz_vhost_iov_ctx               iovs;
      59                 :            :         union {
      60                 :            :                 struct virtio_blk_outhdr        blk_req;
      61                 :            :                 struct virtio_scsi_cmd_req      scsi_req;
      62                 :            :                 struct virtio_scsi_ctrl_tmf_req scsi_tmf_req;
      63                 :            :         } req;
      64                 :            :         union {
      65                 :            :                 uint8_t                                 blk_resp;
      66                 :            :                 struct virtio_scsi_cmd_resp             scsi_resp;
      67                 :            :                 union {
      68                 :            :                         struct virtio_scsi_ctrl_tmf_resp        scsi_tmf_resp;
      69                 :            :                         struct virtio_scsi_ctrl_an_resp         an_resp;
      70                 :            :                 } scsi_tmf_resp;
      71                 :            :         } resp;
      72                 :            : 
      73                 :            :         TAILQ_ENTRY(fuzz_vhost_io_ctx) link;
      74                 :            : };
      75                 :            : 
      76                 :            : struct fuzz_vhost_dev_ctx {
      77                 :            :         struct virtio_dev                       virtio_dev;
      78                 :            :         struct spdk_thread                      *thread;
      79                 :            :         struct spdk_poller                      *poller;
      80                 :            : 
      81                 :            :         struct fuzz_vhost_io_ctx                *io_ctx_array;
      82                 :            :         TAILQ_HEAD(, fuzz_vhost_io_ctx)         free_io_ctx;
      83                 :            :         TAILQ_HEAD(, fuzz_vhost_io_ctx)         outstanding_io_ctx;
      84                 :            : 
      85                 :            :         unsigned int                            random_seed;
      86                 :            : 
      87                 :            :         uint64_t                                submitted_io;
      88                 :            :         uint64_t                                completed_io;
      89                 :            :         uint64_t                                successful_io;
      90                 :            :         uint64_t                                timeout_tsc;
      91                 :            : 
      92                 :            :         bool                                    socket_is_blk;
      93                 :            :         bool                                    test_scsi_tmf;
      94                 :            :         bool                                    valid_lun;
      95                 :            :         bool                                    use_bogus_buffer;
      96                 :            :         bool                                    use_valid_buffer;
      97                 :            :         bool                                    timed_out;
      98                 :            : 
      99                 :            :         TAILQ_ENTRY(fuzz_vhost_dev_ctx) link;
     100                 :            : };
     101                 :            : 
     102                 :            : /* Global run state */
     103                 :            : uint64_t                                g_runtime_ticks;
     104                 :            : int                                     g_runtime;
     105                 :            : int                                     g_num_active_threads;
     106                 :            : bool                                    g_run = true;
     107                 :            : bool                                    g_verbose_mode = false;
     108                 :            : 
     109                 :            : /* Global resources */
     110                 :            : TAILQ_HEAD(, fuzz_vhost_dev_ctx)        g_dev_list = TAILQ_HEAD_INITIALIZER(g_dev_list);
     111                 :            : struct spdk_poller                      *g_run_poller;
     112                 :            : void                                    *g_valid_buffer;
     113                 :            : unsigned int                            g_random_seed;
     114                 :            : 
     115                 :            : 
     116                 :            : /* Global parameters and resources for parsed commands */
     117                 :            : bool                                    g_keep_iov_pointers = false;
     118                 :            : char                                    *g_json_file = NULL;
     119                 :            : struct fuzz_vhost_io_ctx                *g_blk_cmd_array = NULL;
     120                 :            : struct fuzz_vhost_io_ctx                *g_scsi_cmd_array = NULL;
     121                 :            : struct fuzz_vhost_io_ctx                *g_scsi_mgmt_cmd_array = NULL;
     122                 :            : 
     123                 :            : size_t                                  g_blk_cmd_array_size;
     124                 :            : size_t                                  g_scsi_cmd_array_size;
     125                 :            : size_t                                  g_scsi_mgmt_cmd_array_size;
     126                 :            : 
     127                 :            : static void
     128                 :          2 : cleanup(void)
     129                 :            : {
     130                 :            :         struct fuzz_vhost_dev_ctx *dev_ctx, *tmp;
     131         [ -  + ]:          2 :         printf("Fuzzing completed.\n");
     132         [ +  + ]:          8 :         TAILQ_FOREACH_SAFE(dev_ctx, &g_dev_list, link, tmp) {
     133         [ -  + ]:          6 :                 printf("device %p stats: Completed I/O: %lu, Successful I/O: %lu\n", dev_ctx,
     134                 :            :                        dev_ctx->completed_io, dev_ctx->successful_io);
     135                 :          6 :                 virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_REQUESTQ);
     136   [ -  +  +  + ]:          6 :                 if (!dev_ctx->socket_is_blk) {
     137                 :          4 :                         virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_SCSI_EVENTQ);
     138                 :          4 :                         virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_SCSI_CONTROLQ);
     139                 :            :                 }
     140                 :          6 :                 virtio_dev_stop(&dev_ctx->virtio_dev);
     141                 :          6 :                 virtio_dev_destruct(&dev_ctx->virtio_dev);
     142         [ +  - ]:          6 :                 if (dev_ctx->io_ctx_array) {
     143                 :          6 :                         spdk_free(dev_ctx->io_ctx_array);
     144                 :            :                 }
     145                 :          6 :                 free(dev_ctx);
     146                 :            :         }
     147                 :            : 
     148                 :          2 :         spdk_free(g_valid_buffer);
     149                 :            : 
     150         [ +  + ]:          2 :         if (g_blk_cmd_array) {
     151                 :          1 :                 free(g_blk_cmd_array);
     152                 :            :         }
     153         [ +  + ]:          2 :         if (g_scsi_cmd_array) {
     154                 :          1 :                 free(g_scsi_cmd_array);
     155                 :            :         }
     156         [ +  + ]:          2 :         if (g_scsi_mgmt_cmd_array) {
     157                 :          1 :                 free(g_scsi_mgmt_cmd_array);
     158                 :            :         }
     159                 :          2 : }
     160                 :            : 
     161                 :            : /* Get a memory address that is random and not located in our hugepage memory. */
     162                 :            : static void *
     163                 :          0 : get_invalid_mem_address(uint64_t length)
     164                 :            : {
     165                 :          0 :         uint64_t chosen_address = 0x0;
     166                 :            : 
     167                 :            :         while (true) {
     168                 :          0 :                 chosen_address = rand();
     169                 :          0 :                 chosen_address = (chosen_address << 32) | rand();
     170         [ #  # ]:          0 :                 if (spdk_vtophys((void *)chosen_address, &length) == SPDK_VTOPHYS_ERROR) {
     171                 :          0 :                         return (void *)chosen_address;
     172                 :            :                 }
     173                 :            :         }
     174                 :            :         return NULL;
     175                 :            : }
     176                 :            : 
     177                 :            : /* dev initialization code begin. */
     178                 :            : static int
     179                 :          6 : virtio_dev_init(struct virtio_dev *vdev, const char *socket_path, uint64_t flags,
     180                 :            :                 uint16_t max_queues)
     181                 :            : {
     182                 :            :         int rc;
     183                 :            : 
     184                 :          6 :         rc = virtio_user_dev_init(vdev, "dev_ctx", socket_path, 1024);
     185         [ -  + ]:          6 :         if (rc != 0) {
     186   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to initialize virtual bdev\n");
     187                 :          0 :                 return rc;
     188                 :            :         }
     189                 :            : 
     190                 :          6 :         rc = virtio_dev_reset(vdev, flags);
     191         [ -  + ]:          6 :         if (rc != 0) {
     192                 :          0 :                 return rc;
     193                 :            :         }
     194                 :            : 
     195                 :          6 :         rc = virtio_dev_start(vdev, max_queues, VIRTIO_DEV_FIXED_QUEUES);
     196         [ -  + ]:          6 :         if (rc != 0) {
     197                 :          0 :                 return rc;
     198                 :            :         }
     199                 :            : 
     200                 :          6 :         rc = virtio_dev_acquire_queue(vdev, VIRTIO_REQUESTQ);
     201         [ -  + ]:          6 :         if (rc < 0) {
     202   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Couldn't get an unused queue for the io_channel.\n");
     203                 :          0 :                 virtio_dev_stop(vdev);
     204                 :          0 :                 return rc;
     205                 :            :         }
     206                 :          6 :         return 0;
     207                 :            : }
     208                 :            : 
     209                 :            : static int
     210                 :          2 : blk_dev_init(struct virtio_dev *vdev, const char *socket_path, uint16_t max_queues)
     211                 :            : {
     212                 :          2 :         return virtio_dev_init(vdev, socket_path, VIRTIO_BLK_DEV_SUPPORTED_FEATURES, max_queues);
     213                 :            : }
     214                 :            : 
     215                 :            : static int
     216                 :          4 : scsi_dev_init(struct virtio_dev *vdev, const char *socket_path, uint16_t max_queues)
     217                 :            : {
     218                 :            :         int rc;
     219                 :            : 
     220                 :          4 :         rc = virtio_dev_init(vdev, socket_path, VIRTIO_SCSI_DEV_SUPPORTED_FEATURES, max_queues);
     221         [ -  + ]:          4 :         if (rc != 0) {
     222                 :          0 :                 return rc;
     223                 :            :         }
     224                 :            : 
     225                 :          4 :         rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_CONTROLQ);
     226         [ -  + ]:          4 :         if (rc != 0) {
     227                 :          0 :                 SPDK_ERRLOG("Failed to acquire the controlq.\n");
     228                 :          0 :                 return rc;
     229                 :            :         }
     230                 :            : 
     231                 :          4 :         rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_EVENTQ);
     232         [ -  + ]:          4 :         if (rc != 0) {
     233                 :          0 :                 SPDK_ERRLOG("Failed to acquire the eventq.\n");
     234                 :          0 :                 virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
     235                 :          0 :                 return rc;
     236                 :            :         }
     237                 :            : 
     238                 :          4 :         return 0;
     239                 :            : }
     240                 :            : 
     241                 :            : int
     242                 :          6 : fuzz_vhost_dev_init(const char *socket_path, bool is_blk_dev, bool use_bogus_buffer,
     243                 :            :                     bool use_valid_buffer, bool valid_lun, bool test_scsi_tmf)
     244                 :            : {
     245                 :            :         struct fuzz_vhost_dev_ctx *dev_ctx;
     246                 :          6 :         int rc = 0, i;
     247                 :            : 
     248                 :          6 :         dev_ctx = calloc(1, sizeof(*dev_ctx));
     249         [ -  + ]:          6 :         if (dev_ctx == NULL) {
     250                 :          0 :                 return -ENOMEM;
     251                 :            :         }
     252                 :            : 
     253                 :          6 :         dev_ctx->socket_is_blk = is_blk_dev;
     254                 :          6 :         dev_ctx->use_bogus_buffer = use_bogus_buffer;
     255                 :          6 :         dev_ctx->use_valid_buffer = use_valid_buffer;
     256                 :          6 :         dev_ctx->valid_lun = valid_lun;
     257                 :          6 :         dev_ctx->test_scsi_tmf = test_scsi_tmf;
     258                 :            : 
     259                 :          6 :         TAILQ_INIT(&dev_ctx->free_io_ctx);
     260                 :          6 :         TAILQ_INIT(&dev_ctx->outstanding_io_ctx);
     261                 :            : 
     262                 :            :         assert(sizeof(*dev_ctx->io_ctx_array) <= UINT64_MAX / FUZZ_QUEUE_DEPTH);
     263                 :          6 :         dev_ctx->io_ctx_array = spdk_malloc(sizeof(*dev_ctx->io_ctx_array) * FUZZ_QUEUE_DEPTH, 0x0, NULL,
     264                 :            :                                             SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE);
     265         [ -  + ]:          6 :         if (dev_ctx->io_ctx_array == NULL) {
     266                 :          0 :                 free(dev_ctx);
     267                 :          0 :                 return -ENOMEM;
     268                 :            :         }
     269                 :            : 
     270         [ +  + ]:        774 :         for (i = 0; i < FUZZ_QUEUE_DEPTH; i++) {
     271         [ +  + ]:        768 :                 TAILQ_INSERT_HEAD(&dev_ctx->free_io_ctx, &dev_ctx->io_ctx_array[i], link);
     272                 :            :         }
     273                 :            : 
     274                 :          6 :         dev_ctx->thread = spdk_thread_create(NULL, NULL);
     275         [ -  + ]:          6 :         if (dev_ctx->thread == NULL) {
     276   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to allocate a thread for a fuzz device.\n");
     277                 :          0 :                 rc = -ENOMEM;
     278                 :          0 :                 goto error_out;
     279                 :            :         }
     280                 :            : 
     281         [ +  + ]:          6 :         if (is_blk_dev) {
     282                 :          2 :                 rc = blk_dev_init(&dev_ctx->virtio_dev, socket_path, FUZZ_MAX_QUEUES);
     283                 :            :         } else {
     284                 :          4 :                 rc = scsi_dev_init(&dev_ctx->virtio_dev, socket_path, FUZZ_MAX_QUEUES);
     285                 :            :         }
     286                 :            : 
     287         [ -  + ]:          6 :         if (rc) {
     288   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to prepare the device to perform I/O.\n");
     289                 :          0 :                 goto error_out;
     290                 :            :         }
     291                 :            : 
     292                 :          6 :         TAILQ_INSERT_TAIL(&g_dev_list, dev_ctx, link);
     293                 :          6 :         return 0;
     294                 :            : 
     295                 :          0 : error_out:
     296                 :          0 :         spdk_free(dev_ctx->io_ctx_array);
     297                 :          0 :         free(dev_ctx);
     298                 :          0 :         return rc;
     299                 :            : }
     300                 :            : /* dev initialization code end */
     301                 :            : 
     302                 :            : /* data dumping functions begin */
     303                 :            : static int
     304                 :        534 : dump_virtio_cmd(void *ctx, const void *data, size_t size)
     305                 :            : {
     306   [ -  +  -  + ]:        534 :         fprintf(stderr, "%s\n", (const char *)data);
     307                 :        534 :         return 0;
     308                 :            : }
     309                 :            : 
     310                 :            : static void
     311                 :          1 : print_blk_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
     312                 :            : {
     313                 :          1 :         spdk_json_write_named_uint32(w, "type", io_ctx->req.blk_req.type);
     314                 :          1 :         spdk_json_write_named_uint32(w, "ioprio", io_ctx->req.blk_req.ioprio);
     315                 :          1 :         spdk_json_write_named_uint64(w, "sector", io_ctx->req.blk_req.sector);
     316                 :          1 : }
     317                 :            : 
     318                 :            : static void
     319                 :         37 : print_scsi_tmf_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
     320                 :            : {
     321                 :            :         char *lun_data;
     322                 :            : 
     323                 :         37 :         lun_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_tmf_req.lun,
     324                 :            :                         sizeof(io_ctx->req.scsi_tmf_req.lun));
     325                 :            : 
     326                 :         37 :         spdk_json_write_named_uint32(w, "type", io_ctx->req.scsi_tmf_req.type);
     327                 :         37 :         spdk_json_write_named_uint32(w, "subtype", io_ctx->req.scsi_tmf_req.subtype);
     328                 :         37 :         spdk_json_write_named_string(w, "lun", lun_data);
     329                 :         37 :         spdk_json_write_named_uint64(w, "tag", io_ctx->req.scsi_tmf_req.tag);
     330                 :            : 
     331                 :         37 :         free(lun_data);
     332                 :         37 : }
     333                 :            : 
     334                 :            : static void
     335                 :        496 : print_scsi_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
     336                 :            : {
     337                 :            :         char *lun_data;
     338                 :            :         char *cdb_data;
     339                 :            : 
     340                 :        496 :         lun_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_req.lun,
     341                 :            :                         sizeof(io_ctx->req.scsi_req.lun));
     342                 :        496 :         cdb_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_req.cdb,
     343                 :            :                         sizeof(io_ctx->req.scsi_req.cdb));
     344                 :            : 
     345                 :        496 :         spdk_json_write_named_string(w, "lun", lun_data);
     346                 :        496 :         spdk_json_write_named_uint64(w, "tag", io_ctx->req.scsi_req.tag);
     347                 :        496 :         spdk_json_write_named_uint32(w, "task_attr", io_ctx->req.scsi_req.task_attr);
     348                 :        496 :         spdk_json_write_named_uint32(w, "prio", io_ctx->req.scsi_req.prio);
     349                 :        496 :         spdk_json_write_named_uint32(w, "crn", io_ctx->req.scsi_req.crn);
     350                 :        496 :         spdk_json_write_named_string(w, "cdb", cdb_data);
     351                 :            : 
     352                 :        496 :         free(lun_data);
     353                 :        496 :         free(cdb_data);
     354                 :        496 : }
     355                 :            : 
     356                 :            : static void
     357                 :       1602 : print_iov_obj(struct spdk_json_write_ctx *w, const char *iov_name, struct iovec *iov)
     358                 :            : {
     359                 :            :         /* "0x" + up to 16 digits + null terminator */
     360                 :          0 :         char hex_addr[19];
     361                 :            :         int rc;
     362                 :            : 
     363         [ -  + ]:       1602 :         rc = snprintf(hex_addr, 19, "%lx", (uintptr_t)iov->iov_base);
     364                 :            : 
     365                 :            :         /* default to 0. */
     366   [ +  -  -  + ]:       1602 :         if (rc < 0 || rc >= 19) {
     367                 :          0 :                 hex_addr[0] = '0';
     368                 :          0 :                 hex_addr[1] = '\0';
     369                 :            :         }
     370                 :            : 
     371                 :       1602 :         spdk_json_write_named_object_begin(w, iov_name);
     372                 :       1602 :         spdk_json_write_named_string(w, "iov_base", hex_addr);
     373                 :       1602 :         spdk_json_write_named_uint64(w, "iov_len", iov->iov_len);
     374                 :       1602 :         spdk_json_write_object_end(w);
     375                 :       1602 : }
     376                 :            : 
     377                 :            : static void
     378                 :        534 : print_iovs(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
     379                 :            : {
     380                 :        534 :         print_iov_obj(w, "req_iov", &io_ctx->iovs.iov_req);
     381                 :        534 :         print_iov_obj(w, "data_iov", &io_ctx->iovs.iov_data);
     382                 :        534 :         print_iov_obj(w, "resp_iov", &io_ctx->iovs.iov_resp);
     383                 :        534 : }
     384                 :            : 
     385                 :            : static void
     386                 :        534 : print_req_obj(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
     387                 :            : {
     388                 :            : 
     389                 :            :         struct spdk_json_write_ctx *w;
     390                 :            : 
     391                 :        534 :         w = spdk_json_write_begin(dump_virtio_cmd, NULL, SPDK_JSON_WRITE_FLAG_FORMATTED);
     392                 :            : 
     393   [ -  +  +  + ]:        534 :         if (dev_ctx->socket_is_blk) {
     394                 :          1 :                 spdk_json_write_named_object_begin(w, BLK_IO_NAME);
     395                 :          1 :                 print_iovs(w, io_ctx);
     396                 :          1 :                 print_blk_io_data(w, io_ctx);
     397   [ -  +  +  + ]:        533 :         } else if (dev_ctx->test_scsi_tmf) {
     398                 :         37 :                 spdk_json_write_named_object_begin(w, SCSI_MGMT_NAME);
     399                 :         37 :                 print_iovs(w, io_ctx);
     400                 :         37 :                 print_scsi_tmf_io_data(w, io_ctx);
     401                 :            :         } else {
     402                 :        496 :                 spdk_json_write_named_object_begin(w, SCSI_IO_NAME);
     403                 :        496 :                 print_iovs(w, io_ctx);
     404                 :        496 :                 print_scsi_io_data(w, io_ctx);
     405                 :            :         }
     406                 :        534 :         spdk_json_write_object_end(w);
     407                 :        534 :         spdk_json_write_end(w);
     408                 :        534 : }
     409                 :            : 
     410                 :            : static void
     411                 :          0 : dump_outstanding_io(struct fuzz_vhost_dev_ctx *dev_ctx)
     412                 :            : {
     413                 :            :         struct fuzz_vhost_io_ctx *io_ctx, *tmp;
     414                 :            : 
     415         [ #  # ]:          0 :         TAILQ_FOREACH_SAFE(io_ctx, &dev_ctx->outstanding_io_ctx, link, tmp) {
     416                 :          0 :                 print_req_obj(dev_ctx, io_ctx);
     417         [ #  # ]:          0 :                 TAILQ_REMOVE(&dev_ctx->outstanding_io_ctx, io_ctx, link);
     418                 :          0 :                 TAILQ_INSERT_TAIL(&dev_ctx->free_io_ctx, io_ctx, link);
     419                 :            :         }
     420                 :          0 : }
     421                 :            : /* data dumping functions end */
     422                 :            : 
     423                 :            : /* data parsing functions begin */
     424                 :            : static int
     425                 :        180 : hex_value(uint8_t c)
     426                 :            : {
     427                 :            : #define V(x, y) [x] = y + 1
     428                 :            :         static const int8_t val[256] = {
     429                 :            :                 V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
     430                 :            :                 V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
     431                 :            :                 V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
     432                 :            :                 V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
     433                 :            :         };
     434                 :            : #undef V
     435                 :            : 
     436                 :        180 :         return val[c] - 1;
     437                 :            : }
     438                 :            : 
     439                 :            : static int
     440                 :         15 : fuzz_json_decode_hex_uint64(const struct spdk_json_val *val, void *out)
     441                 :            : {
     442                 :         15 :         uint64_t *out_val = out;
     443                 :            :         size_t i;
     444                 :         15 :         char *val_pointer = val->start;
     445                 :            :         int current_val;
     446                 :            : 
     447         [ -  + ]:         15 :         if (val->len > 16) {
     448                 :          0 :                 return -EINVAL;
     449                 :            :         }
     450                 :            : 
     451                 :         15 :         *out_val = 0;
     452         [ +  + ]:        195 :         for (i = 0; i < val->len; i++) {
     453                 :        180 :                 *out_val = *out_val << 4;
     454                 :        180 :                 current_val = hex_value(*val_pointer);
     455         [ -  + ]:        180 :                 if (current_val < 0) {
     456                 :          0 :                         return -EINVAL;
     457                 :            :                 }
     458                 :        180 :                 *out_val += current_val;
     459                 :        180 :                 val_pointer++;
     460                 :            :         }
     461                 :            : 
     462                 :         15 :         return 0;
     463                 :            : }
     464                 :            : 
     465                 :            : static const struct spdk_json_object_decoder fuzz_vhost_iov_decoders[] = {
     466                 :            :         {"iov_base", offsetof(struct iovec, iov_base), fuzz_json_decode_hex_uint64},
     467                 :            :         {"iov_len", offsetof(struct iovec, iov_len), spdk_json_decode_uint64},
     468                 :            : };
     469                 :            : 
     470                 :            : static size_t
     471                 :         15 : parse_iov_struct(struct iovec *iovec, struct spdk_json_val *value)
     472                 :            : {
     473                 :            :         int rc;
     474                 :            : 
     475         [ -  + ]:         15 :         if (value->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
     476                 :          0 :                 return -1;
     477                 :            :         }
     478                 :            : 
     479                 :         15 :         rc = spdk_json_decode_object(value,
     480                 :            :                                      fuzz_vhost_iov_decoders,
     481                 :            :                                      SPDK_COUNTOF(fuzz_vhost_iov_decoders),
     482                 :            :                                      iovec);
     483         [ -  + ]:         15 :         if (rc) {
     484                 :          0 :                 return -1;
     485                 :            :         }
     486                 :            : 
     487         [ +  + ]:         90 :         while (value->type != SPDK_JSON_VAL_OBJECT_END) {
     488                 :         75 :                 value++;
     489                 :         75 :                 rc++;
     490                 :            :         }
     491                 :            : 
     492                 :            :         /* The +1 instructs the calling function to skip over the OBJECT_END function. */
     493                 :         15 :         rc += 1;
     494                 :         15 :         return rc;
     495                 :            : }
     496                 :            : 
     497                 :            : static bool
     498                 :          1 : parse_vhost_blk_cmds(void *item, struct spdk_json_val *value, size_t num_values)
     499                 :            : {
     500                 :          1 :         struct fuzz_vhost_io_ctx *io_ctx = item;
     501                 :            :         struct spdk_json_val *prev_value;
     502                 :            :         int nested_object_size;
     503                 :          0 :         uint64_t tmp_val;
     504                 :          1 :         size_t i = 0;
     505                 :            : 
     506         [ +  + ]:          9 :         while (i < num_values) {
     507                 :          8 :                 nested_object_size = 1;
     508         [ +  + ]:          8 :                 if (value->type == SPDK_JSON_VAL_NAME) {
     509                 :          6 :                         prev_value = value;
     510                 :          6 :                         value++;
     511                 :          6 :                         i++;
     512   [ -  +  +  + ]:          6 :                         if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
     513                 :          1 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
     514   [ -  +  +  + ]:          5 :                         } else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
     515                 :          1 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
     516   [ -  +  +  + ]:          4 :                         } else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
     517                 :          1 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
     518   [ -  +  +  + ]:          3 :                         } else if (!strncmp(prev_value->start, "type", prev_value->len)) {
     519         [ -  + ]:          1 :                                 if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
     520                 :          0 :                                         nested_object_size = -1;
     521                 :            :                                 } else {
     522                 :          1 :                                         io_ctx->req.blk_req.type = tmp_val;
     523                 :            :                                 }
     524   [ -  +  +  + ]:          2 :                         } else if (!strncmp(prev_value->start, "ioprio", prev_value->len)) {
     525         [ -  + ]:          1 :                                 if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
     526                 :          0 :                                         nested_object_size = -1;
     527                 :            :                                 } else {
     528                 :          1 :                                         io_ctx->req.blk_req.ioprio = tmp_val;
     529                 :            :                                 }
     530   [ -  +  +  - ]:          1 :                         } else if (!strncmp(prev_value->start, "sector", prev_value->len)) {
     531         [ -  + ]:          1 :                                 if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
     532                 :          0 :                                         nested_object_size = -1;
     533                 :            :                                 } else {
     534                 :          1 :                                         io_ctx->req.blk_req.sector = tmp_val;
     535                 :            :                                 }
     536                 :            :                         }
     537                 :            :                 }
     538         [ -  + ]:          8 :                 if (nested_object_size < 0) {
     539         [ #  # ]:          0 :                         fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
     540                 :          0 :                                 (char *)prev_value->start, value->len, (char *)value->start);
     541                 :          0 :                         return false;
     542                 :            :                 }
     543                 :          8 :                 value += nested_object_size;
     544                 :          8 :                 i += nested_object_size;
     545                 :            :         }
     546                 :          1 :         return true;
     547                 :            : }
     548                 :            : 
     549                 :            : static bool
     550                 :          2 : parse_vhost_scsi_cmds(void *item, struct spdk_json_val *value, size_t num_values)
     551                 :            : {
     552                 :          2 :         struct fuzz_vhost_io_ctx *io_ctx = item;
     553                 :            :         struct spdk_json_val *prev_value;
     554                 :            :         int nested_object_size;
     555                 :          0 :         uint64_t tmp_val;
     556                 :          2 :         size_t i = 0;
     557                 :            : 
     558         [ +  + ]:         20 :         while (i < num_values) {
     559                 :         18 :                 nested_object_size = 1;
     560         [ +  + ]:         18 :                 if (value->type == SPDK_JSON_VAL_NAME) {
     561                 :         14 :                         prev_value = value;
     562                 :         14 :                         value++;
     563                 :         14 :                         i++;
     564   [ -  +  +  + ]:         14 :                         if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
     565                 :          2 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
     566   [ -  +  +  + ]:         12 :                         } else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
     567                 :          2 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
     568   [ -  +  +  + ]:         10 :                         } else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
     569                 :          2 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
     570   [ -  +  +  + ]:          8 :                         } else if (!strncmp(prev_value->start, "lun", prev_value->len)) {
     571         [ -  + ]:          2 :                                 if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_req.lun,
     572                 :            :                                                                   sizeof(io_ctx->req.scsi_req.lun),
     573                 :          2 :                                                                   (char *)value->start,
     574                 :          2 :                                                                   value->len)) {
     575                 :          0 :                                         nested_object_size = -1;
     576                 :            :                                 }
     577   [ -  +  +  + ]:          6 :                         } else if (!strncmp(prev_value->start, "tag", prev_value->len)) {
     578         [ -  + ]:          2 :                                 if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
     579                 :          0 :                                         nested_object_size = -1;
     580                 :            :                                 } else {
     581                 :          2 :                                         io_ctx->req.scsi_req.tag = tmp_val;
     582                 :            :                                 }
     583   [ -  +  -  + ]:          4 :                         } else if (!strncmp(prev_value->start, "task_attr", prev_value->len)) {
     584         [ #  # ]:          0 :                                 if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
     585                 :          0 :                                         nested_object_size = -1;
     586                 :            :                                 } else {
     587                 :          0 :                                         io_ctx->req.scsi_req.task_attr = tmp_val;
     588                 :            :                                 }
     589   [ -  +  -  + ]:          4 :                         } else if (!strncmp(prev_value->start, "prio", prev_value->len)) {
     590         [ #  # ]:          0 :                                 if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
     591                 :          0 :                                         nested_object_size = -1;
     592                 :            :                                 } else {
     593                 :          0 :                                         io_ctx->req.scsi_req.prio = tmp_val;
     594                 :            :                                 }
     595   [ -  +  -  + ]:          4 :                         } else if (!strncmp(prev_value->start, "crn", prev_value->len)) {
     596         [ #  # ]:          0 :                                 if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
     597                 :          0 :                                         nested_object_size = -1;
     598                 :            :                                 } else {
     599                 :          0 :                                         io_ctx->req.scsi_req.crn = tmp_val;
     600                 :            :                                 }
     601   [ -  +  -  + ]:          4 :                         } else if (!strncmp(prev_value->start, "cdb", prev_value->len)) {
     602         [ #  # ]:          0 :                                 if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_req.cdb,
     603                 :            :                                                                   sizeof(io_ctx->req.scsi_req.cdb),
     604                 :          0 :                                                                   (char *)value->start,
     605                 :          0 :                                                                   value->len)) {
     606                 :          0 :                                         nested_object_size = -1;
     607                 :            :                                 }
     608                 :            :                         }
     609                 :            :                 }
     610         [ -  + ]:         18 :                 if (nested_object_size < 0) {
     611         [ #  # ]:          0 :                         fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
     612                 :          0 :                                 (char *)prev_value->start, value->len, (char *)value->start);
     613                 :          0 :                         return false;
     614                 :            :                 }
     615                 :         18 :                 value += nested_object_size;
     616                 :         18 :                 i += nested_object_size;
     617                 :            :         }
     618                 :          2 :         return true;
     619                 :            : 
     620                 :            : }
     621                 :            : 
     622                 :            : static bool
     623                 :          2 : parse_vhost_scsi_mgmt_cmds(void *item, struct spdk_json_val *value, size_t num_values)
     624                 :            : {
     625                 :          2 :         struct fuzz_vhost_io_ctx *io_ctx = item;
     626                 :            :         struct spdk_json_val *prev_value;
     627                 :            :         int nested_object_size;
     628                 :          0 :         uint64_t tmp_val;
     629                 :          2 :         size_t i = 0;
     630                 :            : 
     631         [ +  + ]:         20 :         while (i < num_values) {
     632                 :         18 :                 nested_object_size = 1;
     633         [ +  + ]:         18 :                 if (value->type == SPDK_JSON_VAL_NAME) {
     634                 :         14 :                         prev_value = value;
     635                 :         14 :                         value++;
     636                 :         14 :                         i++;
     637   [ -  +  +  + ]:         14 :                         if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
     638                 :          2 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
     639   [ -  +  +  + ]:         12 :                         } else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
     640                 :          2 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
     641   [ -  +  +  + ]:         10 :                         } else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
     642                 :          2 :                                 nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
     643   [ -  +  +  + ]:          8 :                         } else if (!strncmp(prev_value->start, "type", prev_value->len)) {
     644         [ -  + ]:          2 :                                 if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
     645                 :          0 :                                         nested_object_size = -1;
     646                 :            :                                 } else {
     647                 :          2 :                                         io_ctx->req.scsi_tmf_req.type = tmp_val;
     648                 :            :                                 }
     649   [ -  +  +  + ]:          6 :                         } else if (!strncmp(prev_value->start, "subtype", prev_value->len)) {
     650         [ -  + ]:          2 :                                 if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
     651                 :          0 :                                         nested_object_size = -1;
     652                 :            :                                 } else {
     653                 :          2 :                                         io_ctx->req.scsi_tmf_req.subtype = tmp_val;
     654                 :            :                                 }
     655   [ -  +  +  + ]:          4 :                         }  else if (!strncmp(prev_value->start, "lun", prev_value->len)) {
     656         [ -  + ]:          2 :                                 if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_tmf_req.lun,
     657                 :            :                                                                   sizeof(io_ctx->req.scsi_tmf_req.lun),
     658                 :          2 :                                                                   (char *)value->start,
     659                 :          2 :                                                                   value->len)) {
     660                 :          0 :                                         nested_object_size = -1;
     661                 :            :                                 }
     662   [ -  +  +  - ]:          2 :                         } else if (!strncmp(prev_value->start, "tag", prev_value->len)) {
     663         [ -  + ]:          2 :                                 if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
     664                 :          0 :                                         nested_object_size = -1;
     665                 :            :                                 } else {
     666                 :          2 :                                         io_ctx->req.scsi_tmf_req.tag = tmp_val;
     667                 :            :                                 }
     668                 :            :                         }
     669                 :            :                 }
     670         [ -  + ]:         18 :                 if (nested_object_size < 0) {
     671         [ #  # ]:          0 :                         fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
     672                 :          0 :                                 (char *)prev_value->start, value->len, (char *)value->start);
     673                 :          0 :                         return false;
     674                 :            :                 }
     675                 :         18 :                 value += nested_object_size;
     676                 :         18 :                 i += nested_object_size;
     677                 :            :         }
     678                 :          2 :         return true;
     679                 :            : }
     680                 :            : /* data parsing functions end */
     681                 :            : 
     682                 :            : /* build requests begin */
     683                 :            : static void
     684                 :          5 : craft_io_from_array(struct fuzz_vhost_io_ctx *src_ctx, struct fuzz_vhost_io_ctx *dest_ctx)
     685                 :            : {
     686   [ -  +  -  + ]:          5 :         if (g_keep_iov_pointers) {
     687                 :          0 :                 dest_ctx->iovs = src_ctx->iovs;
     688                 :            :         }
     689                 :          5 :         dest_ctx->req = src_ctx->req;
     690                 :          5 : }
     691                 :            : 
     692                 :            : static void
     693                 :     127584 : craft_virtio_scsi_req(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
     694                 :            : {
     695                 :     127584 :         io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.scsi_req);
     696                 :     127584 :         io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.scsi_resp);
     697                 :     127584 :         fuzz_fill_random_bytes((char *)&io_ctx->req.scsi_req, sizeof(io_ctx->req.scsi_req),
     698                 :            :                                &dev_ctx->random_seed);
     699                 :            :         /* TODO: set up the logic to find all luns on the target. Right now we are just assuming the first is OK. */
     700   [ -  +  +  - ]:     127584 :         if (dev_ctx->valid_lun) {
     701                 :     127584 :                 io_ctx->req.scsi_req.lun[0] = 1;
     702                 :     127584 :                 io_ctx->req.scsi_req.lun[1] = 0;
     703                 :            :         }
     704                 :     127584 : }
     705                 :            : 
     706                 :            : static void
     707                 :      48704 : craft_virtio_scsi_tmf_req(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
     708                 :            : {
     709                 :      48704 :         io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.scsi_tmf_req);
     710                 :      48704 :         io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.scsi_tmf_resp);
     711                 :      48704 :         fuzz_fill_random_bytes((char *)&io_ctx->req.scsi_tmf_req, sizeof(io_ctx->req.scsi_tmf_req),
     712                 :            :                                &dev_ctx->random_seed);
     713                 :            :         /* TODO: set up the logic to find all luns on the target. Right now we are just assuming the first is OK. */
     714   [ -  +  -  + ]:      48704 :         if (dev_ctx->valid_lun) {
     715                 :          0 :                 io_ctx->req.scsi_tmf_req.lun[0] = 1;
     716                 :          0 :                 io_ctx->req.scsi_tmf_req.lun[1] = 0;
     717                 :            :         }
     718                 :            : 
     719                 :            :         /* Valid controlq commands have to be of type 0, 1, or 2. Any others just return immediately from the target. */
     720                 :            :         /* Try to only test the opcodes that will exercise extra paths in the target side. But allow for at least one invalid value. */
     721                 :      48704 :         io_ctx->req.scsi_tmf_req.type = rand() % 4;
     722                 :      48704 : }
     723                 :            : 
     724                 :            : static void
     725                 :     127584 : craft_virtio_blk_req(struct fuzz_vhost_io_ctx *io_ctx)
     726                 :            : {
     727                 :     127584 :         io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.blk_req);
     728                 :     127584 :         io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.blk_resp);
     729                 :     127584 :         io_ctx->req.blk_req.type = rand();
     730                 :     127584 :         io_ctx->req.blk_req.sector = rand();
     731                 :     127584 : }
     732                 :            : 
     733                 :            : static void
     734                 :     303877 : craft_virtio_req_rsp_pair(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
     735                 :            : {
     736                 :     303877 :         struct fuzz_vhost_iov_ctx *iovs = &io_ctx->iovs;
     737                 :            : 
     738                 :            :         /*
     739                 :            :          * Always set these buffer values up front.
     740                 :            :          * If the user wants to override this with the json values,
     741                 :            :          * they can specify -k when starting the app. */
     742                 :     303877 :         iovs->iov_req.iov_base = &io_ctx->req;
     743   [ -  +  -  + ]:     303877 :         if (dev_ctx->use_bogus_buffer) {
     744                 :          0 :                 iovs->iov_data.iov_len = rand();
     745                 :          0 :                 iovs->iov_data.iov_base = get_invalid_mem_address(iovs->iov_data.iov_len);
     746   [ -  +  +  - ]:     303877 :         } else if (dev_ctx->use_valid_buffer) {
     747                 :     303877 :                 iovs->iov_data.iov_len = 1024;
     748                 :     303877 :                 iovs->iov_data.iov_base = g_valid_buffer;
     749                 :            :         }
     750                 :     303877 :         iovs->iov_resp.iov_base = &io_ctx->resp;
     751                 :            : 
     752   [ -  +  +  +  :     303877 :         if (dev_ctx->socket_is_blk && g_blk_cmd_array) {
                   +  + ]
     753                 :          1 :                 craft_io_from_array(&g_blk_cmd_array[dev_ctx->submitted_io], io_ctx);
     754                 :          1 :                 return;
     755   [ -  +  +  +  :     303876 :         } else if (dev_ctx->test_scsi_tmf && g_scsi_mgmt_cmd_array) {
                   +  + ]
     756                 :          2 :                 craft_io_from_array(&g_scsi_mgmt_cmd_array[dev_ctx->submitted_io], io_ctx);
     757                 :          2 :                 return;
     758         [ +  + ]:     303874 :         } else if (g_scsi_cmd_array) {
     759                 :          2 :                 craft_io_from_array(&g_scsi_cmd_array[dev_ctx->submitted_io], io_ctx);
     760                 :          2 :                 return;
     761                 :            :         }
     762                 :            : 
     763   [ -  +  +  + ]:     303872 :         if (dev_ctx->socket_is_blk) {
     764                 :     127584 :                 craft_virtio_blk_req(io_ctx);
     765   [ -  +  +  + ]:     176288 :         } else if (dev_ctx->test_scsi_tmf) {
     766                 :      48704 :                 craft_virtio_scsi_tmf_req(dev_ctx, io_ctx);
     767                 :            :         } else {
     768                 :     127584 :                 craft_virtio_scsi_req(dev_ctx, io_ctx);
     769                 :            :         }
     770                 :            : }
     771                 :            : /* build requests end */
     772                 :            : 
     773                 :            : /* submit requests begin */
     774                 :            : static uint64_t
     775                 :       2268 : get_max_num_io(struct fuzz_vhost_dev_ctx *dev_ctx)
     776                 :            : {
     777   [ -  +  +  + ]:       2268 :         if (dev_ctx->socket_is_blk) {
     778                 :        756 :                 return g_blk_cmd_array_size;
     779   [ -  +  +  + ]:       1512 :         } else if (dev_ctx->test_scsi_tmf) {
     780                 :        756 :                 return g_scsi_mgmt_cmd_array_size;
     781                 :            :         } else {
     782                 :        756 :                 return g_scsi_cmd_array_size;
     783                 :            :         }
     784                 :            : }
     785                 :            : 
     786                 :            : static int
     787                 :     303877 : submit_virtio_req_rsp_pair(struct fuzz_vhost_dev_ctx *dev_ctx, struct virtqueue *vq,
     788                 :            :                            struct fuzz_vhost_io_ctx *io_ctx)
     789                 :            : {
     790                 :     303877 :         struct fuzz_vhost_iov_ctx *iovs = &io_ctx->iovs;
     791                 :     303877 :         int num_iovs = 2, rc;
     792                 :            : 
     793   [ -  +  +  -  :     303877 :         num_iovs += dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer ? 1 : 0;
             -  +  +  - ]
     794                 :            : 
     795                 :     303877 :         rc = virtqueue_req_start(vq, io_ctx, num_iovs);
     796         [ -  + ]:     303877 :         if (rc) {
     797                 :          0 :                 return rc;
     798                 :            :         }
     799                 :     303877 :         virtqueue_req_add_iovs(vq, &iovs->iov_req, 1, SPDK_VIRTIO_DESC_RO);
     800                 :            :         /* blk and scsi requests favor different orders for the iov objects. */
     801   [ -  +  +  + ]:     303877 :         if (dev_ctx->socket_is_blk) {
     802   [ -  +  +  -  :     127585 :                 if (dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer) {
             -  +  +  - ]
     803                 :     127585 :                         virtqueue_req_add_iovs(vq, &iovs->iov_data, 1, SPDK_VIRTIO_DESC_WR);
     804                 :            :                 }
     805                 :     127585 :                 virtqueue_req_add_iovs(vq, &iovs->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
     806                 :            :         } else {
     807                 :     176292 :                 virtqueue_req_add_iovs(vq, &iovs->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
     808   [ -  +  +  -  :     176292 :                 if (dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer) {
             -  +  +  - ]
     809                 :     176292 :                         virtqueue_req_add_iovs(vq, &iovs->iov_data, 1, SPDK_VIRTIO_DESC_WR);
     810                 :            :                 }
     811                 :            :         }
     812                 :     303877 :         virtqueue_req_flush(vq);
     813                 :     303877 :         return 0;
     814                 :            : }
     815                 :            : 
     816                 :            : static void
     817                 :    3280991 : dev_submit_requests(struct fuzz_vhost_dev_ctx *dev_ctx, struct virtqueue *vq,
     818                 :            :                     uint64_t max_io_to_submit)
     819                 :            : {
     820                 :            :         struct fuzz_vhost_io_ctx *io_ctx;
     821                 :            :         int rc;
     822                 :            : 
     823   [ +  +  +  + ]:    3584868 :         while (!TAILQ_EMPTY(&dev_ctx->free_io_ctx) && dev_ctx->submitted_io < max_io_to_submit) {
     824                 :     303877 :                 io_ctx = TAILQ_FIRST(&dev_ctx->free_io_ctx);
     825                 :     303877 :                 craft_virtio_req_rsp_pair(dev_ctx, io_ctx);
     826                 :     303877 :                 rc = submit_virtio_req_rsp_pair(dev_ctx, vq, io_ctx);
     827         [ +  - ]:     303877 :                 if (rc == 0) {
     828         [ +  + ]:     303877 :                         TAILQ_REMOVE(&dev_ctx->free_io_ctx, io_ctx, link);
     829                 :     303877 :                         TAILQ_INSERT_TAIL(&dev_ctx->outstanding_io_ctx, io_ctx, link);
     830                 :     303877 :                         dev_ctx->submitted_io++;
     831         [ #  # ]:          0 :                 } else if (rc == -ENOMEM) {
     832                 :            :                         /* There are just not enough available buffers right now. try later. */
     833                 :          0 :                         return;
     834         [ #  # ]:          0 :                 } else if (rc == -EINVAL) {
     835                 :            :                         /* The virtqueue must be broken. We know we can fit at least three descriptors */
     836   [ #  #  #  # ]:          0 :                         fprintf(stderr, "One of the virtqueues for dev %p is broken. stopping all devices.\n", dev_ctx);
     837                 :          0 :                         g_run = 0;
     838                 :            :                 }
     839                 :            :         }
     840                 :            : }
     841                 :            : /* submit requests end */
     842                 :            : 
     843                 :            : /* complete requests begin */
     844                 :            : static void
     845                 :     303877 : check_successful_op(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
     846                 :            : {
     847                 :     303877 :         bool is_successful = false;
     848                 :            : 
     849   [ -  +  +  + ]:     303877 :         if (dev_ctx->socket_is_blk) {
     850         [ +  + ]:     127585 :                 if (io_ctx->resp.blk_resp == 0) {
     851                 :          1 :                         is_successful = true;
     852                 :            :                 }
     853   [ -  +  +  + ]:     176292 :         } else if (dev_ctx->test_scsi_tmf) {
     854         [ +  + ]:      48706 :                 if (io_ctx->resp.scsi_tmf_resp.scsi_tmf_resp.response == 0 &&
     855         [ +  + ]:        317 :                     io_ctx->resp.scsi_tmf_resp.an_resp.response == 0) {
     856                 :         37 :                         is_successful = true;
     857                 :            :                 }
     858                 :            :         } else {
     859         [ +  + ]:     127586 :                 if (io_ctx->resp.scsi_resp.status == 0) {
     860                 :        496 :                         is_successful = true;
     861                 :            :                 }
     862                 :            :         }
     863                 :            : 
     864         [ +  + ]:     303877 :         if (is_successful) {
     865   [ -  +  -  + ]:        534 :                 fprintf(stderr, "An I/O completed without an error status. This could be worth looking into.\n");
     866   [ -  +  -  + ]:        534 :                 fprintf(stderr,
     867                 :            :                         "There is also a good chance that the target just failed before setting a status.\n");
     868                 :        534 :                 dev_ctx->successful_io++;
     869                 :        534 :                 print_req_obj(dev_ctx, io_ctx);
     870   [ -  +  -  + ]:     303343 :         } else if (g_verbose_mode) {
     871   [ #  #  #  # ]:          0 :                 fprintf(stderr, "The following I/O failed as expected.\n");
     872                 :          0 :                 print_req_obj(dev_ctx, io_ctx);
     873                 :            :         }
     874                 :     303877 : }
     875                 :            : 
     876                 :            : static void
     877                 :     303877 : complete_io(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
     878                 :            : {
     879         [ +  + ]:     303877 :         TAILQ_REMOVE(&dev_ctx->outstanding_io_ctx, io_ctx, link);
     880         [ +  + ]:     303877 :         TAILQ_INSERT_HEAD(&dev_ctx->free_io_ctx, io_ctx, link);
     881                 :     303877 :         check_successful_op(dev_ctx, io_ctx);
     882                 :     303877 :         dev_ctx->completed_io++;
     883                 :     303877 :         dev_ctx->timeout_tsc = fuzz_refresh_timeout();
     884                 :     303877 : }
     885                 :            : 
     886                 :            : static int
     887                 :    3287606 : poll_dev(void *ctx)
     888                 :            : {
     889                 :    3287606 :         struct fuzz_vhost_dev_ctx *dev_ctx = ctx;
     890                 :            :         struct virtqueue *vq;
     891                 :          0 :         struct fuzz_vhost_io_ctx *io_ctx[FUZZ_QUEUE_DEPTH];
     892                 :            :         int num_active_threads;
     893                 :    3287606 :         uint64_t max_io_to_complete = UINT64_MAX;
     894                 :            :         uint64_t current_ticks;
     895                 :          0 :         uint32_t len[FUZZ_QUEUE_DEPTH];
     896                 :            :         uint16_t num_cpl, i;
     897                 :            : 
     898         [ +  + ]:    3287606 :         if (g_json_file) {
     899                 :       2268 :                 max_io_to_complete = get_max_num_io(dev_ctx);
     900                 :            :         }
     901                 :            : 
     902   [ -  +  +  +  :    3287606 :         if (!dev_ctx->socket_is_blk && dev_ctx->test_scsi_tmf) {
             -  +  +  + ]
     903                 :    1098496 :                 vq = dev_ctx->virtio_dev.vqs[VIRTIO_SCSI_CONTROLQ];
     904                 :            :         } else {
     905                 :    2189110 :                 vq = dev_ctx->virtio_dev.vqs[VIRTIO_REQUESTQ];
     906                 :            :         }
     907                 :            : 
     908                 :    3287606 :         num_cpl = virtio_recv_pkts(vq, (void **)io_ctx, len, FUZZ_QUEUE_DEPTH);
     909                 :            : 
     910         [ +  + ]:    3591483 :         for (i = 0; i < num_cpl; i++) {
     911                 :     303877 :                 complete_io(dev_ctx, io_ctx[i]);
     912                 :            :         }
     913                 :            : 
     914                 :    3287606 :         current_ticks = spdk_get_ticks();
     915                 :            : 
     916         [ -  + ]:    3287606 :         if (current_ticks > dev_ctx->timeout_tsc) {
     917                 :          0 :                 dev_ctx->timed_out = true;
     918                 :          0 :                 g_run = false;
     919   [ #  #  #  # ]:          0 :                 fprintf(stderr, "The VQ on device %p timed out. Dumping contents now.\n", dev_ctx);
     920                 :          0 :                 dump_outstanding_io(dev_ctx);
     921                 :            :         }
     922                 :            : 
     923         [ +  + ]:    3287606 :         if (current_ticks > g_runtime_ticks) {
     924                 :       6612 :                 g_run = 0;
     925                 :            :         }
     926                 :            : 
     927   [ -  +  +  +  :    3287606 :         if (!g_run || dev_ctx->completed_io >= max_io_to_complete) {
                   +  + ]
     928         [ +  + ]:       6615 :                 if (TAILQ_EMPTY(&dev_ctx->outstanding_io_ctx)) {
     929                 :          6 :                         spdk_poller_unregister(&dev_ctx->poller);
     930                 :          6 :                         num_active_threads = __sync_sub_and_fetch(&g_num_active_threads, 1);
     931         [ +  + ]:          6 :                         if (num_active_threads == 0) {
     932                 :          2 :                                 g_run = 0;
     933                 :            :                         }
     934                 :          6 :                         spdk_thread_exit(dev_ctx->thread);
     935                 :            :                 }
     936                 :       6615 :                 return 0;
     937                 :            :         }
     938                 :            : 
     939                 :    3280991 :         dev_submit_requests(dev_ctx, vq, max_io_to_complete);
     940                 :    3280991 :         return 0;
     941                 :            : }
     942                 :            : /* complete requests end */
     943                 :            : 
     944                 :            : static void
     945                 :          6 : start_io(void *ctx)
     946                 :            : {
     947                 :          6 :         struct fuzz_vhost_dev_ctx *dev_ctx = ctx;
     948                 :            : 
     949         [ -  + ]:          6 :         if (g_random_seed) {
     950                 :          0 :                 dev_ctx->random_seed = g_random_seed;
     951                 :            :         } else {
     952                 :          6 :                 dev_ctx->random_seed = spdk_get_ticks();
     953                 :            :         }
     954                 :            : 
     955                 :          6 :         dev_ctx->timeout_tsc = fuzz_refresh_timeout();
     956                 :            : 
     957                 :          6 :         dev_ctx->poller = SPDK_POLLER_REGISTER(poll_dev, dev_ctx, 0);
     958         [ -  + ]:          6 :         if (dev_ctx->poller == NULL) {
     959                 :          0 :                 return;
     960                 :            :         }
     961                 :            : 
     962                 :            : }
     963                 :            : 
     964                 :            : static int
     965                 :    1098496 : end_fuzz(void *ctx)
     966                 :            : {
     967   [ -  +  +  +  :    1098496 :         if (!g_run && !g_num_active_threads) {
                   +  + ]
     968                 :          2 :                 spdk_poller_unregister(&g_run_poller);
     969                 :          2 :                 cleanup();
     970                 :          2 :                 spdk_app_stop(0);
     971                 :            :         }
     972                 :    1098496 :         return 0;
     973                 :            : }
     974                 :            : 
     975                 :            : static void
     976                 :          2 : begin_fuzz(void *ctx)
     977                 :            : {
     978                 :            :         struct fuzz_vhost_dev_ctx *dev_ctx;
     979                 :            : 
     980                 :          2 :         g_runtime_ticks = spdk_get_ticks() + spdk_get_ticks_hz() * g_runtime;
     981                 :            : 
     982                 :          2 :         g_valid_buffer = spdk_malloc(0x1000, 0x200, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE);
     983         [ -  + ]:          2 :         if (g_valid_buffer == NULL) {
     984   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to allocate a valid buffer for I/O\n");
     985                 :          0 :                 goto out;
     986                 :            :         }
     987                 :            : 
     988                 :          2 :         g_run_poller = SPDK_POLLER_REGISTER(end_fuzz, NULL, 0);
     989         [ -  + ]:          2 :         if (g_run_poller == NULL) {
     990   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to register a poller for test completion checking.\n");
     991                 :            :         }
     992                 :            : 
     993         [ +  + ]:          8 :         TAILQ_FOREACH(dev_ctx, &g_dev_list, link) {
     994         [ -  + ]:          6 :                 assert(dev_ctx->thread != NULL);
     995                 :          6 :                 spdk_thread_send_msg(dev_ctx->thread, start_io, dev_ctx);
     996                 :          6 :                 __sync_add_and_fetch(&g_num_active_threads, 1);
     997                 :            :         }
     998                 :            : 
     999                 :          2 :         return;
    1000                 :          0 : out:
    1001                 :          0 :         cleanup();
    1002                 :          0 :         spdk_app_stop(0);
    1003                 :            : }
    1004                 :            : 
    1005                 :            : static void
    1006                 :          0 : fuzz_vhost_usage(void)
    1007                 :            : {
    1008   [ #  #  #  # ]:          0 :         fprintf(stderr, " -j <path>                 Path to a json file containing named objects.\n");
    1009   [ #  #  #  # ]:          0 :         fprintf(stderr,
    1010                 :            :                 " -k                        Keep the iov pointer addresses from the json file. only valid with -j.\n");
    1011   [ #  #  #  # ]:          0 :         fprintf(stderr, " -S <integer>              Seed value for test.\n");
    1012   [ #  #  #  # ]:          0 :         fprintf(stderr, " -t <integer>              Time in seconds to run the fuzz test.\n");
    1013   [ #  #  #  # ]:          0 :         fprintf(stderr, " -V                        Enable logging of each submitted command.\n");
    1014                 :          0 : }
    1015                 :            : 
    1016                 :            : static int
    1017                 :          2 : fuzz_vhost_parse(int ch, char *arg)
    1018                 :            : {
    1019                 :            :         int64_t error_test;
    1020                 :            : 
    1021   [ +  -  -  +  :          2 :         switch (ch) {
                   -  - ]
    1022                 :          1 :         case 'j':
    1023                 :          1 :                 g_json_file = optarg;
    1024                 :          1 :                 break;
    1025                 :          0 :         case 'k':
    1026                 :          0 :                 g_keep_iov_pointers = true;
    1027                 :          0 :                 break;
    1028                 :          0 :         case 'S':
    1029                 :          0 :                 error_test = spdk_strtol(arg, 10);
    1030         [ #  # ]:          0 :                 if (error_test < 0) {
    1031   [ #  #  #  # ]:          0 :                         fprintf(stderr, "Invalid value supplied for the random seed.\n");
    1032                 :          0 :                         return -1;
    1033                 :            :                 } else {
    1034                 :          0 :                         g_random_seed = spdk_strtol(arg, 10);
    1035                 :            :                 }
    1036                 :          0 :                 break;
    1037                 :          1 :         case 't':
    1038                 :          1 :                 g_runtime = spdk_strtol(arg, 10);
    1039   [ +  -  -  + ]:          1 :                 if (g_runtime < 0 || g_runtime > MAX_RUNTIME_S) {
    1040   [ #  #  #  # ]:          0 :                         fprintf(stderr, "You must supply a positive runtime value less than 86401.\n");
    1041                 :          0 :                         return -1;
    1042                 :            :                 }
    1043                 :          1 :                 break;
    1044                 :          0 :         case 'V':
    1045                 :          0 :                 g_verbose_mode = true;
    1046                 :          0 :                 break;
    1047                 :          0 :         case '?':
    1048                 :            :         default:
    1049                 :          0 :                 return -EINVAL;
    1050                 :            :         }
    1051                 :          2 :         return 0;
    1052                 :            : }
    1053                 :            : 
    1054                 :            : int
    1055                 :          2 : main(int argc, char **argv)
    1056                 :            : {
    1057                 :          2 :         struct spdk_app_opts opts = {};
    1058                 :            :         int rc;
    1059                 :            : 
    1060                 :          2 :         spdk_app_opts_init(&opts, sizeof(opts));
    1061                 :          2 :         opts.name = "vhost_fuzz";
    1062                 :          2 :         opts.rpc_addr = NULL;
    1063                 :          2 :         g_runtime = DEFAULT_RUNTIME;
    1064                 :            : 
    1065                 :          2 :         rc = spdk_app_parse_args(argc, argv, &opts, "j:kS:t:V", NULL, fuzz_vhost_parse, fuzz_vhost_usage);
    1066         [ -  + ]:          2 :         if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) {
    1067   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to parse the application arguments.\n");
    1068                 :          0 :                 return -1;
    1069                 :            :         }
    1070                 :            : 
    1071         [ +  + ]:          2 :         if (g_json_file != NULL) {
    1072                 :          1 :                 g_blk_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
    1073                 :            :                                        (void **)&g_blk_cmd_array,
    1074                 :            :                                        sizeof(struct fuzz_vhost_io_ctx),
    1075                 :            :                                        BLK_IO_NAME, parse_vhost_blk_cmds);
    1076                 :          1 :                 g_scsi_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
    1077                 :            :                                         (void **)&g_scsi_cmd_array,
    1078                 :            :                                         sizeof(struct fuzz_vhost_io_ctx),
    1079                 :            :                                         SCSI_IO_NAME, parse_vhost_scsi_cmds);
    1080                 :          1 :                 g_scsi_mgmt_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
    1081                 :            :                                              (void **)&g_scsi_mgmt_cmd_array,
    1082                 :            :                                              sizeof(struct fuzz_vhost_io_ctx),
    1083                 :            :                                              SCSI_IO_NAME, parse_vhost_scsi_mgmt_cmds);
    1084   [ -  +  -  -  :          1 :                 if (g_blk_cmd_array_size == 0 && g_scsi_cmd_array_size == 0 && g_scsi_mgmt_cmd_array_size == 0) {
                   -  - ]
    1085   [ #  #  #  # ]:          0 :                         fprintf(stderr, "The provided json file did not contain any valid commands. Exiting.\n");
    1086                 :          0 :                         return -EINVAL;
    1087                 :            :                 }
    1088                 :            :         }
    1089                 :            : 
    1090                 :          2 :         rc = spdk_app_start(&opts, begin_fuzz, NULL);
    1091                 :            : 
    1092                 :          2 :         spdk_app_fini();
    1093                 :          2 :         return rc;
    1094                 :            : }

Generated by: LCOV version 1.14