summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/app/fuzz/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/test/app/fuzz/common
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/test/app/fuzz/common')
-rw-r--r--src/spdk/test/app/fuzz/common/fuzz_common.h303
-rwxr-xr-xsrc/spdk/test/app/fuzz/common/fuzz_rpc.py106
2 files changed, 409 insertions, 0 deletions
diff --git a/src/spdk/test/app/fuzz/common/fuzz_common.h b/src/spdk/test/app/fuzz/common/fuzz_common.h
new file mode 100644
index 000000000..7619f4fb1
--- /dev/null
+++ b/src/spdk/test/app/fuzz/common/fuzz_common.h
@@ -0,0 +1,303 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+#include "spdk/env.h"
+#include "spdk/file.h"
+#include "spdk/base64.h"
+#include "spdk/json.h"
+
+#define DEFAULT_RUNTIME 30 /* seconds */
+#define MAX_RUNTIME_S 86400 /* 24 hours */
+#define IO_TIMEOUT_S 5
+
+#define UNSIGNED_2BIT_MAX ((1 << 2) - 1)
+#define UNSIGNED_4BIT_MAX ((1 << 4) - 1)
+#define UNSIGNED_8BIT_MAX ((1 << 8) - 1)
+
+typedef bool (*json_parse_fn)(void *ele, struct spdk_json_val *val, size_t num_vals);
+
+static void
+fuzz_fill_random_bytes(char *character_repr, size_t len, unsigned int *rand_seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ character_repr[i] = rand_r(rand_seed) % UINT8_MAX;
+ }
+}
+
+static uint64_t
+fuzz_refresh_timeout(void)
+{
+ uint64_t current_ticks;
+ uint64_t new_timeout_ticks;
+
+ current_ticks = spdk_get_ticks();
+
+ new_timeout_ticks = current_ticks + IO_TIMEOUT_S * spdk_get_ticks_hz();
+ assert(new_timeout_ticks > current_ticks);
+
+ return new_timeout_ticks;
+}
+
+static char *
+fuzz_get_value_base_64_buffer(void *item, size_t len)
+{
+ char *value_string;
+ size_t total_size;
+ int rc;
+
+ /* Null pointer */
+ total_size = spdk_base64_get_encoded_strlen(len) + 1;
+
+ value_string = calloc(1, total_size);
+ if (value_string == NULL) {
+ return NULL;
+ }
+
+ rc = spdk_base64_encode(value_string, item, len);
+ if (rc < 0) {
+ free(value_string);
+ return NULL;
+ }
+
+ return value_string;
+}
+
+static int
+fuzz_get_base_64_buffer_value(void *item, size_t len, char *buf, size_t buf_len)
+{
+ size_t size_of_data;
+ char *new_buf;
+ int rc;
+
+ new_buf = malloc(buf_len + 1);
+ if (new_buf == NULL) {
+ return -ENOMEM;
+ }
+
+ snprintf(new_buf, buf_len + 1, "%s", buf);
+
+ size_of_data = spdk_base64_get_decoded_len(buf_len);
+
+ if (size_of_data < len) {
+ free(new_buf);
+ return -EINVAL;
+ }
+
+ rc = spdk_base64_decode(item, &size_of_data, new_buf);
+
+ if (rc || size_of_data != len) {
+ free(new_buf);
+ return -EINVAL;
+ }
+
+ free(new_buf);
+ return 0;
+}
+
+static ssize_t
+read_json_into_buffer(const char *filename, struct spdk_json_val **values, void **file_data)
+{
+ FILE *file = fopen(filename, "r");
+ size_t file_data_size;
+ ssize_t num_json_values = 0, rc;
+
+ if (file == NULL) {
+ /* errno is set by fopen */
+ return 0;
+ }
+
+ *file_data = spdk_posix_file_load(file, &file_data_size);
+ if (*file_data == NULL) {
+ fclose(file);
+ return 0;
+ }
+
+ fclose(file);
+
+ num_json_values = spdk_json_parse(*file_data, file_data_size, NULL, 0, NULL,
+ SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
+
+ *values = calloc(num_json_values, sizeof(**values));
+ if (values == NULL) {
+ free(*file_data);
+ *file_data = NULL;
+ return 0;
+ }
+
+ rc = spdk_json_parse(*file_data, file_data_size, *values, num_json_values, NULL,
+ SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
+ if (num_json_values != rc) {
+ free(*values);
+ *values = NULL;
+ free(*file_data);
+ *file_data = NULL;
+ return 0;
+ }
+
+ return num_json_values;
+}
+
+static size_t
+double_arr_size(void **buffer, size_t num_ele, size_t ele_size)
+{
+ void *tmp;
+ size_t new_num_ele, allocation_size;
+
+ if (num_ele > SIZE_MAX / 2) {
+ return 0;
+ }
+
+ new_num_ele = num_ele * 2;
+
+ if (new_num_ele > SIZE_MAX / ele_size) {
+ return 0;
+ }
+
+ allocation_size = new_num_ele * ele_size;
+
+ tmp = realloc(*buffer, allocation_size);
+ if (tmp != NULL) {
+ *buffer = tmp;
+ return new_num_ele;
+ }
+
+ return 0;
+}
+
+static uint64_t
+fuzz_parse_args_into_array(const char *file, void **arr, size_t ele_size, const char *obj_name,
+ json_parse_fn cb_fn)
+{
+ ssize_t i, num_json_values;
+ struct spdk_json_val *values = NULL, *values_head = NULL, *obj_start;
+ void *file_data = NULL;;
+ char *arr_idx_pointer;
+ size_t num_arr_elements, arr_elements_used, values_in_obj;
+ bool rc;
+
+ num_json_values = read_json_into_buffer(file, &values_head, &file_data);
+ values = values_head;
+ if (num_json_values == 0 || values == NULL) {
+ if (file_data != NULL) {
+ free(file_data);
+ }
+ fprintf(stderr, "The file provided does not exist or we were unable to parse it.\n");
+ return 0;
+ }
+
+ num_arr_elements = 10;
+ arr_elements_used = 0;
+ *arr = calloc(num_arr_elements, ele_size);
+ arr_idx_pointer = (char *)*arr;
+ if (arr_idx_pointer == NULL) {
+ free(values);
+ free(file_data);
+ return 0;
+ }
+
+ i = 0;
+ while (i < num_json_values) {
+ if (values->type != SPDK_JSON_VAL_NAME) {
+ i++;
+ values++;
+ continue;
+ }
+
+ if (!strncmp(values->start, obj_name, values->len)) {
+ i++;
+ values++;
+ assert(values->type == SPDK_JSON_VAL_OBJECT_BEGIN);
+ obj_start = values;
+ values_in_obj = spdk_json_val_len(obj_start);
+ values += values_in_obj;
+ i += values_in_obj;
+
+ rc = cb_fn((void *)arr_idx_pointer, obj_start, values_in_obj);
+ if (rc == false) {
+ fprintf(stderr, "failed to parse file after %lu elements.\n", arr_elements_used);
+ goto fail;
+ }
+
+ arr_idx_pointer += ele_size;
+ arr_elements_used++;
+ if (arr_elements_used == num_arr_elements) {
+ num_arr_elements = double_arr_size(arr, num_arr_elements, ele_size);
+ if (num_arr_elements == 0) {
+ fprintf(stderr, "failed to allocate enough space for all json elements in your file.\n");
+ goto fail;
+ } else {
+ /* reset the array element position in case the pointer changed. */
+ arr_idx_pointer = ((char *)*arr) + arr_elements_used * ele_size;
+ }
+ }
+
+ continue;
+ } else {
+ i++;
+ values++;
+ continue;
+ }
+ }
+
+ if (arr_elements_used == 0) {
+ goto fail;
+ }
+
+ free(values_head);
+ free(file_data);
+ return arr_elements_used;
+fail:
+ free(values_head);
+ free(file_data);
+ free(*arr);
+ *arr = NULL;
+ return 0;
+}
+
+static int
+fuzz_parse_json_num(struct spdk_json_val *val, uint64_t max_val, uint64_t *val_ptr)
+{
+ uint64_t tmp_val;
+ int rc;
+
+ rc = spdk_json_number_to_uint64(val, &tmp_val);
+ if (rc || tmp_val > max_val) {
+ return -EINVAL;
+ } else {
+ *val_ptr = tmp_val;
+ return 0;
+ }
+}
diff --git a/src/spdk/test/app/fuzz/common/fuzz_rpc.py b/src/spdk/test/app/fuzz/common/fuzz_rpc.py
new file mode 100755
index 000000000..05cb67ed8
--- /dev/null
+++ b/src/spdk/test/app/fuzz/common/fuzz_rpc.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+
+from rpc.client import print_dict, JSONRPCException
+
+import logging
+import argparse
+import rpc
+import sys
+import shlex
+
+try:
+ from shlex import quote
+except ImportError:
+ from pipes import quote
+
+
+def print_array(a):
+ print(" ".join((quote(v) for v in a)))
+
+
+def _fuzz_vhost_create_dev(client, socket, is_blk, use_bogus_buffer, use_valid_buffer, test_scsi_tmf, valid_lun):
+ """Create a new device in the vhost fuzzer.
+
+ Args:
+ socket: A valid unix domain socket for the dev to bind to.
+ is_blk: if set, create a virtio_blk device, otherwise use scsi.
+ use_bogus_buffer: if set, pass an invalid memory address as a buffer accompanying requests.
+ use_valid_buffer: if set, pass in a valid memory buffer with requests. Overrides use_bogus_buffer.
+ test_scsi_tmf: Test scsi management commands on the given device. Valid if and only if is_blk is false.
+ valid_lun: Supply only a valid lun number when submitting commands to the given device. Valid if and only if is_blk is false.
+
+ Returns:
+ True or False
+ """
+
+ params = {"socket": socket,
+ "is_blk": is_blk,
+ "use_bogus_buffer": use_bogus_buffer,
+ "use_valid_buffer": use_valid_buffer,
+ "test_scsi_tmf": test_scsi_tmf,
+ "valid_lun": valid_lun}
+
+ return client.call("fuzz_vhost_create_dev", params)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description='SPDK RPC command line interface. NOTE: spdk/scripts/ is expected in PYTHONPATH')
+ parser.add_argument('-s', dest='server_addr',
+ help='RPC domain socket path or IP address', default='/var/tmp/spdk.sock')
+ parser.add_argument('-p', dest='port',
+ help='RPC port number (if server_addr is IP address)',
+ default=5260, type=int)
+ parser.add_argument('-t', dest='timeout',
+ help='Timeout as a floating point number expressed in seconds waiting for response. Default: 60.0',
+ default=60.0, type=float)
+ parser.add_argument('-v', dest='verbose', action='store_const', const="INFO",
+ help='Set verbose mode to INFO', default="ERROR")
+ parser.add_argument('--verbose', dest='verbose', choices=['DEBUG', 'INFO', 'ERROR'],
+ help="""Set verbose level. """)
+ subparsers = parser.add_subparsers(help='RPC methods')
+
+ def fuzz_vhost_create_dev(args):
+ _fuzz_vhost_create_dev(
+ args.client,
+ args.socket,
+ args.is_blk,
+ args.use_bogus_buffer,
+ args.use_valid_buffer,
+ args.test_scsi_tmf,
+ args.valid_lun)
+
+ p = subparsers.add_parser('fuzz_vhost_create_dev', help="Add a new device to the vhost fuzzer.")
+ p.add_argument('-s', '--socket', help="Path to a valid unix domain socket for dev binding.")
+ p.add_argument('-b', '--is-blk', help='The specified socket corresponds to a vhost-blk dev.', action='store_true')
+ p.add_argument('-u', '--use-bogus-buffer', help='Pass bogus buffer addresses with requests when fuzzing.', action='store_true')
+ p.add_argument('-v', '--use-valid-buffer', help='Pass valid buffers when fuzzing. overrides use-bogus-buffer.', action='store_true')
+ p.add_argument('-m', '--test-scsi-tmf', help='for a scsi device, test scsi management commands.', action='store_true')
+ p.add_argument('-l', '--valid-lun', help='for a scsi device, test only using valid lun IDs.', action='store_true')
+ p.set_defaults(func=fuzz_vhost_create_dev)
+
+ def call_rpc_func(args):
+ try:
+ args.func(args)
+ except JSONRPCException as ex:
+ print(ex.message)
+ exit(1)
+
+ def execute_script(parser, client, fd):
+ for rpc_call in map(str.rstrip, fd):
+ if not rpc_call.strip():
+ continue
+ args = parser.parse_args(shlex.split(rpc_call))
+ args.client = client
+ call_rpc_func(args)
+
+ args = parser.parse_args()
+ args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.timeout, log_level=getattr(logging, args.verbose.upper()))
+ if hasattr(args, 'func'):
+ call_rpc_func(args)
+ elif sys.stdin.isatty():
+ # No arguments and no data piped through stdin
+ parser.print_help()
+ exit(1)
+ else:
+ execute_script(parser, args.client, sys.stdin)