All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 00/21] Add virtual device fuzzing support
@ 2020-01-29  5:34 Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
                   ` (21 more replies)
  0 siblings, 22 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, bsd, darren.kenny, stefanha, Bulekov,  Alexander

This series adds a framework for coverage-guided fuzzing of
virtual-devices. Fuzzing targets are based on qtest and can make use of
the libqos abstractions.

V8:
 * Small fixes to the virtio-net.
 * Keep rcu_atfork when not using qtest.

V7:
 * virtio-net: add virtio-net-check-used which waits for inputs on
 the tx/ctrl vq by watching the used vring.
 * virtio-net: add virtio-net-socket which uses the socket backend and can
 exercise the rx components of virtio-net.
 * virtio-net: add virtio-net-slirp which uses the user backend and exercises
 slirp. This may lead to real traffic emitted by qemu so it is best to
 run in an isolated network environment.
 * build should succeed after each commit

V5/V6:
 * added virtio-scsi fuzzer
 * add support for using fork-based fuzzers with multiple libfuzzer
   workers
 * misc fixes addressing V4 comments
 * cleanup in-process handlers/globals in libqtest.c
 * small fixes to fork-based fuzzing and support for multiple workers
 * changes to the virtio-net fuzzer to kick after each vq add

V4:
 * add/transfer license headers to new files
 * restructure the added QTestClientTransportOps struct
 * restructure the FuzzTarget struct and fuzzer skeleton
 * fork-based fuzzer now directly mmaps shm over the coverage bitmaps
 * fixes to i440 and virtio-net fuzz targets
 * undo the changes to qtest_memwrite
 * possible to build /fuzz and /all in the same build-dir
 * misc fixes to address V3 comments

V3:
 * rebased onto v4.1.0+
 * add the fuzzer as a new build-target type in the build-system
 * add indirection to qtest client/server communication functions
 * remove ramfile and snapshot-based fuzzing support
 * add i440fx fuzz-target as a reference for developers.
 * add linker-script to assist with fork-based fuzzer

V2:
 * split off changes to qos virtio-net and qtest server to other patches
 * move vl:main initialization into new func: qemu_init
 * moved useful functions from qos-test.c to a separate object
 * use struct of function pointers for add_fuzz_target(), instead of
   arguments
 * move ramfile to migration/qemu-file
 * rewrite fork-based fuzzer pending patch to libfuzzer
 * pass check-patch

Alexander Bulekov (21):
  softmmu: split off vl.c:main() into main.c
  module: check module wasn't already initialized
  fuzz: add FUZZ_TARGET module type
  qtest: add qtest_server_send abstraction
  libqtest: add a layer of abstraction to send/recv
  libqtest: make bufwrite rely on the TransportOps
  qtest: add in-process incoming command handler
  libqos: rename i2c_send and i2c_recv
  libqos: split qos-test and libqos makefile vars
  libqos: move useful qos-test funcs to qos_external
  fuzz: add fuzzer skeleton
  exec: keep ram block across fork when using qtest
  main: keep rcu_atfork callback enabled for qtest
  fuzz: support for fork-based fuzzing.
  fuzz: add support for qos-assisted fuzz targets
  fuzz: add target/fuzz makefile rules
  fuzz: add configure flag --enable-fuzzing
  fuzz: add i440fx fuzz targets
  fuzz: add virtio-net fuzz target
  fuzz: add virtio-scsi fuzz target
  fuzz: add documentation to docs/devel/

 Makefile                            |  16 +-
 Makefile.objs                       |   2 +
 Makefile.target                     |  18 ++-
 configure                           |  39 +++++
 docs/devel/fuzzing.txt              | 116 ++++++++++++++
 exec.c                              |  12 +-
 include/qemu/module.h               |   4 +-
 include/sysemu/qtest.h              |   4 +
 include/sysemu/sysemu.h             |   4 +
 main.c                              |  53 +++++++
 qtest.c                             |  31 +++-
 tests/qtest/Makefile.include        |  72 ++++-----
 tests/qtest/fuzz/Makefile.include   |  18 +++
 tests/qtest/fuzz/fork_fuzz.c        |  55 +++++++
 tests/qtest/fuzz/fork_fuzz.h        |  23 +++
 tests/qtest/fuzz/fork_fuzz.ld       |  37 +++++
 tests/qtest/fuzz/fuzz.c             | 179 ++++++++++++++++++++++
 tests/qtest/fuzz/fuzz.h             |  95 ++++++++++++
 tests/qtest/fuzz/i440fx_fuzz.c      | 178 +++++++++++++++++++++
 tests/qtest/fuzz/qos_fuzz.c         | 229 ++++++++++++++++++++++++++++
 tests/qtest/fuzz/qos_fuzz.h         |  33 ++++
 tests/qtest/fuzz/virtio_net_fuzz.c  | 195 +++++++++++++++++++++++
 tests/qtest/fuzz/virtio_scsi_fuzz.c | 200 ++++++++++++++++++++++++
 tests/qtest/libqos/i2c.c            |  10 +-
 tests/qtest/libqos/i2c.h            |   4 +-
 tests/qtest/libqos/qos_external.c   | 168 ++++++++++++++++++++
 tests/qtest/libqos/qos_external.h   |  28 ++++
 tests/qtest/libqtest.c              | 119 +++++++++++++--
 tests/qtest/libqtest.h              |   4 +
 tests/qtest/pca9552-test.c          |  10 +-
 tests/qtest/qos-test.c              | 132 +---------------
 util/module.c                       |   7 +
 vl.c                                |  45 +++---
 33 files changed, 1916 insertions(+), 224 deletions(-)
 create mode 100644 docs/devel/fuzzing.txt
 create mode 100644 main.c
 create mode 100644 tests/qtest/fuzz/Makefile.include
 create mode 100644 tests/qtest/fuzz/fork_fuzz.c
 create mode 100644 tests/qtest/fuzz/fork_fuzz.h
 create mode 100644 tests/qtest/fuzz/fork_fuzz.ld
 create mode 100644 tests/qtest/fuzz/fuzz.c
 create mode 100644 tests/qtest/fuzz/fuzz.h
 create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c
 create mode 100644 tests/qtest/fuzz/qos_fuzz.c
 create mode 100644 tests/qtest/fuzz/qos_fuzz.h
 create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c
 create mode 100644 tests/qtest/fuzz/virtio_scsi_fuzz.c
 create mode 100644 tests/qtest/libqos/qos_external.c
 create mode 100644 tests/qtest/libqos/qos_external.h

-- 
2.23.0


^ permalink raw reply	[flat|nested] 55+ messages in thread

* [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-30 14:39   ` Stefan Hajnoczi
                     ` (2 more replies)
  2020-01-29  5:34 ` [PATCH v8 02/21] module: check module wasn't already initialized Bulekov, Alexander
                   ` (20 subsequent siblings)
  21 siblings, 3 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, bsd, stefanha, Bulekov, Alexander

A program might rely on functions implemented in vl.c, but implement its
own main(). By placing main into a separate source file, there are no
complaints about duplicate main()s when linking against vl.o. For
example, the virtual-device fuzzer uses a main() provided by libfuzzer,
and needs to perform some initialization before running the softmmu
initialization. Now, main simply calls three vl.c functions which
handle the guest initialization, main loop and cleanup.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
---
 Makefile                |  1 +
 Makefile.objs           |  2 ++
 Makefile.target         |  2 +-
 include/sysemu/sysemu.h |  4 ++++
 main.c                  | 53 +++++++++++++++++++++++++++++++++++++++++
 vl.c                    | 36 +++++++---------------------
 6 files changed, 70 insertions(+), 28 deletions(-)
 create mode 100644 main.c

diff --git a/Makefile b/Makefile
index 32bd554480..e6de7a47bb 100644
--- a/Makefile
+++ b/Makefile
@@ -473,6 +473,7 @@ $(SOFTMMU_ALL_RULES): $(chardev-obj-y)
 $(SOFTMMU_ALL_RULES): $(crypto-obj-y)
 $(SOFTMMU_ALL_RULES): $(io-obj-y)
 $(SOFTMMU_ALL_RULES): config-all-devices.mak
+$(SOFTMMU_ALL_RULES): $(softmmu-main-y)
 ifdef DECOMPRESS_EDK2_BLOBS
 $(SOFTMMU_ALL_RULES): $(edk2-decompressed)
 endif
diff --git a/Makefile.objs b/Makefile.objs
index 7c1e50f9d6..5ab166fed5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -84,6 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
 # qapi
 
 common-obj-y += qapi/
+
+softmmu-obj-y = main.o
 endif
 
 #######################################################################
diff --git a/Makefile.target b/Makefile.target
index 6e61f607b1..8dcf3dddd8 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -202,7 +202,7 @@ endif
 COMMON_LDADDS = ../libqemuutil.a
 
 # build either PROG or PROGW
-$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
+$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-obj-y)
 	$(call LINK, $(filter-out %.mak, $^))
 ifdef CONFIG_DARWIN
 	$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 80c57fdc4e..270df5fa34 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -118,6 +118,10 @@ QemuOpts *qemu_get_machine_opts(void);
 
 bool defaults_enabled(void);
 
+void qemu_init(int argc, char **argv, char **envp);
+void qemu_main_loop(void);
+void qemu_cleanup(void);
+
 extern QemuOptsList qemu_legacy_drive_opts;
 extern QemuOptsList qemu_common_drive_opts;
 extern QemuOptsList qemu_drive_opts;
diff --git a/main.c b/main.c
new file mode 100644
index 0000000000..f10ceda541
--- /dev/null
+++ b/main.c
@@ -0,0 +1,53 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+
+#ifdef CONFIG_SDL
+#if defined(__APPLE__) || defined(main)
+#include <SDL.h>
+int main(int argc, char **argv)
+{
+    return qemu_main(argc, argv, NULL);
+}
+#undef main
+#define main qemu_main
+#endif
+#endif /* CONFIG_SDL */
+
+#ifdef CONFIG_COCOA
+#undef main
+#define main qemu_main
+#endif /* CONFIG_COCOA */
+
+int main(int argc, char **argv, char **envp)
+{
+    qemu_init(argc, argv, envp);
+    qemu_main_loop();
+    qemu_cleanup();
+
+    return 0;
+}
diff --git a/vl.c b/vl.c
index 751401214c..bb77935f04 100644
--- a/vl.c
+++ b/vl.c
@@ -36,25 +36,6 @@
 #include "sysemu/seccomp.h"
 #include "sysemu/tcg.h"
 
-#ifdef CONFIG_SDL
-#if defined(__APPLE__) || defined(main)
-#include <SDL.h>
-int qemu_main(int argc, char **argv, char **envp);
-int main(int argc, char **argv)
-{
-    return qemu_main(argc, argv, NULL);
-}
-#undef main
-#define main qemu_main
-#endif
-#endif /* CONFIG_SDL */
-
-#ifdef CONFIG_COCOA
-#undef main
-#define main qemu_main
-#endif /* CONFIG_COCOA */
-
-
 #include "qemu/error-report.h"
 #include "qemu/sockets.h"
 #include "sysemu/accel.h"
@@ -1657,7 +1638,7 @@ static bool main_loop_should_exit(void)
     return false;
 }
 
-static void main_loop(void)
+void qemu_main_loop(void)
 {
 #ifdef CONFIG_PROFILER
     int64_t ti;
@@ -2820,7 +2801,7 @@ static void configure_accelerators(const char *progname)
     }
 }
 
-int main(int argc, char **argv, char **envp)
+void qemu_init(int argc, char **argv, char **envp)
 {
     int i;
     int snapshot, linux_boot;
@@ -3372,7 +3353,7 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_watchdog:
                 if (watchdog) {
                     error_report("only one watchdog option may be given");
-                    return 1;
+                    exit(1);
                 }
                 watchdog = optarg;
                 break;
@@ -4284,7 +4265,7 @@ int main(int argc, char **argv, char **envp)
     parse_numa_opts(current_machine);
 
     /* do monitor/qmp handling at preconfig state if requested */
-    main_loop();
+    qemu_main_loop();
 
     audio_init_audiodevs();
 
@@ -4394,7 +4375,7 @@ int main(int argc, char **argv, char **envp)
     if (vmstate_dump_file) {
         /* dump and exit */
         dump_vmstate_json_to_file(vmstate_dump_file);
-        return 0;
+        exit(0);
     }
 
     if (incoming) {
@@ -4411,8 +4392,11 @@ int main(int argc, char **argv, char **envp)
     accel_setup_post(current_machine);
     os_setup_post();
 
-    main_loop();
+    return;
+}
 
+void qemu_cleanup(void)
+{
     gdbserver_cleanup();
 
     /*
@@ -4449,6 +4433,4 @@ int main(int argc, char **argv, char **envp)
     qemu_chr_cleanup();
     user_creatable_cleanup();
     /* TODO: unref root container, check all devices are ok */
-
-    return 0;
 }
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 02/21] module: check module wasn't already initialized
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 03/21] fuzz: add FUZZ_TARGET module type Bulekov, Alexander
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Bulekov, Alexander, Darren Kenny, bsd, stefanha, pbonzini,
	Philippe Mathieu-Daudé

The virtual-device fuzzer must initialize QOM, prior to running
vl:qemu_init, so that it can use the qos_graph to identify the arguments
required to initialize a guest for libqos-assisted fuzzing. This change
prevents errors when vl:qemu_init tries to (re)initialize the previously
initialized QOM module.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
 util/module.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/util/module.c b/util/module.c
index 8c5315a7a3..236a7bb52a 100644
--- a/util/module.c
+++ b/util/module.c
@@ -30,6 +30,7 @@ typedef struct ModuleEntry
 typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
 
 static ModuleTypeList init_type_list[MODULE_INIT_MAX];
+static bool modules_init_done[MODULE_INIT_MAX];
 
 static ModuleTypeList dso_init_list;
 
@@ -91,11 +92,17 @@ void module_call_init(module_init_type type)
     ModuleTypeList *l;
     ModuleEntry *e;
 
+    if (modules_init_done[type]) {
+        return;
+    }
+
     l = find_type(type);
 
     QTAILQ_FOREACH(e, l, node) {
         e->init();
     }
+
+    modules_init_done[type] = true;
 }
 
 #ifdef CONFIG_MODULES
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 03/21] fuzz: add FUZZ_TARGET module type
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 02/21] module: check module wasn't already initialized Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:28   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 04/21] qtest: add qtest_server_send abstraction Bulekov, Alexander
                   ` (18 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, bsd, stefanha, Bulekov, Alexander

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/qemu/module.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/qemu/module.h b/include/qemu/module.h
index 65ba596e46..684753d808 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -46,6 +46,7 @@ typedef enum {
     MODULE_INIT_TRACE,
     MODULE_INIT_XEN_BACKEND,
     MODULE_INIT_LIBQOS,
+    MODULE_INIT_FUZZ_TARGET,
     MODULE_INIT_MAX
 } module_init_type;
 
@@ -56,7 +57,8 @@ typedef enum {
 #define xen_backend_init(function) module_init(function, \
                                                MODULE_INIT_XEN_BACKEND)
 #define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
-
+#define fuzz_target_init(function) module_init(function, \
+                                               MODULE_INIT_FUZZ_TARGET)
 #define block_module_load_one(lib) module_load_one("block-", lib)
 #define ui_module_load_one(lib) module_load_one("ui-", lib)
 #define audio_module_load_one(lib) module_load_one("audio-", lib)
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 04/21] qtest: add qtest_server_send abstraction
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (2 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 03/21] fuzz: add FUZZ_TARGET module type Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:30   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 05/21] libqtest: add a layer of abstraction to send/recv Bulekov, Alexander
                   ` (17 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

qtest_server_send is a function pointer specifying the handler used to
transmit data to the qtest client. In the standard configuration, this
calls the CharBackend handler, but now it is possible for other types of
handlers, e.g direct-function calls if the qtest client and server
exist within the same process (inproc)

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Acked-by: Thomas Huth <thuth@redhat.com>
---
 include/sysemu/qtest.h |  3 +++
 qtest.c                | 18 ++++++++++++++++--
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index 5ed09c80b1..e2f1047fd7 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -26,4 +26,7 @@ bool qtest_driver(void);
 
 void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
 
+void qtest_server_set_send_handler(void (*send)(void *, const char *),
+                                 void *opaque);
+
 #endif
diff --git a/qtest.c b/qtest.c
index 12432f99cf..938c3746d6 100644
--- a/qtest.c
+++ b/qtest.c
@@ -42,6 +42,8 @@ static GString *inbuf;
 static int irq_levels[MAX_IRQ];
 static qemu_timeval start_time;
 static bool qtest_opened;
+static void (*qtest_server_send)(void*, const char*);
+static void *qtest_server_send_opaque;
 
 #define FMT_timeval "%ld.%06ld"
 
@@ -228,8 +230,10 @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...)
     va_end(ap);
 }
 
-static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
+static void qtest_server_char_be_send(void *opaque, const char *str)
 {
+    size_t len = strlen(str);
+    CharBackend* chr = (CharBackend *)opaque;
     qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
     if (qtest_log_fp && qtest_opened) {
         fprintf(qtest_log_fp, "%s", str);
@@ -238,7 +242,7 @@ static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
 
 static void qtest_send(CharBackend *chr, const char *str)
 {
-    do_qtest_send(chr, str, strlen(str));
+    qtest_server_send(qtest_server_send_opaque, str);
 }
 
 static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
@@ -783,6 +787,16 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
     qemu_chr_fe_set_echo(&qtest_chr, true);
 
     inbuf = g_string_new("");
+
+    if (!qtest_server_send) {
+        qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
+    }
+}
+
+void qtest_server_set_send_handler(void (*send)(void*, const char*), void *opaque)
+{
+    qtest_server_send = send;
+    qtest_server_send_opaque = opaque;
 }
 
 bool qtest_driver(void)
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 05/21] libqtest: add a layer of abstraction to send/recv
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (3 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 04/21] qtest: add qtest_server_send abstraction Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:32   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 06/21] libqtest: make bufwrite rely on the TransportOps Bulekov, Alexander
                   ` (16 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

This makes it simple to swap the transport functions for qtest commands
to and from the qtest client. For example, now it is possible to
directly pass qtest commands to a server handler that exists within the
same process, without the standard way of writing to a file descriptor.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/libqtest.c | 48 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 39 insertions(+), 9 deletions(-)

diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 76c9f8eade..e5056a1d0f 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -35,6 +35,15 @@
 #define SOCKET_TIMEOUT 50
 #define SOCKET_MAX_FDS 16
 
+
+typedef void (*QTestSendFn)(QTestState *s, const char *buf);
+typedef GString* (*QTestRecvFn)(QTestState *);
+
+typedef struct QTestClientTransportOps {
+    QTestSendFn     send;      /* for sending qtest commands */
+    QTestRecvFn     recv_line; /* for receiving qtest command responses */
+} QTestTransportOps;
+
 struct QTestState
 {
     int fd;
@@ -45,6 +54,7 @@ struct QTestState
     bool big_endian;
     bool irq_level[MAX_IRQ];
     GString *rx;
+    QTestTransportOps ops;
 };
 
 static GHookList abrt_hooks;
@@ -52,6 +62,14 @@ static struct sigaction sigact_old;
 
 static int qtest_query_target_endianness(QTestState *s);
 
+static void qtest_client_socket_send(QTestState*, const char *buf);
+static void socket_send(int fd, const char *buf, size_t size);
+
+static GString *qtest_client_socket_recv_line(QTestState *);
+
+static void qtest_client_set_tx_handler(QTestState *s, QTestSendFn send);
+static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv);
+
 static int init_socket(const char *socket_path)
 {
     struct sockaddr_un addr;
@@ -234,6 +252,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
     sock = init_socket(socket_path);
     qmpsock = init_socket(qmp_socket_path);
 
+    qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
+    qtest_client_set_tx_handler(s, qtest_client_socket_send);
+
     qtest_add_abrt_handler(kill_qemu_hook_func, s);
 
     command = g_strdup_printf("exec %s "
@@ -379,13 +400,9 @@ static void socket_send(int fd, const char *buf, size_t size)
     }
 }
 
-static void socket_sendf(int fd, const char *fmt, va_list ap)
+static void qtest_client_socket_send(QTestState *s, const char *buf)
 {
-    gchar *str = g_strdup_vprintf(fmt, ap);
-    size_t size = strlen(str);
-
-    socket_send(fd, str, size);
-    g_free(str);
+    socket_send(s->fd, buf, strlen(buf));
 }
 
 static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
@@ -393,8 +410,11 @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
     va_list ap;
 
     va_start(ap, fmt);
-    socket_sendf(s->fd, fmt, ap);
+    gchar *str = g_strdup_vprintf(fmt, ap);
     va_end(ap);
+
+    s->ops.send(s, str);
+    g_free(str);
 }
 
 /* Sends a message and file descriptors to the socket.
@@ -431,7 +451,7 @@ static void socket_send_fds(int socket_fd, int *fds, size_t fds_num,
     g_assert_cmpint(ret, >, 0);
 }
 
-static GString *qtest_recv_line(QTestState *s)
+static GString *qtest_client_socket_recv_line(QTestState *s)
 {
     GString *line;
     size_t offset;
@@ -468,7 +488,7 @@ static gchar **qtest_rsp(QTestState *s, int expected_args)
     int i;
 
 redo:
-    line = qtest_recv_line(s);
+    line = s->ops.recv_line(s);
     words = g_strsplit(line->str, " ", 0);
     g_string_free(line, TRUE);
 
@@ -1337,3 +1357,13 @@ void qmp_assert_error_class(QDict *rsp, const char *class)
 
     qobject_unref(rsp);
 }
+
+static void qtest_client_set_tx_handler(QTestState *s,
+                    QTestSendFn send)
+{
+    s->ops.send = send;
+}
+static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
+{
+    s->ops.recv_line = recv;
+}
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 06/21] libqtest: make bufwrite rely on the TransportOps
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (4 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 05/21] libqtest: add a layer of abstraction to send/recv Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:36   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 07/21] qtest: add in-process incoming command handler Bulekov, Alexander
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

When using qtest "in-process" communication, qtest_sendf directly calls
a function in the server (qtest.c). Previously, bufwrite used
socket_send, which bypasses the TransportOps enabling the call into
qtest.c. This change replaces the socket_send calls with ops->send,
maintaining the benefits of the direct socket_send call, while adding
support for in-process qtest calls.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/libqtest.c | 71 ++++++++++++++++++++++++++++++++++++++++--
 tests/qtest/libqtest.h |  4 +++
 2 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index e5056a1d0f..49075b55a1 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -37,10 +37,18 @@
 
 
 typedef void (*QTestSendFn)(QTestState *s, const char *buf);
+typedef void (*ExternalSendFn)(void *s, const char *buf);
 typedef GString* (*QTestRecvFn)(QTestState *);
 
 typedef struct QTestClientTransportOps {
     QTestSendFn     send;      /* for sending qtest commands */
+
+    /*
+     * use external_send to send qtest command strings through functions which
+     * do not accept a QTestState as the first parameter.
+     */
+    ExternalSendFn  external_send;
+
     QTestRecvFn     recv_line; /* for receiving qtest command responses */
 } QTestTransportOps;
 
@@ -1078,8 +1086,8 @@ void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
 
     bdata = g_base64_encode(data, size);
     qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
-    socket_send(s->fd, bdata, strlen(bdata));
-    socket_send(s->fd, "\n", 1);
+    s->ops.send(s, bdata);
+    s->ops.send(s, "\n");
     qtest_rsp(s, 0);
     g_free(bdata);
 }
@@ -1367,3 +1375,62 @@ static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
 {
     s->ops.recv_line = recv;
 }
+/* A type-safe wrapper for s->send() */
+static void send_wrapper(QTestState *s, const char *buf)
+{
+    s->ops.external_send(s, buf);
+}
+
+static GString *qtest_client_inproc_recv_line(QTestState *s)
+{
+    GString *line;
+    size_t offset;
+    char *eol;
+
+    eol = strchr(s->rx->str, '\n');
+    offset = eol - s->rx->str;
+    line = g_string_new_len(s->rx->str, offset);
+    g_string_erase(s->rx, 0, offset + 1);
+    return line;
+}
+
+QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
+                    void (*send)(void*, const char*))
+{
+    QTestState *qts;
+    qts = g_new0(QTestState, 1);
+    *s = qts; /* Expose qts early on, since the query endianness relies on it */
+    qts->wstatus = 0;
+    for (int i = 0; i < MAX_IRQ; i++) {
+        qts->irq_level[i] = false;
+    }
+
+    qtest_client_set_rx_handler(qts, qtest_client_inproc_recv_line);
+
+    /* send() may not have a matching protoype, so use a type-safe wrapper */
+    qts->ops.external_send = send;
+    qtest_client_set_tx_handler(qts, send_wrapper);
+
+    qts->big_endian = qtest_query_target_endianness(qts);
+
+    /*
+     * Set a dummy path for QTEST_QEMU_BINARY. Doesn't need to exist, but this
+     * way, qtest_get_arch works for inproc qtest.
+     */
+    gchar *bin_path = g_strconcat("/qemu-system-", arch, NULL);
+    setenv("QTEST_QEMU_BINARY", bin_path, 0);
+    g_free(bin_path);
+
+    return qts;
+}
+
+void qtest_client_inproc_recv(void *opaque, const char *str)
+{
+    QTestState *qts = *(QTestState **)opaque;
+
+    if (!qts->rx) {
+        qts->rx = g_string_new(NULL);
+    }
+    g_string_append(qts->rx, str);
+    return;
+}
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index c9e21e05b3..f5cf93c386 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -729,4 +729,8 @@ bool qtest_probe_child(QTestState *s);
  */
 void qtest_set_expected_status(QTestState *s, int status);
 
+QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
+                    void (*send)(void*, const char*));
+
+void qtest_client_inproc_recv(void *opaque, const char *str);
 #endif
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 07/21] qtest: add in-process incoming command handler
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (5 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 06/21] libqtest: make bufwrite rely on the TransportOps Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:39   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 08/21] libqos: rename i2c_send and i2c_recv Bulekov, Alexander
                   ` (14 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

The handler allows a qtest client to send commands to the server by
directly calling a function, rather than using a file/CharBackend

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/sysemu/qtest.h |  1 +
 qtest.c                | 13 +++++++++++++
 2 files changed, 14 insertions(+)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index e2f1047fd7..eedd3664f0 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -28,5 +28,6 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
 
 void qtest_server_set_send_handler(void (*send)(void *, const char *),
                                  void *opaque);
+void qtest_server_inproc_recv(void *opaque, const char *buf);
 
 #endif
diff --git a/qtest.c b/qtest.c
index 938c3746d6..ad6eb6a526 100644
--- a/qtest.c
+++ b/qtest.c
@@ -803,3 +803,16 @@ bool qtest_driver(void)
 {
     return qtest_chr.chr != NULL;
 }
+
+void qtest_server_inproc_recv(void *dummy, const char *buf)
+{
+    static GString *gstr;
+    if (!gstr) {
+        gstr = g_string_new(NULL);
+    }
+    g_string_append(gstr, buf);
+    if (gstr->str[gstr->len - 1] == '\n') {
+        qtest_process_inbuf(NULL, gstr);
+        g_string_truncate(gstr, 0);
+    }
+}
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 09/21] libqos: split qos-test and libqos makefile vars
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (7 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 08/21] libqos: rename i2c_send and i2c_recv Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external Bulekov, Alexander
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov,  Alexander, Darren Kenny,
	bsd, stefanha, pbonzini, Philippe Mathieu-Daudé

Most qos-related objects were specified in the qos-test-obj-y variable.
qos-test-obj-y also included qos-test.o which defines a main().
This made it difficult to repurpose qos-test-obj-y to link anything
beside tests/qos-test against libqos. This change separates objects that
are libqos-specific and ones that are qos-test specific into different
variables.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
 tests/qtest/Makefile.include | 71 ++++++++++++++++++------------------
 1 file changed, 36 insertions(+), 35 deletions(-)

diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
index e6bb4ab28c..08a48c1252 100644
--- a/tests/qtest/Makefile.include
+++ b/tests/qtest/Makefile.include
@@ -157,52 +157,53 @@ check-qtest-s390x-y += migration-test
 # libqos / qgraph :
 libqgraph-obj-y = tests/qtest/libqos/qgraph.o
 
-libqos-obj-y = $(libqgraph-obj-y) tests/qtest/libqos/pci.o tests/qtest/libqos/fw_cfg.o
-libqos-obj-y += tests/qtest/libqos/malloc.o
-libqos-obj-y += tests/qtest/libqos/libqos.o
-libqos-spapr-obj-y = $(libqos-obj-y) tests/qtest/libqos/malloc-spapr.o
+libqos-core-obj-y = $(libqgraph-obj-y) tests/qtest/libqos/pci.o tests/qtest/libqos/fw_cfg.o
+libqos-core-obj-y += tests/qtest/libqos/malloc.o
+libqos-core-obj-y += tests/qtest/libqos/libqos.o
+libqos-spapr-obj-y = $(libqos-core-obj-y) tests/qtest/libqos/malloc-spapr.o
 libqos-spapr-obj-y += tests/qtest/libqos/libqos-spapr.o
 libqos-spapr-obj-y += tests/qtest/libqos/rtas.o
 libqos-spapr-obj-y += tests/qtest/libqos/pci-spapr.o
-libqos-pc-obj-y = $(libqos-obj-y) tests/qtest/libqos/pci-pc.o
+libqos-pc-obj-y = $(libqos-core-obj-y) tests/qtest/libqos/pci-pc.o
 libqos-pc-obj-y += tests/qtest/libqos/malloc-pc.o tests/qtest/libqos/libqos-pc.o
 libqos-pc-obj-y += tests/qtest/libqos/ahci.o
 libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/usb.o
 
 # qos devices:
-qos-test-obj-y = tests/qtest/qos-test.o $(libqgraph-obj-y)
-qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
-qos-test-obj-y += tests/qtest/libqos/e1000e.o
-qos-test-obj-y += tests/qtest/libqos/i2c.o
-qos-test-obj-y += tests/qtest/libqos/i2c-imx.o
-qos-test-obj-y += tests/qtest/libqos/i2c-omap.o
-qos-test-obj-y += tests/qtest/libqos/sdhci.o
-qos-test-obj-y += tests/qtest/libqos/tpci200.o
-qos-test-obj-y += tests/qtest/libqos/virtio.o
-qos-test-obj-$(CONFIG_VIRTFS) += tests/qtest/libqos/virtio-9p.o
-qos-test-obj-y += tests/qtest/libqos/virtio-balloon.o
-qos-test-obj-y += tests/qtest/libqos/virtio-blk.o
-qos-test-obj-y += tests/qtest/libqos/virtio-mmio.o
-qos-test-obj-y += tests/qtest/libqos/virtio-net.o
-qos-test-obj-y += tests/qtest/libqos/virtio-pci.o
-qos-test-obj-y += tests/qtest/libqos/virtio-pci-modern.o
-qos-test-obj-y += tests/qtest/libqos/virtio-rng.o
-qos-test-obj-y += tests/qtest/libqos/virtio-scsi.o
-qos-test-obj-y += tests/qtest/libqos/virtio-serial.o
+libqos-obj-y =  $(libqgraph-obj-y)
+libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
+libqos-obj-y += tests/qtest/libqos/e1000e.o
+libqos-obj-y += tests/qtest/libqos/i2c.o
+libqos-obj-y += tests/qtest/libqos/i2c-imx.o
+libqos-obj-y += tests/qtest/libqos/i2c-omap.o
+libqos-obj-y += tests/qtest/libqos/sdhci.o
+libqos-obj-y += tests/qtest/libqos/tpci200.o
+libqos-obj-y += tests/qtest/libqos/virtio.o
+libqos-obj-$(CONFIG_VIRTFS) += tests/qtest/libqos/virtio-9p.o
+libqos-obj-y += tests/qtest/libqos/virtio-balloon.o
+libqos-obj-y += tests/qtest/libqos/virtio-blk.o
+libqos-obj-y += tests/qtest/libqos/virtio-mmio.o
+libqos-obj-y += tests/qtest/libqos/virtio-net.o
+libqos-obj-y += tests/qtest/libqos/virtio-pci.o
+libqos-obj-y += tests/qtest/libqos/virtio-pci-modern.o
+libqos-obj-y += tests/qtest/libqos/virtio-rng.o
+libqos-obj-y += tests/qtest/libqos/virtio-scsi.o
+libqos-obj-y += tests/qtest/libqos/virtio-serial.o
 
 # qos machines:
-qos-test-obj-y += tests/qtest/libqos/aarch64-xlnx-zcu102-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-imx25-pdk-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-n800-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-raspi2-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-sabrelite-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-smdkc210-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-virt-machine.o
-qos-test-obj-y += tests/qtest/libqos/arm-xilinx-zynq-a9-machine.o
-qos-test-obj-y += tests/qtest/libqos/ppc64_pseries-machine.o
-qos-test-obj-y += tests/qtest/libqos/x86_64_pc-machine.o
+libqos-obj-y += tests/qtest/libqos/aarch64-xlnx-zcu102-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-imx25-pdk-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-n800-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-raspi2-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-sabrelite-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-smdkc210-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-virt-machine.o
+libqos-obj-y += tests/qtest/libqos/arm-xilinx-zynq-a9-machine.o
+libqos-obj-y += tests/qtest/libqos/ppc64_pseries-machine.o
+libqos-obj-y += tests/qtest/libqos/x86_64_pc-machine.o
 
 # qos tests:
+qos-test-obj-y += tests/qtest/qos-test.o
 qos-test-obj-y += tests/qtest/ac97-test.o
 qos-test-obj-y += tests/qtest/ds1338-test.o
 qos-test-obj-y += tests/qtest/e1000-test.o
@@ -234,7 +235,7 @@ check-unit-y += tests/test-qgraph$(EXESUF)
 tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y)
 
 check-qtest-generic-y += qos-test
-tests/qtest/qos-test$(EXESUF): $(qos-test-obj-y)
+tests/qtest/qos-test$(EXESUF): $(qos-test-obj-y) $(libqos-obj-y)
 
 # QTest dependencies:
 tests/qtest/qmp-test$(EXESUF): tests/qtest/qmp-test.o
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 08/21] libqos: rename i2c_send and i2c_recv
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (6 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 07/21] qtest: add in-process incoming command handler Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:40   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 09/21] libqos: split qos-test and libqos makefile vars Bulekov, Alexander
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

The names i2c_send and i2c_recv collide with functions defined in
hw/i2c/core.c. This causes an error when linking against libqos and
softmmu simultaneously (for example when using qtest inproc). Rename the
libqos functions to avoid this.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Acked-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/libqos/i2c.c   | 10 +++++-----
 tests/qtest/libqos/i2c.h   |  4 ++--
 tests/qtest/pca9552-test.c | 10 +++++-----
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/tests/qtest/libqos/i2c.c b/tests/qtest/libqos/i2c.c
index 156114e745..38f800dbab 100644
--- a/tests/qtest/libqos/i2c.c
+++ b/tests/qtest/libqos/i2c.c
@@ -10,12 +10,12 @@
 #include "libqos/i2c.h"
 #include "libqtest.h"
 
-void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
+void qi2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
 {
     i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
 }
 
-void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
+void qi2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
 {
     i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
 }
@@ -23,8 +23,8 @@ void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
 void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
                     uint8_t *buf, uint16_t len)
 {
-    i2c_send(i2cdev, &reg, 1);
-    i2c_recv(i2cdev, buf, len);
+    qi2c_send(i2cdev, &reg, 1);
+    qi2c_recv(i2cdev, buf, len);
 }
 
 void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
@@ -33,7 +33,7 @@ void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
     uint8_t *cmd = g_malloc(len + 1);
     cmd[0] = reg;
     memcpy(&cmd[1], buf, len);
-    i2c_send(i2cdev, cmd, len + 1);
+    qi2c_send(i2cdev, cmd, len + 1);
     g_free(cmd);
 }
 
diff --git a/tests/qtest/libqos/i2c.h b/tests/qtest/libqos/i2c.h
index 945b65b34c..c65f087834 100644
--- a/tests/qtest/libqos/i2c.h
+++ b/tests/qtest/libqos/i2c.h
@@ -47,8 +47,8 @@ struct QI2CDevice {
 void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
 void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
 
-void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
-void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
+void qi2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
+void qi2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
 
 void i2c_read_block(QI2CDevice *dev, uint8_t reg,
                     uint8_t *buf, uint16_t len);
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index 4b800d3c3e..d80ed93cd3 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -32,22 +32,22 @@ static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc)
 
     pca9552_init(i2cdev);
 
-    i2c_send(i2cdev, &reg, 1);
+    qi2c_send(i2cdev, &reg, 1);
 
     /* PCA9552_LS0 */
-    i2c_recv(i2cdev, &resp, 1);
+    qi2c_recv(i2cdev, &resp, 1);
     g_assert_cmphex(resp, ==, 0x54);
 
     /* PCA9552_LS1 */
-    i2c_recv(i2cdev, &resp, 1);
+    qi2c_recv(i2cdev, &resp, 1);
     g_assert_cmphex(resp, ==, 0x55);
 
     /* PCA9552_LS2 */
-    i2c_recv(i2cdev, &resp, 1);
+    qi2c_recv(i2cdev, &resp, 1);
     g_assert_cmphex(resp, ==, 0x55);
 
     /* PCA9552_LS3 */
-    i2c_recv(i2cdev, &resp, 1);
+    qi2c_recv(i2cdev, &resp, 1);
     g_assert_cmphex(resp, ==, 0x54);
 }
 
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (8 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 09/21] libqos: split qos-test and libqos makefile vars Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-29 10:03   ` Paolo Bonzini
  2020-02-05 13:34   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 11/21] fuzz: add fuzzer skeleton Bulekov, Alexander
                   ` (11 subsequent siblings)
  21 siblings, 2 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha,
	pbonzini, Philippe Mathieu-Daudé

The moved functions are not specific to qos-test and might be useful
elsewhere. For example the virtual-device fuzzer makes use of them for
qos-assisted fuzz-targets.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
 tests/qtest/Makefile.include      |   1 +
 tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++++++++++++
 tests/qtest/libqos/qos_external.h |  28 +++++
 tests/qtest/qos-test.c            | 132 +----------------------
 4 files changed, 198 insertions(+), 131 deletions(-)
 create mode 100644 tests/qtest/libqos/qos_external.c
 create mode 100644 tests/qtest/libqos/qos_external.h

diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
index 08a48c1252..bdc93d3866 100644
--- a/tests/qtest/Makefile.include
+++ b/tests/qtest/Makefile.include
@@ -172,6 +172,7 @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/u
 # qos devices:
 libqos-obj-y =  $(libqgraph-obj-y)
 libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
+libqos-obj-y += tests/qtest/libqos/qos_external.o
 libqos-obj-y += tests/qtest/libqos/e1000e.o
 libqos-obj-y += tests/qtest/libqos/i2c.o
 libqos-obj-y += tests/qtest/libqos/i2c-imx.o
diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c
new file mode 100644
index 0000000000..398556dde0
--- /dev/null
+++ b/tests/qtest/libqos/qos_external.c
@@ -0,0 +1,168 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include <getopt.h>
+#include "libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/qstring.h"
+#include "qemu/module.h"
+#include "qapi/qmp/qlist.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "libqos/qgraph_internal.h"
+#include "libqos/qos_external.h"
+
+
+
+void apply_to_node(const char *name, bool is_machine, bool is_abstract)
+{
+    char *machine_name = NULL;
+    if (is_machine) {
+        const char *arch = qtest_get_arch();
+        machine_name = g_strconcat(arch, "/", name, NULL);
+        name = machine_name;
+    }
+    qos_graph_node_set_availability(name, true);
+    if (is_abstract) {
+        qos_delete_cmd_line(name);
+    }
+    g_free(machine_name);
+}
+
+/**
+ * apply_to_qlist(): using QMP queries QEMU for a list of
+ * machines and devices available, and sets the respective node
+ * as true. If a node is found, also all its produced and contained
+ * child are marked available.
+ *
+ * See qos_graph_node_set_availability() for more info
+ */
+void apply_to_qlist(QList *list, bool is_machine)
+{
+    const QListEntry *p;
+    const char *name;
+    bool abstract;
+    QDict *minfo;
+    QObject *qobj;
+    QString *qstr;
+    QBool *qbool;
+
+    for (p = qlist_first(list); p; p = qlist_next(p)) {
+        minfo = qobject_to(QDict, qlist_entry_obj(p));
+        qobj = qdict_get(minfo, "name");
+        qstr = qobject_to(QString, qobj);
+        name = qstring_get_str(qstr);
+
+        qobj = qdict_get(minfo, "abstract");
+        if (qobj) {
+            qbool = qobject_to(QBool, qobj);
+            abstract = qbool_get_bool(qbool);
+        } else {
+            abstract = false;
+        }
+
+        apply_to_node(name, is_machine, abstract);
+        qobj = qdict_get(minfo, "alias");
+        if (qobj) {
+            qstr = qobject_to(QString, qobj);
+            name = qstring_get_str(qstr);
+            apply_to_node(name, is_machine, abstract);
+        }
+    }
+}
+
+QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
+{
+    return obj->get_driver(obj, "memory");
+}
+
+/**
+ * allocate_objects(): given an array of nodes @arg,
+ * walks the path invoking all constructors and
+ * passing the corresponding parameter in order to
+ * continue the objects allocation.
+ * Once the test is reached, return the object it consumes.
+ *
+ * Since the machine and QEDGE_CONSUMED_BY nodes allocate
+ * memory in the constructor, g_test_queue_destroy is used so
+ * that after execution they can be safely free'd.  (The test's
+ * ->before callback is also welcome to use g_test_queue_destroy).
+ *
+ * Note: as specified in walk_path() too, @arg is an array of
+ * char *, where arg[0] is a pointer to the command line
+ * string that will be used to properly start QEMU when executing
+ * the test, and the remaining elements represent the actual objects
+ * that will be allocated.
+ */
+void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
+{
+    int current = 0;
+    QGuestAllocator *alloc;
+    QOSGraphObject *parent = NULL;
+    QOSGraphEdge *edge;
+    QOSGraphNode *node;
+    void *edge_arg;
+    void *obj;
+
+    node = qos_graph_get_node(path[current]);
+    g_assert(node->type == QNODE_MACHINE);
+
+    obj = qos_machine_new(node, qts);
+    qos_object_queue_destroy(obj);
+
+    alloc = get_machine_allocator(obj);
+    if (p_alloc) {
+        *p_alloc = alloc;
+    }
+
+    for (;;) {
+        if (node->type != QNODE_INTERFACE) {
+            qos_object_start_hw(obj);
+            parent = obj;
+        }
+
+        /* follow edge and get object for next node constructor */
+        current++;
+        edge = qos_graph_get_edge(path[current - 1], path[current]);
+        node = qos_graph_get_node(path[current]);
+
+        if (node->type == QNODE_TEST) {
+            g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
+            return obj;
+        }
+
+        switch (qos_graph_edge_get_type(edge)) {
+        case QEDGE_PRODUCES:
+            obj = parent->get_driver(parent, path[current]);
+            break;
+
+        case QEDGE_CONSUMED_BY:
+            edge_arg = qos_graph_edge_get_arg(edge);
+            obj = qos_driver_new(node, obj, alloc, edge_arg);
+            qos_object_queue_destroy(obj);
+            break;
+
+        case QEDGE_CONTAINS:
+            obj = parent->get_device(parent, path[current]);
+            break;
+        }
+    }
+}
+
diff --git a/tests/qtest/libqos/qos_external.h b/tests/qtest/libqos/qos_external.h
new file mode 100644
index 0000000000..7b44930c55
--- /dev/null
+++ b/tests/qtest/libqos/qos_external.h
@@ -0,0 +1,28 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef QOS_EXTERNAL_H
+#define QOS_EXTERNAL_H
+#include "libqos/qgraph.h"
+
+void apply_to_node(const char *name, bool is_machine, bool is_abstract);
+void apply_to_qlist(QList *list, bool is_machine);
+QGuestAllocator *get_machine_allocator(QOSGraphObject *obj);
+void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc);
+
+#endif
diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c
index fd70d73ea5..ad193f43a5 100644
--- a/tests/qtest/qos-test.c
+++ b/tests/qtest/qos-test.c
@@ -27,65 +27,11 @@
 #include "libqos/malloc.h"
 #include "libqos/qgraph.h"
 #include "libqos/qgraph_internal.h"
+#include "libqos/qos_external.h"
 
 static char *old_path;
 
-static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
-{
-    char *machine_name = NULL;
-    if (is_machine) {
-        const char *arch = qtest_get_arch();
-        machine_name = g_strconcat(arch, "/", name, NULL);
-        name = machine_name;
-    }
-    qos_graph_node_set_availability(name, true);
-    if (is_abstract) {
-        qos_delete_cmd_line(name);
-    }
-    g_free(machine_name);
-}
 
-/**
- * apply_to_qlist(): using QMP queries QEMU for a list of
- * machines and devices available, and sets the respective node
- * as true. If a node is found, also all its produced and contained
- * child are marked available.
- *
- * See qos_graph_node_set_availability() for more info
- */
-static void apply_to_qlist(QList *list, bool is_machine)
-{
-    const QListEntry *p;
-    const char *name;
-    bool abstract;
-    QDict *minfo;
-    QObject *qobj;
-    QString *qstr;
-    QBool *qbool;
-
-    for (p = qlist_first(list); p; p = qlist_next(p)) {
-        minfo = qobject_to(QDict, qlist_entry_obj(p));
-        qobj = qdict_get(minfo, "name");
-        qstr = qobject_to(QString, qobj);
-        name = qstring_get_str(qstr);
-
-        qobj = qdict_get(minfo, "abstract");
-        if (qobj) {
-            qbool = qobject_to(QBool, qobj);
-            abstract = qbool_get_bool(qbool);
-        } else {
-            abstract = false;
-        }
-
-        apply_to_node(name, is_machine, abstract);
-        qobj = qdict_get(minfo, "alias");
-        if (qobj) {
-            qstr = qobject_to(QString, qobj);
-            name = qstring_get_str(qstr);
-            apply_to_node(name, is_machine, abstract);
-        }
-    }
-}
 
 /**
  * qos_set_machines_devices_available(): sets availability of qgraph
@@ -129,10 +75,6 @@ static void qos_set_machines_devices_available(void)
     qobject_unref(response);
 }
 
-static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
-{
-    return obj->get_driver(obj, "memory");
-}
 
 static void restart_qemu_or_continue(char *path)
 {
@@ -159,78 +101,6 @@ void qos_invalidate_command_line(void)
     old_path = NULL;
 }
 
-/**
- * allocate_objects(): given an array of nodes @arg,
- * walks the path invoking all constructors and
- * passing the corresponding parameter in order to
- * continue the objects allocation.
- * Once the test is reached, return the object it consumes.
- *
- * Since the machine and QEDGE_CONSUMED_BY nodes allocate
- * memory in the constructor, g_test_queue_destroy is used so
- * that after execution they can be safely free'd.  (The test's
- * ->before callback is also welcome to use g_test_queue_destroy).
- *
- * Note: as specified in walk_path() too, @arg is an array of
- * char *, where arg[0] is a pointer to the command line
- * string that will be used to properly start QEMU when executing
- * the test, and the remaining elements represent the actual objects
- * that will be allocated.
- */
-static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
-{
-    int current = 0;
-    QGuestAllocator *alloc;
-    QOSGraphObject *parent = NULL;
-    QOSGraphEdge *edge;
-    QOSGraphNode *node;
-    void *edge_arg;
-    void *obj;
-
-    node = qos_graph_get_node(path[current]);
-    g_assert(node->type == QNODE_MACHINE);
-
-    obj = qos_machine_new(node, qts);
-    qos_object_queue_destroy(obj);
-
-    alloc = get_machine_allocator(obj);
-    if (p_alloc) {
-        *p_alloc = alloc;
-    }
-
-    for (;;) {
-        if (node->type != QNODE_INTERFACE) {
-            qos_object_start_hw(obj);
-            parent = obj;
-        }
-
-        /* follow edge and get object for next node constructor */
-        current++;
-        edge = qos_graph_get_edge(path[current - 1], path[current]);
-        node = qos_graph_get_node(path[current]);
-
-        if (node->type == QNODE_TEST) {
-            g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
-            return obj;
-        }
-
-        switch (qos_graph_edge_get_type(edge)) {
-        case QEDGE_PRODUCES:
-            obj = parent->get_driver(parent, path[current]);
-            break;
-
-        case QEDGE_CONSUMED_BY:
-            edge_arg = qos_graph_edge_get_arg(edge);
-            obj = qos_driver_new(node, obj, alloc, edge_arg);
-            qos_object_queue_destroy(obj);
-            break;
-
-        case QEDGE_CONTAINS:
-            obj = parent->get_device(parent, path[current]);
-            break;
-        }
-    }
-}
 
 /* The argument to run_one_test, which is the test function that is registered
  * with GTest, is a vector of strings.  The first item is the initial command
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 11/21] fuzz: add fuzzer skeleton
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (9 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 11:55   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 12/21] exec: keep ram block across fork when using qtest Bulekov, Alexander
                   ` (10 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

tests/fuzz/fuzz.c serves as the entry point for the virtual-device
fuzzer. Namely, libfuzzer invokes the LLVMFuzzerInitialize and
LLVMFuzzerTestOneInput functions, both of which are defined in this
file. This change adds a "FuzzTarget" struct, along with the
fuzz_add_target function, which should be used to define new fuzz
targets.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/fuzz/Makefile.include |   6 +
 tests/qtest/fuzz/fuzz.c           | 179 ++++++++++++++++++++++++++++++
 tests/qtest/fuzz/fuzz.h           |  95 ++++++++++++++++
 3 files changed, 280 insertions(+)
 create mode 100644 tests/qtest/fuzz/Makefile.include
 create mode 100644 tests/qtest/fuzz/fuzz.c
 create mode 100644 tests/qtest/fuzz/fuzz.h

diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
new file mode 100644
index 0000000000..8632bb89f4
--- /dev/null
+++ b/tests/qtest/fuzz/Makefile.include
@@ -0,0 +1,6 @@
+QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
+
+fuzz-obj-y += tests/qtest/libqtest.o
+fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
+
+FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c
new file mode 100644
index 0000000000..0d78ac8d36
--- /dev/null
+++ b/tests/qtest/fuzz/fuzz.c
@@ -0,0 +1,179 @@
+/*
+ * fuzzing driver
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include <wordexp.h>
+
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
+#include "tests/qtest/libqtest.h"
+#include "tests/qtest/libqos/qgraph.h"
+#include "fuzz.h"
+
+#define MAX_EVENT_LOOPS 10
+
+typedef struct FuzzTargetState {
+        FuzzTarget *target;
+        QSLIST_ENTRY(FuzzTargetState) target_list;
+} FuzzTargetState;
+
+typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList;
+
+static const char *fuzz_arch = TARGET_NAME;
+
+static FuzzTargetList *fuzz_target_list;
+static FuzzTarget *fuzz_target;
+static QTestState *fuzz_qts;
+
+
+
+void flush_events(QTestState *s)
+{
+    int i = MAX_EVENT_LOOPS;
+    while (g_main_context_pending(NULL) && i-- > 0) {
+        main_loop_wait(false);
+    }
+}
+
+static QTestState *qtest_setup(void)
+{
+    qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts);
+    return qtest_inproc_init(&fuzz_qts, false, fuzz_arch,
+            &qtest_server_inproc_recv);
+}
+
+void fuzz_add_target(const FuzzTarget *target)
+{
+    FuzzTargetState *tmp;
+    FuzzTargetState *target_state;
+    if (!fuzz_target_list) {
+        fuzz_target_list = g_new0(FuzzTargetList, 1);
+    }
+
+    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
+        if (g_strcmp0(tmp->target->name, target->name) == 0) {
+            fprintf(stderr, "Error: Fuzz target name %s already in use\n",
+                    target->name);
+            abort();
+        }
+    }
+    target_state = g_new0(FuzzTargetState, 1);
+    target_state->target = g_new0(FuzzTarget, 1);
+    *(target_state->target) = *target;
+    QSLIST_INSERT_HEAD(fuzz_target_list, target_state, target_list);
+}
+
+
+
+static void usage(char *path)
+{
+    printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path);
+    printf("where FUZZ_TARGET is one of:\n");
+    FuzzTargetState *tmp;
+    if (!fuzz_target_list) {
+        fprintf(stderr, "Fuzz target list not initialized\n");
+        abort();
+    }
+    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
+        printf(" * %s  : %s\n", tmp->target->name,
+                tmp->target->description);
+    }
+    exit(0);
+}
+
+static FuzzTarget *fuzz_get_target(char* name)
+{
+    FuzzTargetState *tmp;
+    if (!fuzz_target_list) {
+        fprintf(stderr, "Fuzz target list not initialized\n");
+        abort();
+    }
+
+    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
+        if (strcmp(tmp->target->name, name) == 0) {
+            return tmp->target;
+        }
+    }
+    return NULL;
+}
+
+
+/* Executed for each fuzzing-input */
+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size)
+{
+    /*
+     * Do the pre-fuzz-initialization before the first fuzzing iteration,
+     * instead of before the actual fuzz loop. This is needed since libfuzzer
+     * may fork off additional workers, prior to the fuzzing loop, and if
+     * pre_fuzz() sets up e.g. shared memory, this should be done for the
+     * individual worker processes
+     */
+    static int pre_fuzz_done;
+    if (!pre_fuzz_done && fuzz_target->pre_fuzz) {
+        fuzz_target->pre_fuzz(fuzz_qts);
+        pre_fuzz_done = true;
+    }
+
+    fuzz_target->fuzz(fuzz_qts, Data, Size);
+    return 0;
+}
+
+/* Executed once, prior to fuzzing */
+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
+{
+
+    char *target_name;
+
+    /* Initialize qgraph and modules */
+    qos_graph_init();
+    module_call_init(MODULE_INIT_FUZZ_TARGET);
+    module_call_init(MODULE_INIT_QOM);
+    module_call_init(MODULE_INIT_LIBQOS);
+
+    if (*argc <= 1) {
+        usage(**argv);
+    }
+
+    /* Identify the fuzz target */
+    target_name = (*argv)[1];
+    if (!strstr(target_name, "--fuzz-target=")) {
+        usage(**argv);
+    }
+
+    target_name += strlen("--fuzz-target=");
+
+    fuzz_target = fuzz_get_target(target_name);
+    if (!fuzz_target) {
+        usage(**argv);
+    }
+
+    fuzz_qts = qtest_setup();
+
+    if (fuzz_target->pre_vm_init) {
+        fuzz_target->pre_vm_init();
+    }
+
+    /* Run QEMU's softmmu main with the fuzz-target dependent arguments */
+    const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target);
+
+    /* Split the runcmd into an argv and argc */
+    wordexp_t result;
+    wordexp(init_cmdline, &result, 0);
+
+    qemu_init(result.we_wordc, result.we_wordv, NULL);
+
+    return 0;
+}
diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h
new file mode 100644
index 0000000000..03901d414e
--- /dev/null
+++ b/tests/qtest/fuzz/fuzz.h
@@ -0,0 +1,95 @@
+/*
+ * fuzzing driver
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef FUZZER_H_
+#define FUZZER_H_
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+
+#include "tests/qtest/libqtest.h"
+
+/**
+ * A libfuzzer fuzzing target
+ *
+ * The QEMU fuzzing binary is built with all available targets, each
+ * with a unique @name that can be specified on the command-line to
+ * select which target should run.
+ *
+ * A target must implement ->fuzz() to process a random input.  If QEMU
+ * crashes in ->fuzz() then libfuzzer will record a failure.
+ *
+ * Fuzzing targets are registered with fuzz_add_target():
+ *
+ *   static const FuzzTarget fuzz_target = {
+ *       .name = "my-device-fifo",
+ *       .description = "Fuzz the FIFO buffer registers of my-device",
+ *       ...
+ *   };
+ *
+ *   static void register_fuzz_target(void)
+ *   {
+ *       fuzz_add_target(&fuzz_target);
+ *   }
+ *   fuzz_target_init(register_fuzz_target);
+ */
+typedef struct FuzzTarget {
+    const char *name;         /* target identifier (passed to --fuzz-target=)*/
+    const char *description;  /* help text */
+
+
+    /*
+     * returns the arg-list that is passed to qemu/softmmu init()
+     * Cannot be NULL
+     */
+    const char* (*get_init_cmdline)(struct FuzzTarget *);
+
+    /*
+     * will run once, prior to running qemu/softmmu init.
+     * eg: set up shared-memory for communication with the child-process
+     * Can be NULL
+     */
+    void(*pre_vm_init)(void);
+
+    /*
+     * will run once, after QEMU has been initialized, prior to the fuzz-loop.
+     * eg: detect the memory map
+     * Can be NULL
+     */
+    void(*pre_fuzz)(QTestState *);
+
+    /*
+     * accepts and executes an input from libfuzzer. this is repeatedly
+     * executed during the fuzzing loop. Its should handle setup, input
+     * execution and cleanup.
+     * Cannot be NULL
+     */
+    void(*fuzz)(QTestState *, const unsigned char *, size_t);
+
+} FuzzTarget;
+
+void flush_events(QTestState *);
+void reboot(QTestState *);
+
+/*
+ * makes a copy of *target and adds it to the target-list.
+ * i.e. fine to set up target on the caller's stack
+ */
+void fuzz_add_target(const FuzzTarget *target);
+
+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size);
+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp);
+
+#endif
+
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 12/21] exec: keep ram block across fork when using qtest
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (10 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 11/21] fuzz: add fuzzer skeleton Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 13:00   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest Bulekov, Alexander
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: pbonzini, bsd, Richard Henderson, stefanha, Bulekov,  Alexander

Ram blocks were marked MADV_DONTFORK breaking fuzzing-tests which
execute each test-input in a forked process.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 exec.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/exec.c b/exec.c
index d4b769d0d4..99368f175b 100644
--- a/exec.c
+++ b/exec.c
@@ -35,6 +35,7 @@
 #include "sysemu/kvm.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/tcg.h"
+#include "sysemu/qtest.h"
 #include "qemu/timer.h"
 #include "qemu/config-file.h"
 #include "qemu/error-report.h"
@@ -2306,8 +2307,15 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
     if (new_block->host) {
         qemu_ram_setup_dump(new_block->host, new_block->max_length);
         qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE);
-        /* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */
-        qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK);
+        /*
+         * MADV_DONTFORK is also needed by KVM in absence of synchronous MMU
+         * Configure it unless the machine is a qtest server, in which case
+         * KVM is not used and it may be forked (eg for fuzzing purposes).
+         */
+        if (!qtest_enabled()) {
+            qemu_madvise(new_block->host, new_block->max_length,
+                         QEMU_MADV_DONTFORK);
+        }
         ram_block_notify_add(new_block->host, new_block->max_length);
     }
 }
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (11 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 12/21] exec: keep ram block across fork when using qtest Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-30 14:42   ` Stefan Hajnoczi
                     ` (2 more replies)
  2020-01-29  5:34 ` [PATCH v8 14/21] fuzz: support for fork-based fuzzing Bulekov, Alexander
                   ` (8 subsequent siblings)
  21 siblings, 3 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, bsd, stefanha, Bulekov, Alexander

The qtest-based fuzzer makes use of forking to reset-state between
tests. Keep the callback enabled, so the call_rcu thread gets created
within the child process.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
---
 vl.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/vl.c b/vl.c
index bb77935f04..cf8e2d3ebb 100644
--- a/vl.c
+++ b/vl.c
@@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
     set_memory_options(&ram_slots, &maxram_size, machine_class);
 
     os_daemonize();
-    rcu_disable_atfork();
+
+    /*
+     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
+     * may be forked testing purposes (e.g. fork-server based fuzzing)
+     */
+    if (!qtest_enabled()) {
+        rcu_disable_atfork();
+    }
 
     if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
         error_reportf_err(err, "cannot create PID file: ");
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 14/21] fuzz: support for fork-based fuzzing.
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (12 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 16/21] fuzz: add target/fuzz makefile rules Bulekov, Alexander
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

fork() is a simple way to ensure that state does not leak in between
fuzzing runs. Unfortunately, the fuzzer mutation engine relies on
bitmaps which contain coverage information for each fuzzing run, and
these bitmaps should be copied from the child to the parent(where the
mutation occurs). These bitmaps are created through compile-time
instrumentation and they are not shared with fork()-ed processes, by
default. To address this, we create a shared memory region, adjust its
size and map it _over_ the counter region. Furthermore, libfuzzer
doesn't generally expose the globals that specify the location of the
counters/coverage bitmap. As a workaround, we rely on a custom linker
script which forces all of the bitmaps we care about to be placed in a
contiguous region, which is easy to locate and mmap over.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/fuzz/Makefile.include |  5 +++
 tests/qtest/fuzz/fork_fuzz.c      | 55 +++++++++++++++++++++++++++++++
 tests/qtest/fuzz/fork_fuzz.h      | 23 +++++++++++++
 tests/qtest/fuzz/fork_fuzz.ld     | 37 +++++++++++++++++++++
 4 files changed, 120 insertions(+)
 create mode 100644 tests/qtest/fuzz/fork_fuzz.c
 create mode 100644 tests/qtest/fuzz/fork_fuzz.h
 create mode 100644 tests/qtest/fuzz/fork_fuzz.ld

diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
index 8632bb89f4..a90915d56d 100644
--- a/tests/qtest/fuzz/Makefile.include
+++ b/tests/qtest/fuzz/Makefile.include
@@ -2,5 +2,10 @@ QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
 
 fuzz-obj-y += tests/qtest/libqtest.o
 fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
+fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
 
 FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
+
+# Linker Script to force coverage-counters into known regions which we can mark
+# shared
+FUZZ_LDFLAGS += -Xlinker -T$(SRC_PATH)/tests/qtest/fuzz/fork_fuzz.ld
diff --git a/tests/qtest/fuzz/fork_fuzz.c b/tests/qtest/fuzz/fork_fuzz.c
new file mode 100644
index 0000000000..2bd0851903
--- /dev/null
+++ b/tests/qtest/fuzz/fork_fuzz.c
@@ -0,0 +1,55 @@
+/*
+ * Fork-based fuzzing helpers
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "fork_fuzz.h"
+
+
+void counter_shm_init(void)
+{
+    char *shm_path = g_strdup_printf("/qemu-fuzz-cntrs.%d", getpid());
+    int fd = shm_open(shm_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+    g_free(shm_path);
+
+    if (fd == -1) {
+        perror("Error: ");
+        exit(1);
+    }
+    if (ftruncate(fd, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START) == -1) {
+        perror("Error: ");
+        exit(1);
+    }
+    /* Copy what's in the counter region to the shm.. */
+    void *rptr = mmap(NULL ,
+            &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START,
+            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    memcpy(rptr,
+           &__FUZZ_COUNTERS_START,
+           &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START);
+
+    munmap(rptr, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START);
+
+    /* And map the shm over the counter region */
+    rptr = mmap(&__FUZZ_COUNTERS_START,
+            &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START,
+            PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
+
+    close(fd);
+
+    if (!rptr) {
+        perror("Error: ");
+        exit(1);
+    }
+}
+
+
diff --git a/tests/qtest/fuzz/fork_fuzz.h b/tests/qtest/fuzz/fork_fuzz.h
new file mode 100644
index 0000000000..9ecb8b58ef
--- /dev/null
+++ b/tests/qtest/fuzz/fork_fuzz.h
@@ -0,0 +1,23 @@
+/*
+ * Fork-based fuzzing helpers
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef FORK_FUZZ_H
+#define FORK_FUZZ_H
+
+extern uint8_t __FUZZ_COUNTERS_START;
+extern uint8_t __FUZZ_COUNTERS_END;
+
+void counter_shm_init(void);
+
+#endif
+
diff --git a/tests/qtest/fuzz/fork_fuzz.ld b/tests/qtest/fuzz/fork_fuzz.ld
new file mode 100644
index 0000000000..b23a59f194
--- /dev/null
+++ b/tests/qtest/fuzz/fork_fuzz.ld
@@ -0,0 +1,37 @@
+/* We adjust linker script modification to place all of the stuff that needs to
+ * persist across fuzzing runs into a contiguous seciton of memory. Then, it is
+ * easy to re-map the counter-related memory as shared.
+*/
+
+SECTIONS
+{
+  .data.fuzz_start : ALIGN(4K)
+  {
+      __FUZZ_COUNTERS_START = .;
+      __start___sancov_cntrs = .;
+      *(_*sancov_cntrs);
+      __stop___sancov_cntrs = .;
+
+      /* Lowest stack counter */
+      *(__sancov_lowest_stack);
+  }
+  .data.fuzz_ordered :
+  {
+      /* Coverage counters. They're not necessary for fuzzing, but are useful
+       * for analyzing the fuzzing performance
+       */
+      __start___llvm_prf_cnts = .;
+      *(*llvm_prf_cnts);
+      __stop___llvm_prf_cnts = .;
+
+      /* Internal Libfuzzer TracePC object which contains the ValueProfileMap */
+      FuzzerTracePC*(.bss*);
+  }
+  .data.fuzz_end : ALIGN(4K)
+  {
+      __FUZZ_COUNTERS_END = .;
+  }
+}
+/* Dont overwrite the SECTIONS in the default linker script. Instead insert the
+ * above into the default script */
+INSERT AFTER .data;
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 16/21] fuzz: add target/fuzz makefile rules
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (13 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 14/21] fuzz: support for fork-based fuzzing Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-29  5:34 ` [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets Bulekov, Alexander
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, bsd, Darren Kenny, stefanha, Bulekov,  Alexander

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 Makefile        | 15 ++++++++++++++-
 Makefile.objs   |  2 +-
 Makefile.target | 16 ++++++++++++++++
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index e6de7a47bb..dab291455f 100644
--- a/Makefile
+++ b/Makefile
@@ -464,7 +464,7 @@ config-host.h-timestamp: config-host.mak
 qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
 
-TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
+TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
 
 SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
 $(SOFTMMU_ALL_RULES): $(authz-obj-y)
@@ -478,6 +478,15 @@ ifdef DECOMPRESS_EDK2_BLOBS
 $(SOFTMMU_ALL_RULES): $(edk2-decompressed)
 endif
 
+SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES))
+$(SOFTMMU_FUZZ_RULES): $(authz-obj-y)
+$(SOFTMMU_FUZZ_RULES): $(block-obj-y)
+$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y)
+$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y)
+$(SOFTMMU_FUZZ_RULES): $(io-obj-y)
+$(SOFTMMU_FUZZ_RULES): config-all-devices.mak
+$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
+
 .PHONY: $(TARGET_DIRS_RULES)
 # The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
 # $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal
@@ -528,6 +537,9 @@ subdir-slirp: slirp/all
 $(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
 	$(qom-obj-y)
 
+$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
+	$(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
+
 ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
 ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
 # Only keep -O and -g cflags
@@ -537,6 +549,7 @@ $(ROM_DIRS_RULES):
 
 .PHONY: recurse-all recurse-clean recurse-install
 recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
+recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS))
 recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
 recurse-install: $(addsuffix /install, $(TARGET_DIRS))
 $(addsuffix /install, $(TARGET_DIRS)): all
diff --git a/Makefile.objs b/Makefile.objs
index 5ab166fed5..9d87a1009e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -84,8 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
 # qapi
 
 common-obj-y += qapi/
-
 softmmu-obj-y = main.o
+
 endif
 
 #######################################################################
diff --git a/Makefile.target b/Makefile.target
index 8dcf3dddd8..fd6fe79495 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -227,6 +227,22 @@ ifdef CONFIG_TRACE_SYSTEMTAP
 	rm -f *.stp
 endif
 
+ifdef CONFIG_FUZZ
+include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include
+include $(SRC_PATH)/tests/qtest/Makefile.include
+
+fuzz: fuzz-vars
+fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
+fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
+fuzz-vars: $(QEMU_PROG_FUZZ)
+dummy := $(call unnest-vars,, fuzz-obj-y)
+
+
+$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
+	$(call LINK, $(filter-out %.mak, $^))
+
+endif
+
 install: all
 ifneq ($(PROGS),)
 	$(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (14 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 16/21] fuzz: add target/fuzz makefile rules Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 13:18   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 17/21] fuzz: add configure flag --enable-fuzzing Bulekov, Alexander
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/fuzz/Makefile.include |   2 +
 tests/qtest/fuzz/qos_fuzz.c       | 229 ++++++++++++++++++++++++++++++
 tests/qtest/fuzz/qos_fuzz.h       |  33 +++++
 3 files changed, 264 insertions(+)
 create mode 100644 tests/qtest/fuzz/qos_fuzz.c
 create mode 100644 tests/qtest/fuzz/qos_fuzz.h

diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
index a90915d56d..e3bdd33ff4 100644
--- a/tests/qtest/fuzz/Makefile.include
+++ b/tests/qtest/fuzz/Makefile.include
@@ -1,8 +1,10 @@
 QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
 
 fuzz-obj-y += tests/qtest/libqtest.o
+fuzz-obj-y += $(libqos-obj-y)
 fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
 fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
+fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
 
 FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
 
diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c
new file mode 100644
index 0000000000..efdcc6e9d3
--- /dev/null
+++ b/tests/qtest/fuzz/qos_fuzz.c
@@ -0,0 +1,229 @@
+/*
+ * QOS-assisted fuzzing helpers
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
+
+#include "tests/qtest/libqtest.h"
+#include "tests/qtest/libqos/malloc.h"
+#include "tests/qtest/libqos/qgraph.h"
+#include "tests/qtest/libqos/qgraph_internal.h"
+#include "tests/qtest/libqos/qos_external.h"
+
+#include "fuzz.h"
+#include "qos_fuzz.h"
+
+#include "qapi/qapi-commands-machine.h"
+#include "qapi/qapi-commands-qom.h"
+#include "qapi/qmp/qlist.h"
+
+
+void *fuzz_qos_obj;
+QGuestAllocator *fuzz_qos_alloc;
+
+static const char *fuzz_target_name;
+static char **fuzz_path_vec;
+
+/*
+ * Replaced the qmp commands with direct qmp_marshal calls.
+ * Probably there is a better way to do this
+ */
+static void qos_set_machines_devices_available(void)
+{
+    QDict *req = qdict_new();
+    QObject *response;
+    QDict *args = qdict_new();
+    QList *lst;
+    Error *err = NULL;
+
+    qmp_marshal_query_machines(NULL, &response, &err);
+    assert(!err);
+    lst = qobject_to(QList, response);
+    apply_to_qlist(lst, true);
+
+    qobject_unref(response);
+
+
+    qdict_put_str(req, "execute", "qom-list-types");
+    qdict_put_str(args, "implements", "device");
+    qdict_put_bool(args, "abstract", true);
+    qdict_put_obj(req, "arguments", (QObject *) args);
+
+    qmp_marshal_qom_list_types(args, &response, &err);
+    assert(!err);
+    lst = qobject_to(QList, response);
+    apply_to_qlist(lst, false);
+    qobject_unref(response);
+    qobject_unref(req);
+}
+
+static char **current_path;
+
+void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc)
+{
+    return allocate_objects(qts, current_path + 1, p_alloc);
+}
+
+static const char *qos_build_main_args(void)
+{
+    char **path = fuzz_path_vec;
+    QOSGraphNode *test_node;
+    GString *cmd_line = g_string_new(path[0]);
+    void *test_arg;
+
+    /* Before test */
+    current_path = path;
+    test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]);
+    test_arg = test_node->u.test.arg;
+    if (test_node->u.test.before) {
+        test_arg = test_node->u.test.before(cmd_line, test_arg);
+    }
+    /* Prepend the arguments that we need */
+    g_string_prepend(cmd_line,
+            TARGET_NAME " -display none -machine accel=qtest -m 64 ");
+    return cmd_line->str;
+}
+
+/*
+ * This function is largely a copy of qos-test.c:walk_path. Since walk_path
+ * is itself a callback, its a little annoying to add another argument/layer of
+ * indirection
+ */
+static void walk_path(QOSGraphNode *orig_path, int len)
+{
+    QOSGraphNode *path;
+    QOSGraphEdge *edge;
+
+    /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */
+    QOSEdgeType etype = QEDGE_CONSUMED_BY;
+
+    /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */
+    char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2));
+    int path_vec_size = 0;
+
+    char *after_cmd, *before_cmd, *after_device;
+    GString *after_device_str = g_string_new("");
+    char *node_name = orig_path->name, *path_str;
+
+    GString *cmd_line = g_string_new("");
+    GString *cmd_line2 = g_string_new("");
+
+    path = qos_graph_get_node(node_name); /* root */
+    node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */
+
+    path_vec[path_vec_size++] = node_name;
+    path_vec[path_vec_size++] = qos_get_machine_type(node_name);
+
+    for (;;) {
+        path = qos_graph_get_node(node_name);
+        if (!path->path_edge) {
+            break;
+        }
+
+        node_name = qos_graph_edge_get_dest(path->path_edge);
+
+        /* append node command line + previous edge command line */
+        if (path->command_line && etype == QEDGE_CONSUMED_BY) {
+            g_string_append(cmd_line, path->command_line);
+            g_string_append(cmd_line, after_device_str->str);
+            g_string_truncate(after_device_str, 0);
+        }
+
+        path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge);
+        /* detect if edge has command line args */
+        after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge);
+        after_device = qos_graph_edge_get_extra_device_opts(path->path_edge);
+        before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge);
+        edge = qos_graph_get_edge(path->name, node_name);
+        etype = qos_graph_edge_get_type(edge);
+
+        if (before_cmd) {
+            g_string_append(cmd_line, before_cmd);
+        }
+        if (after_cmd) {
+            g_string_append(cmd_line2, after_cmd);
+        }
+        if (after_device) {
+            g_string_append(after_device_str, after_device);
+        }
+    }
+
+    path_vec[path_vec_size++] = NULL;
+    g_string_append(cmd_line, after_device_str->str);
+    g_string_free(after_device_str, true);
+
+    g_string_append(cmd_line, cmd_line2->str);
+    g_string_free(cmd_line2, true);
+
+    /*
+     * here position 0 has <arch>/<machine>, position 1 has <machine>.
+     * The path must not have the <arch>, qtest_add_data_func adds it.
+     */
+    path_str = g_strjoinv("/", path_vec + 1);
+
+    /* Check that this is the test we care about: */
+    char *test_name = strrchr(path_str, '/') + 1;
+    if (strcmp(test_name, fuzz_target_name) == 0) {
+        /*
+         * put arch/machine in position 1 so run_one_test can do its work
+         * and add the command line at position 0.
+         */
+        path_vec[1] = path_vec[0];
+        path_vec[0] = g_string_free(cmd_line, false);
+
+        fuzz_path_vec = path_vec;
+    } else {
+        g_free(path_vec);
+    }
+
+    g_free(path_str);
+}
+
+static const char *qos_get_cmdline(FuzzTarget *t)
+{
+    /*
+     * Set a global variable that we use to identify the qos_path for our
+     * fuzz_target
+     */
+    fuzz_target_name = t->name;
+    qos_set_machines_devices_available();
+    qos_graph_foreach_test_path(walk_path);
+    return qos_build_main_args();
+}
+
+void fuzz_add_qos_target(
+        FuzzTarget *fuzz_opts,
+        const char *interface,
+        QOSGraphTestOptions *opts
+        )
+{
+    qos_add_test(fuzz_opts->name, interface, NULL, opts);
+    fuzz_opts->get_init_cmdline = qos_get_cmdline;
+    fuzz_add_target(fuzz_opts);
+}
+
+void qos_init_path(QTestState *s)
+{
+    fuzz_qos_obj = qos_allocate_objects(s , &fuzz_qos_alloc);
+}
diff --git a/tests/qtest/fuzz/qos_fuzz.h b/tests/qtest/fuzz/qos_fuzz.h
new file mode 100644
index 0000000000..477f11b02b
--- /dev/null
+++ b/tests/qtest/fuzz/qos_fuzz.h
@@ -0,0 +1,33 @@
+/*
+ * QOS-assisted fuzzing helpers
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef _QOS_FUZZ_H_
+#define _QOS_FUZZ_H_
+
+#include "tests/qtest/fuzz/fuzz.h"
+#include "tests/qtest/libqos/qgraph.h"
+
+int qos_fuzz(const unsigned char *Data, size_t Size);
+void qos_setup(void);
+
+extern void *fuzz_qos_obj;
+extern QGuestAllocator *fuzz_qos_alloc;
+
+void fuzz_add_qos_target(
+        FuzzTarget *fuzz_opts,
+        const char *interface,
+        QOSGraphTestOptions *opts
+        );
+
+void qos_init_path(QTestState *);
+
+#endif
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 17/21] fuzz: add configure flag --enable-fuzzing
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (15 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 13:19   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 18/21] fuzz: add i440fx fuzz targets Bulekov, Alexander
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: pbonzini, bsd, Philippe Mathieu-Daudé,
	stefanha, Bulekov,  Alexander

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
 configure | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/configure b/configure
index 08c3a1c1f0..1a1e57eb61 100755
--- a/configure
+++ b/configure
@@ -504,6 +504,7 @@ debug_mutex="no"
 libpmem=""
 default_devices="yes"
 plugins="no"
+fuzzing="no"
 
 supported_cpu="no"
 supported_os="no"
@@ -634,6 +635,15 @@ int main(void) { return 0; }
 EOF
 }
 
+write_c_fuzzer_skeleton() {
+    cat > $TMPC <<EOF
+#include <stdint.h>
+#include <sys/types.h>
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; }
+EOF
+}
+
 if check_define __linux__ ; then
   targetos="Linux"
 elif check_define _WIN32 ; then
@@ -1540,6 +1550,10 @@ for opt do
   ;;
   --disable-containers) use_containers="no"
   ;;
+  --enable-fuzzing) fuzzing=yes
+  ;;
+  --disable-fuzzing) fuzzing=no
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -5992,6 +6006,15 @@ EOF
   fi
 fi
 
+##########################################
+# checks for fuzzer
+if test "$fuzzing" = "yes" ; then
+  write_c_fuzzer_skeleton
+  if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address,fuzzer" ""; then
+      have_fuzzer=yes
+  fi
+fi
+
 ##########################################
 # check for libpmem
 
@@ -6576,6 +6599,7 @@ echo "libpmem support   $libpmem"
 echo "libudev           $libudev"
 echo "default devices   $default_devices"
 echo "plugin support    $plugins"
+echo "fuzzing support   $fuzzing"
 
 if test "$supported_cpu" = "no"; then
     echo
@@ -7400,6 +7424,16 @@ fi
 if test "$sheepdog" = "yes" ; then
   echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
 fi
+if test "$fuzzing" = "yes" ; then
+  if test "$have_fuzzer" = "yes"; then
+    FUZZ_LDFLAGS=" -fsanitize=address,fuzzer"
+    FUZZ_CFLAGS=" -fsanitize=address,fuzzer"
+    CFLAGS=" -fsanitize=address,fuzzer-no-link"
+  else
+    error_exit "Your compiler doesn't support -fsanitize=address,fuzzer"
+    exit 1
+  fi
+fi
 
 if test "$plugins" = "yes" ; then
     echo "CONFIG_PLUGIN=y" >> $config_host_mak
@@ -7502,6 +7536,11 @@ if test "$libudev" != "no"; then
     echo "CONFIG_LIBUDEV=y" >> $config_host_mak
     echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak
 fi
+if test "$fuzzing" != "no"; then
+    echo "CONFIG_FUZZ=y" >> $config_host_mak
+    echo "FUZZ_CFLAGS=$FUZZ_CFLAGS" >> $config_host_mak
+    echo "FUZZ_LDFLAGS=$FUZZ_LDFLAGS" >> $config_host_mak
+fi
 
 if test "$edk2_blobs" = "yes" ; then
   echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 18/21] fuzz: add i440fx fuzz targets
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (16 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 17/21] fuzz: add configure flag --enable-fuzzing Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 13:26   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 19/21] fuzz: add virtio-net fuzz target Bulekov, Alexander
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

These three targets should simply fuzz reads/writes to a couple ioports,
but they mostly serve as examples of different ways to write targets.
They demonstrate using qtest and qos for fuzzing, as well as using
rebooting and forking to reset state, or not resetting it at all.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/fuzz/Makefile.include |   3 +
 tests/qtest/fuzz/i440fx_fuzz.c    | 178 ++++++++++++++++++++++++++++++
 2 files changed, 181 insertions(+)
 create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c

diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
index e3bdd33ff4..38b8cdd9f1 100644
--- a/tests/qtest/fuzz/Makefile.include
+++ b/tests/qtest/fuzz/Makefile.include
@@ -6,6 +6,9 @@ fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
 fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
 fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
 
+# Targets
+fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
+
 FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
 
 # Linker Script to force coverage-counters into known regions which we can mark
diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c
new file mode 100644
index 0000000000..c7791182b8
--- /dev/null
+++ b/tests/qtest/fuzz/i440fx_fuzz.c
@@ -0,0 +1,178 @@
+/*
+ * I440FX Fuzzing Target
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/main-loop.h"
+#include "tests/qtest/libqtest.h"
+#include "tests/qtest/libqos/pci.h"
+#include "tests/qtest/libqos/pci-pc.h"
+#include "fuzz.h"
+#include "fuzz/qos_fuzz.h"
+#include "fuzz/fork_fuzz.h"
+
+
+#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
+#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
+
+enum action_id {
+    WRITEB,
+    WRITEW,
+    WRITEL,
+    READB,
+    READW,
+    READL,
+    ACTION_MAX
+};
+
+static void i440fx_fuzz_qtest(QTestState *s,
+        const unsigned char *Data, size_t Size) {
+    typedef struct QTestFuzzAction {
+        uint32_t value;
+        uint8_t id;
+        uint8_t addr;
+    } QTestFuzzAction;
+    QTestFuzzAction a;
+
+    while (Size >= sizeof(a)) {
+        memcpy(&a, Data, sizeof(a));
+        uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
+                                      I440FX_PCI_HOST_BRIDGE_DATA;
+        switch (a.id % ACTION_MAX) {
+        case WRITEB:
+            qtest_outb(s, addr, (uint8_t)a.value);
+            break;
+        case WRITEW:
+            qtest_outw(s, addr, (uint16_t)a.value);
+            break;
+        case WRITEL:
+            qtest_outl(s, addr, (uint32_t)a.value);
+            break;
+        case READB:
+            qtest_inb(s, addr);
+            break;
+        case READW:
+            qtest_inw(s, addr);
+            break;
+        case READL:
+            qtest_inl(s, addr);
+            break;
+        }
+        Size -= sizeof(a);
+        Data += sizeof(a);
+    }
+    flush_events(s);
+}
+
+static void i440fx_fuzz_qos(QTestState *s,
+        const unsigned char *Data, size_t Size) {
+
+    typedef struct QOSFuzzAction {
+        uint32_t value;
+        int devfn;
+        uint8_t offset;
+        uint8_t id;
+    } QOSFuzzAction;
+
+    static QPCIBus *bus;
+    if (!bus) {
+        bus = qpci_new_pc(s, fuzz_qos_alloc);
+    }
+
+    QOSFuzzAction a;
+    while (Size >= sizeof(a)) {
+        memcpy(&a, Data, sizeof(a));
+        switch (a.id % ACTION_MAX) {
+        case WRITEB:
+            bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
+            break;
+        case WRITEW:
+            bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
+            break;
+        case WRITEL:
+            bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
+            break;
+        case READB:
+            bus->config_readb(bus, a.devfn, a.offset);
+            break;
+        case READW:
+            bus->config_readw(bus, a.devfn, a.offset);
+            break;
+        case READL:
+            bus->config_readl(bus, a.devfn, a.offset);
+            break;
+        }
+        Size -= sizeof(a);
+        Data += sizeof(a);
+    }
+    flush_events(s);
+}
+
+static void i440fx_fuzz_qos_fork(QTestState *s,
+        const unsigned char *Data, size_t Size) {
+    if (fork() == 0) {
+        i440fx_fuzz_qos(s, Data, Size);
+        _Exit(0);
+    } else {
+        wait(NULL);
+    }
+}
+
+static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
+                                       "-m 0 -display none";
+static const char *i440fx_argv(FuzzTarget *t)
+{
+    return i440fx_qtest_argv;
+}
+
+static void fork_init(void)
+{
+    counter_shm_init();
+}
+
+static void register_pci_fuzz_targets(void)
+{
+    /* Uses simple qtest commands and reboots to reset state */
+    fuzz_add_target(&(FuzzTarget){
+                .name = "i440fx-qtest-reboot-fuzz",
+                .description = "Fuzz the i440fx using raw qtest commands and"
+                               "rebooting after each run",
+                .get_init_cmdline = i440fx_argv,
+                .fuzz = i440fx_fuzz_qtest});
+
+    /* Uses libqos and forks to prevent state leakage */
+    fuzz_add_qos_target(&(FuzzTarget){
+                .name = "i440fx-qos-fork-fuzz",
+                .description = "Fuzz the i440fx using raw qtest commands and"
+                               "rebooting after each run",
+                .pre_vm_init = &fork_init,
+                .fuzz = i440fx_fuzz_qos_fork,},
+                "i440FX-pcihost",
+                &(QOSGraphTestOptions){}
+                );
+
+    /*
+     * Uses libqos. Doesn't do anything to reset state. Note that if we were to
+     * reboot after each run, we would also have to redo the qos-related
+     * initialization (qos_init_path)
+     */
+    fuzz_add_qos_target(&(FuzzTarget){
+                .name = "i440fx-qos-noreset-fuzz",
+                .description = "Fuzz the i440fx using raw qtest commands and"
+                               "rebooting after each run",
+                .fuzz = i440fx_fuzz_qos,},
+                "i440FX-pcihost",
+                &(QOSGraphTestOptions){}
+                );
+}
+
+fuzz_target_init(register_pci_fuzz_targets);
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 19/21] fuzz: add virtio-net fuzz target
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (17 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 18/21] fuzz: add i440fx fuzz targets Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-01-30 14:43   ` Stefan Hajnoczi
  2020-02-05 13:57   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 20/21] fuzz: add virtio-scsi " Bulekov, Alexander
                   ` (2 subsequent siblings)
  21 siblings, 2 replies; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

The virtio-net fuzz target feeds inputs to all three virtio-net
virtqueues, and uses forking to avoid leaking state between fuzz runs.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
---
 tests/qtest/fuzz/Makefile.include  |   1 +
 tests/qtest/fuzz/virtio_net_fuzz.c | 195 +++++++++++++++++++++++++++++
 2 files changed, 196 insertions(+)
 create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c

diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
index 38b8cdd9f1..77385777ef 100644
--- a/tests/qtest/fuzz/Makefile.include
+++ b/tests/qtest/fuzz/Makefile.include
@@ -8,6 +8,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
 
 # Targets
 fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
+fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
 
 FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
 
diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c
new file mode 100644
index 0000000000..e4e3e50865
--- /dev/null
+++ b/tests/qtest/fuzz/virtio_net_fuzz.c
@@ -0,0 +1,195 @@
+/*
+ * virtio-net Fuzzing Target
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "standard-headers/linux/virtio_config.h"
+#include "tests/qtest/libqtest.h"
+#include "tests/qtest/libqos/virtio-net.h"
+#include "fuzz.h"
+#include "fork_fuzz.h"
+#include "qos_fuzz.h"
+
+
+#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
+#define QVIRTIO_RX_VQ 0
+#define QVIRTIO_TX_VQ 1
+#define QVIRTIO_CTRL_VQ 2
+
+static int sockfds[2];
+static bool sockfds_initialized;
+
+static void virtio_net_fuzz_multi(QTestState *s,
+        const unsigned char *Data, size_t Size, bool check_used)
+{
+    typedef struct vq_action {
+        uint8_t queue;
+        uint8_t length;
+        uint8_t write;
+        uint8_t next;
+        uint8_t rx;
+    } vq_action;
+
+    uint32_t free_head = 0;
+
+    QGuestAllocator *t_alloc = fuzz_qos_alloc;
+
+    QVirtioNet *net_if = fuzz_qos_obj;
+    QVirtioDevice *dev = net_if->vdev;
+    QVirtQueue *q;
+    vq_action vqa;
+    while (Size >= sizeof(vqa)) {
+        memcpy(&vqa, Data, sizeof(vqa));
+        Data += sizeof(vqa);
+        Size -= sizeof(vqa);
+
+        q = net_if->queues[vqa.queue % 3];
+
+        vqa.length = vqa.length >= Size ? Size :  vqa.length;
+
+        /*
+         * Only attempt to write incoming packets, when using the socket
+         * backend. Otherwise, always place the input on a virtqueue.
+         */
+        if (vqa.rx && sockfds_initialized) {
+            write(sockfds[0], Data, vqa.length);
+        } else {
+            vqa.rx = 0;
+            uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
+            /*
+             * If checking used ring, ensure that the fuzzer doesn't trigger
+             * trivial asserion failure on zero-zied buffer
+             */
+            qtest_memwrite(s, req_addr, Data, vqa.length);
+
+
+            free_head = qvirtqueue_add(s, q, req_addr, vqa.length,
+                    vqa.write, vqa.next);
+            qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
+            qvirtqueue_kick(s, dev, q, free_head);
+        }
+
+        /*
+         * normally, we could just use qvirtio_wait_used_elem, but since we
+         * must manually run the main-loop for all the bhs to run, we use
+         * this hack with flush_events(), to run the main_loop
+         */
+        gint64 start_time = g_get_monotonic_time();
+        for (;;) {
+            /* Run the main loop */
+            qtest_clock_step(s, 100);
+            flush_events(s);
+            /* Poll the used vring only if we added to the TX or CTRL vq */
+            if (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
+                uint32_t got_desc_idx;
+                /* Input led to a virtio_error */
+                if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
+                    break;
+                }
+                if (dev->bus->get_queue_isr_status(dev, q) &&
+                        qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
+                    g_assert_cmpint(got_desc_idx, ==, free_head);
+                    break;
+                }
+                g_assert(g_get_monotonic_time() - start_time
+                         <= QVIRTIO_NET_TIMEOUT_US);
+            } else {
+                break;
+            }
+        }
+        Data += vqa.length;
+        Size -= vqa.length;
+    }
+}
+
+static void virtio_net_fork_fuzz(QTestState *s,
+        const unsigned char *Data, size_t Size)
+{
+    if (fork() == 0) {
+        virtio_net_fuzz_multi(s, Data, Size, false);
+        flush_events(s);
+        _Exit(0);
+    } else {
+        wait(NULL);
+    }
+}
+
+static void virtio_net_fork_fuzz_check_used(QTestState *s,
+        const unsigned char *Data, size_t Size)
+{
+    if (fork() == 0) {
+        virtio_net_fuzz_multi(s, Data, Size, true);
+        flush_events(s);
+        _Exit(0);
+    } else {
+        wait(NULL);
+    }
+}
+
+static void virtio_net_pre_fuzz(QTestState *s)
+{
+    qos_init_path(s);
+    counter_shm_init();
+}
+
+static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg)
+{
+    int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds);
+    g_assert_cmpint(ret, !=, -1);
+    fcntl(sockfds[0], F_SETFL, O_NONBLOCK);
+    sockfds_initialized = true;
+    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
+                           sockfds[1]);
+    return arg;
+}
+
+static void *virtio_net_test_setup_user(GString *cmd_line, void *arg)
+{
+    g_string_append_printf(cmd_line, " -netdev user,id=hs0 ");
+    return arg;
+}
+
+static void register_virtio_net_fuzz_targets(void)
+{
+    fuzz_add_qos_target(&(FuzzTarget){
+            .name = "virtio-net-socket",
+            .description = "Fuzz the virtio-net virtual queues. Fuzz incoming "
+            "traffic using the socket backend",
+            .pre_fuzz = &virtio_net_pre_fuzz,
+            .fuzz = virtio_net_fork_fuzz,},
+            "virtio-net",
+            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
+            );
+
+    fuzz_add_qos_target(&(FuzzTarget){
+            .name = "virtio-net-socket-check-used",
+            .description = "Fuzz the virtio-net virtual queues. Wait for the "
+            "descriptors to be used. Timeout may indicate improperly handled "
+            "input",
+            .pre_fuzz = &virtio_net_pre_fuzz,
+            .fuzz = virtio_net_fork_fuzz_check_used,},
+            "virtio-net",
+            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
+            );
+    fuzz_add_qos_target(&(FuzzTarget){
+            .name = "virtio-net-slirp",
+            .description = "Fuzz the virtio-net virtual queues with the slirp "
+            " backend. Warning: May result in network traffic emitted from the "
+            " process. Run in an isolated network environment.",
+            .pre_fuzz = &virtio_net_pre_fuzz,
+            .fuzz = virtio_net_fork_fuzz,},
+            "virtio-net",
+            &(QOSGraphTestOptions){.before = virtio_net_test_setup_user}
+            );
+}
+
+fuzz_target_init(register_virtio_net_fuzz_targets);
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 20/21] fuzz: add virtio-scsi fuzz target
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (18 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 19/21] fuzz: add virtio-net fuzz target Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 13:31   ` Darren Kenny
  2020-01-29  5:34 ` [PATCH v8 21/21] fuzz: add documentation to docs/devel/ Bulekov, Alexander
  2020-01-30 14:44 ` [PATCH v8 00/21] Add virtual device fuzzing support Stefan Hajnoczi
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laurent Vivier, Thomas Huth, Bulekov, Alexander, bsd, stefanha, pbonzini

The virtio-scsi fuzz target sets up and fuzzes the available virtio-scsi
queues. After an element is placed on a queue, the fuzzer can select
whether to perform a kick, or continue adding elements.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qtest/fuzz/Makefile.include   |   1 +
 tests/qtest/fuzz/virtio_scsi_fuzz.c | 200 ++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+)
 create mode 100644 tests/qtest/fuzz/virtio_scsi_fuzz.c

diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
index 77385777ef..cde3e9636c 100644
--- a/tests/qtest/fuzz/Makefile.include
+++ b/tests/qtest/fuzz/Makefile.include
@@ -9,6 +9,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
 # Targets
 fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
 fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
+fuzz-obj-y += tests/qtest/fuzz/virtio_scsi_fuzz.o
 
 FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
 
diff --git a/tests/qtest/fuzz/virtio_scsi_fuzz.c b/tests/qtest/fuzz/virtio_scsi_fuzz.c
new file mode 100644
index 0000000000..ee7ca5448c
--- /dev/null
+++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c
@@ -0,0 +1,200 @@
+/*
+ * virtio-serial Fuzzing Target
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ *  Alexander Bulekov   <alxndr@bu.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "tests/qtest/libqtest.h"
+#include "tests/qtest/libqos/virtio-net.h"
+#include "libqos/virtio-scsi.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_pci.h"
+#include "standard-headers/linux/virtio_scsi.h"
+#include "fuzz.h"
+#include "fork_fuzz.h"
+#include "qos_fuzz.h"
+
+#define PCI_SLOT                0x02
+#define PCI_FN                  0x00
+#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
+
+#define MAX_NUM_QUEUES 64
+
+/* Based on tests/virtio-scsi-test.c */
+typedef struct {
+    int num_queues;
+    QVirtQueue *vq[MAX_NUM_QUEUES + 2];
+} QVirtioSCSIQueues;
+
+static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask)
+{
+    QVirtioSCSIQueues *vs;
+    uint64_t feat;
+    int i;
+
+    vs = g_new0(QVirtioSCSIQueues, 1);
+
+    feat = qvirtio_get_features(dev);
+    if (mask) {
+        feat &= ~QVIRTIO_F_BAD_FEATURE | mask;
+    } else {
+        feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
+    }
+    qvirtio_set_features(dev, feat);
+
+    vs->num_queues = qvirtio_config_readl(dev, 0);
+
+    for (i = 0; i < vs->num_queues + 2; i++) {
+        vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i);
+    }
+
+    qvirtio_set_driver_ok(dev);
+
+    return vs;
+}
+
+static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues,
+        const unsigned char *Data, size_t Size)
+{
+    typedef struct vq_action {
+        uint8_t queue;
+        uint8_t length;
+        uint8_t write;
+        uint8_t next;
+        uint8_t kick;
+    } vq_action;
+
+    uint32_t free_head[MAX_NUM_QUEUES + 2] = {0};
+    QGuestAllocator *t_alloc = fuzz_qos_alloc;
+
+    QVirtioSCSI *scsi = fuzz_qos_obj;
+    QVirtioDevice *dev = scsi->vdev;
+    QVirtQueue *q;
+    vq_action vqa;
+    while (Size >= sizeof(vqa)) {
+        memcpy(&vqa, Data, sizeof(vqa));
+
+        Data += sizeof(vqa);
+        Size -= sizeof(vqa);
+
+        vqa.queue = vqa.queue % queues->num_queues;
+        vqa.length = vqa.length >= Size ? Size : vqa.length;
+        vqa.write = vqa.write & 1;
+        vqa.next = vqa.next & 1;
+        vqa.kick = vqa.kick & 1;
+
+
+        q = queues->vq[vqa.queue];
+
+        uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
+        qtest_memwrite(s, req_addr, Data, vqa.length);
+        if (free_head[vqa.queue] == 0) {
+            free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
+                    vqa.write, vqa.next);
+        } else {
+            qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
+        }
+
+        if (vqa.kick) {
+            qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
+            free_head[vqa.queue] = 0;
+        }
+        Data += vqa.length;
+        Size -= vqa.length;
+    }
+    for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
+        if (free_head[i]) {
+            qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
+        }
+    }
+}
+
+static void virtio_scsi_fork_fuzz(QTestState *s,
+        const unsigned char *Data, size_t Size)
+{
+    QVirtioSCSI *scsi = fuzz_qos_obj;
+    static QVirtioSCSIQueues *queues;
+    if (!queues) {
+        queues = qvirtio_scsi_init(scsi->vdev, 0);
+    }
+    if (fork() == 0) {
+        virtio_scsi_fuzz(s, queues, Data, Size);
+        flush_events(s);
+        _Exit(0);
+    } else {
+        wait(NULL);
+    }
+}
+
+static void virtio_scsi_with_flag_fuzz(QTestState *s,
+        const unsigned char *Data, size_t Size)
+{
+    QVirtioSCSI *scsi = fuzz_qos_obj;
+    static QVirtioSCSIQueues *queues;
+
+    if (fork() == 0) {
+        if (Size >= sizeof(uint64_t)) {
+            queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data);
+            virtio_scsi_fuzz(s, queues,
+                             Data + sizeof(uint64_t), Size - sizeof(uint64_t));
+            flush_events(s);
+        }
+        _Exit(0);
+    } else {
+        wait(NULL);
+    }
+}
+
+static void virtio_scsi_pre_fuzz(QTestState *s)
+{
+    qos_init_path(s);
+    counter_shm_init();
+}
+
+static void *virtio_scsi_test_setup(GString *cmd_line, void *arg)
+{
+    g_string_append(cmd_line,
+                    " -drive file=blkdebug::null-co://,"
+                    "file.image.read-zeroes=on,"
+                    "if=none,id=dr1,format=raw,file.align=4k "
+                    "-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
+    return arg;
+}
+
+
+static void register_virtio_scsi_fuzz_targets(void)
+{
+    fuzz_add_qos_target(&(FuzzTarget){
+                .name = "virtio-scsi-fuzz",
+                .description = "Fuzz the virtio-net virtual queues, forking"
+                                "for each fuzz run",
+                .pre_vm_init = &counter_shm_init,
+                .pre_fuzz = &virtio_scsi_pre_fuzz,
+                .fuzz = virtio_scsi_fork_fuzz,},
+                "virtio-scsi",
+                &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
+                );
+
+    fuzz_add_qos_target(&(FuzzTarget){
+                .name = "virtio-scsi-flags-fuzz",
+                .description = "Fuzz the virtio-net virtual queues, forking"
+                "for each fuzz run (also fuzzes the virtio flags)",
+                .pre_vm_init = &counter_shm_init,
+                .pre_fuzz = &virtio_scsi_pre_fuzz,
+                .fuzz = virtio_scsi_with_flag_fuzz,},
+                "virtio-scsi",
+                &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
+                );
+}
+
+fuzz_target_init(register_virtio_scsi_fuzz_targets);
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [PATCH v8 21/21] fuzz: add documentation to docs/devel/
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (19 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 20/21] fuzz: add virtio-scsi " Bulekov, Alexander
@ 2020-01-29  5:34 ` Bulekov, Alexander
  2020-02-05 13:33   ` Darren Kenny
  2020-01-30 14:44 ` [PATCH v8 00/21] Add virtual device fuzzing support Stefan Hajnoczi
  21 siblings, 1 reply; 55+ messages in thread
From: Bulekov, Alexander @ 2020-01-29  5:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, bsd, stefanha, Bulekov, Alexander

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 docs/devel/fuzzing.txt | 116 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)
 create mode 100644 docs/devel/fuzzing.txt

diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt
new file mode 100644
index 0000000000..324d2cd92b
--- /dev/null
+++ b/docs/devel/fuzzing.txt
@@ -0,0 +1,116 @@
+= Fuzzing =
+
+== Introduction ==
+
+This document describes the virtual-device fuzzing infrastructure in QEMU and
+how to use it to implement additional fuzzers.
+
+== Basics ==
+
+Fuzzing operates by passing inputs to an entry point/target function. The
+fuzzer tracks the code coverage triggered by the input. Based on these
+findings, the fuzzer mutates the input and repeats the fuzzing.
+
+To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer
+is an _in-process_ fuzzer. For the developer, this means that it is their
+responsibility to ensure that state is reset between fuzzing-runs.
+
+== Building the fuzzers ==
+
+NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is
+much faster, since the page-map has a smaller size. This is due to the fact that
+AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results
+in a large page-map, and a much slower fork().
+
+To build the fuzzers, install a recent version of clang:
+Configure with (substitute the clang binaries with the version you installed):
+
+    CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing
+
+Fuzz targets are built similarly to system/softmmu:
+
+    make i386-softmmu/fuzz
+
+This builds ./i386-softmmu/qemu-fuzz-i386
+
+The first option to this command is: --fuzz_taget=FUZZ_NAME
+To list all of the available fuzzers run qemu-fuzz-i386 with no arguments.
+
+eg:
+    ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz
+
+Internally, libfuzzer parses all arguments that do not begin with "--".
+Information about these is available by passing -help=1
+
+Now the only thing left to do is wait for the fuzzer to trigger potential
+crashes.
+
+== Adding a new fuzzer ==
+Coverage over virtual devices can be improved by adding additional fuzzers.
+Fuzzers are kept in tests/qtest/fuzz/ and should be added to
+tests/qtest/fuzz/Makefile.include
+
+Fuzzers can rely on both qtest and libqos to communicate with virtual devices.
+
+1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``.
+
+2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers
+for reference.
+
+3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the
+corresponding object to fuzz-obj-y
+
+Fuzzers can be more-or-less thought of as special qtest programs which can
+modify the qtest commands and/or qtest command arguments based on inputs
+provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the
+fuzzer loops over the byte-array interpreting it as a list of qtest commands,
+addresses, or values.
+
+= Implementation Details =
+
+== The Fuzzer's Lifecycle ==
+
+The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's
+own main(), which performs some setup, and calls the entrypoints:
+
+LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the
+necessary state
+
+LLVMFuzzerTestOneInput: called for each fuzzing run. Processes the input and
+resets the state at the end of each run.
+
+In more detail:
+
+LLVMFuzzerInitialize parses the arguments to the fuzzer (must start with two
+dashes, so they are ignored by libfuzzer main()). Currently, the arguments
+select the fuzz target. Then, the qtest client is initialized. If the target
+requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
+Then the QGraph is walked and the QEMU cmd_line is determined and saved.
+
+After this, the vl.c:qemu__main is called to set up the guest. There are
+target-specific hooks that can be called before and after qemu_main, for
+additional setup(e.g. PCI setup, or VM snapshotting).
+
+LLVMFuzzerTestOneInput: Uses qtest/qos functions to act based on the fuzz
+input. It is also responsible for manually calling the main loop/main_loop_wait
+to ensure that bottom halves are executed and any cleanup required before the
+next input.
+
+Since the same process is reused for many fuzzing runs, QEMU state needs to
+be reset at the end of each run. There are currently two implemented
+options for resetting state:
+1. Reboot the guest between runs.
+   Pros: Straightforward and fast for simple fuzz targets.
+   Cons: Depending on the device, does not reset all device state. If the
+   device requires some initialization prior to being ready for fuzzing
+   (common for QOS-based targets), this initialization needs to be done after
+   each reboot.
+   Example target: i440fx-qtest-reboot-fuzz
+2. Run each test case in a separate forked process and copy the coverage
+   information back to the parent. This is fairly similar to AFL's "deferred"
+   fork-server mode [3]
+   Pros: Relatively fast. Devices only need to be initialized once. No need
+   to do slow reboots or vmloads.
+   Cons: Not officially supported by libfuzzer. Does not work well for devices
+   that rely on dedicated threads.
+   Example target: virtio-net-fork-fuzz
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external
  2020-01-29  5:34 ` [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external Bulekov, Alexander
@ 2020-01-29 10:03   ` Paolo Bonzini
  2020-02-05 13:34   ` Darren Kenny
  1 sibling, 0 replies; 55+ messages in thread
From: Paolo Bonzini @ 2020-01-29 10:03 UTC (permalink / raw)
  To: Bulekov, Alexander, qemu-devel
  Cc: Laurent Vivier, bsd, Philippe Mathieu-Daudé, Thomas Huth, stefanha

On 29/01/20 06:34, Bulekov, Alexander wrote:
> The moved functions are not specific to qos-test and might be useful
> elsewhere. For example the virtual-device fuzzer makes use of them for
> qos-assisted fuzz-targets.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
>  tests/qtest/Makefile.include      |   1 +
>  tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++++++++++++
>  tests/qtest/libqos/qos_external.h |  28 +++++
>  tests/qtest/qos-test.c            | 132 +----------------------
>  4 files changed, 198 insertions(+), 131 deletions(-)
>  create mode 100644 tests/qtest/libqos/qos_external.c
>  create mode 100644 tests/qtest/libqos/qos_external.h
> 
> diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
> index 08a48c1252..bdc93d3866 100644
> --- a/tests/qtest/Makefile.include
> +++ b/tests/qtest/Makefile.include
> @@ -172,6 +172,7 @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/u
>  # qos devices:
>  libqos-obj-y =  $(libqgraph-obj-y)
>  libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
> +libqos-obj-y += tests/qtest/libqos/qos_external.o
>  libqos-obj-y += tests/qtest/libqos/e1000e.o
>  libqos-obj-y += tests/qtest/libqos/i2c.o
>  libqos-obj-y += tests/qtest/libqos/i2c-imx.o
> diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c
> new file mode 100644
> index 0000000000..398556dde0
> --- /dev/null
> +++ b/tests/qtest/libqos/qos_external.c
> @@ -0,0 +1,168 @@
> +/*
> + * libqos driver framework
> + *
> + * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License version 2 as published by the Free Software Foundation.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>
> + */
> +
> +#include "qemu/osdep.h"
> +#include <getopt.h>
> +#include "libqtest.h"
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qbool.h"
> +#include "qapi/qmp/qstring.h"
> +#include "qemu/module.h"
> +#include "qapi/qmp/qlist.h"
> +#include "libqos/malloc.h"
> +#include "libqos/qgraph.h"
> +#include "libqos/qgraph_internal.h"
> +#include "libqos/qos_external.h"
> +
> +
> +
> +void apply_to_node(const char *name, bool is_machine, bool is_abstract)
> +{
> +    char *machine_name = NULL;
> +    if (is_machine) {
> +        const char *arch = qtest_get_arch();
> +        machine_name = g_strconcat(arch, "/", name, NULL);
> +        name = machine_name;
> +    }
> +    qos_graph_node_set_availability(name, true);
> +    if (is_abstract) {
> +        qos_delete_cmd_line(name);
> +    }
> +    g_free(machine_name);
> +}

This can be static.

> +
> +/**
> + * apply_to_qlist(): using QMP queries QEMU for a list of
> + * machines and devices available, and sets the respective node
> + * as true. If a node is found, also all its produced and contained
> + * child are marked available.
> + *
> + * See qos_graph_node_set_availability() for more info
> + */
> +void apply_to_qlist(QList *list, bool is_machine)

I think this could be split in two functions with a nicer API, for example

void mark_machines_available(QList *list);
void mark_devices_available(QList *list);

It's okay if internally they just call apply_to_qlist(list, true/false),

only the API would be nicer.
Also feel free to move it to tests/libqos/qgraph.c and
tests/libqos/qgraph_internal.h.  It's probably a good idea to rename
qgraph.h to qgraph-object.h, and qgraph_internal.h to qgraph.h while at it.

Paolo


> +{
> +    const QListEntry *p;
> +    const char *name;
> +    bool abstract;
> +    QDict *minfo;
> +    QObject *qobj;
> +    QString *qstr;
> +    QBool *qbool;
> +
> +    for (p = qlist_first(list); p; p = qlist_next(p)) {
> +        minfo = qobject_to(QDict, qlist_entry_obj(p));
> +        qobj = qdict_get(minfo, "name");
> +        qstr = qobject_to(QString, qobj);
> +        name = qstring_get_str(qstr);
> +
> +        qobj = qdict_get(minfo, "abstract");
> +        if (qobj) {
> +            qbool = qobject_to(QBool, qobj);
> +            abstract = qbool_get_bool(qbool);
> +        } else {
> +            abstract = false;
> +        }
> +
> +        apply_to_node(name, is_machine, abstract);
> +        qobj = qdict_get(minfo, "alias");
> +        if (qobj) {
> +            qstr = qobject_to(QString, qobj);
> +            name = qstring_get_str(qstr);
> +            apply_to_node(name, is_machine, abstract);
> +        }
> +    }
> +}
> +
> +QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
> +{
> +    return obj->get_driver(obj, "memory");
> +}
> +
> +/**
> + * allocate_objects(): given an array of nodes @arg,
> + * walks the path invoking all constructors and
> + * passing the corresponding parameter in order to
> + * continue the objects allocation.
> + * Once the test is reached, return the object it consumes.
> + *
> + * Since the machine and QEDGE_CONSUMED_BY nodes allocate
> + * memory in the constructor, g_test_queue_destroy is used so
> + * that after execution they can be safely free'd.  (The test's
> + * ->before callback is also welcome to use g_test_queue_destroy).
> + *
> + * Note: as specified in walk_path() too, @arg is an array of
> + * char *, where arg[0] is a pointer to the command line
> + * string that will be used to properly start QEMU when executing
> + * the test, and the remaining elements represent the actual objects
> + * that will be allocated.
> + */
> +void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
> +{
> +    int current = 0;
> +    QGuestAllocator *alloc;
> +    QOSGraphObject *parent = NULL;
> +    QOSGraphEdge *edge;
> +    QOSGraphNode *node;
> +    void *edge_arg;
> +    void *obj;
> +
> +    node = qos_graph_get_node(path[current]);
> +    g_assert(node->type == QNODE_MACHINE);
> +
> +    obj = qos_machine_new(node, qts);
> +    qos_object_queue_destroy(obj);
> +
> +    alloc = get_machine_allocator(obj);
> +    if (p_alloc) {
> +        *p_alloc = alloc;
> +    }
> +
> +    for (;;) {
> +        if (node->type != QNODE_INTERFACE) {
> +            qos_object_start_hw(obj);
> +            parent = obj;
> +        }
> +
> +        /* follow edge and get object for next node constructor */
> +        current++;
> +        edge = qos_graph_get_edge(path[current - 1], path[current]);
> +        node = qos_graph_get_node(path[current]);
> +
> +        if (node->type == QNODE_TEST) {
> +            g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
> +            return obj;
> +        }
> +
> +        switch (qos_graph_edge_get_type(edge)) {
> +        case QEDGE_PRODUCES:
> +            obj = parent->get_driver(parent, path[current]);
> +            break;
> +
> +        case QEDGE_CONSUMED_BY:
> +            edge_arg = qos_graph_edge_get_arg(edge);
> +            obj = qos_driver_new(node, obj, alloc, edge_arg);
> +            qos_object_queue_destroy(obj);
> +            break;
> +
> +        case QEDGE_CONTAINS:
> +            obj = parent->get_device(parent, path[current]);
> +            break;
> +        }
> +    }
> +}
> +
> diff --git a/tests/qtest/libqos/qos_external.h b/tests/qtest/libqos/qos_external.h
> new file mode 100644
> index 0000000000..7b44930c55
> --- /dev/null
> +++ b/tests/qtest/libqos/qos_external.h
> @@ -0,0 +1,28 @@
> +/*
> + * libqos driver framework
> + *
> + * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License version 2 as published by the Free Software Foundation.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>
> + */
> +
> +#ifndef QOS_EXTERNAL_H
> +#define QOS_EXTERNAL_H
> +#include "libqos/qgraph.h"
> +
> +void apply_to_node(const char *name, bool is_machine, bool is_abstract);
> +void apply_to_qlist(QList *list, bool is_machine);
> +QGuestAllocator *get_machine_allocator(QOSGraphObject *obj);
> +void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc);
> +
> +#endif
> diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c
> index fd70d73ea5..ad193f43a5 100644
> --- a/tests/qtest/qos-test.c
> +++ b/tests/qtest/qos-test.c
> @@ -27,65 +27,11 @@
>  #include "libqos/malloc.h"
>  #include "libqos/qgraph.h"
>  #include "libqos/qgraph_internal.h"
> +#include "libqos/qos_external.h"
>  
>  static char *old_path;
>  
> -static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
> -{
> -    char *machine_name = NULL;
> -    if (is_machine) {
> -        const char *arch = qtest_get_arch();
> -        machine_name = g_strconcat(arch, "/", name, NULL);
> -        name = machine_name;
> -    }
> -    qos_graph_node_set_availability(name, true);
> -    if (is_abstract) {
> -        qos_delete_cmd_line(name);
> -    }
> -    g_free(machine_name);
> -}
>  
> -/**
> - * apply_to_qlist(): using QMP queries QEMU for a list of
> - * machines and devices available, and sets the respective node
> - * as true. If a node is found, also all its produced and contained
> - * child are marked available.
> - *
> - * See qos_graph_node_set_availability() for more info
> - */
> -static void apply_to_qlist(QList *list, bool is_machine)
> -{
> -    const QListEntry *p;
> -    const char *name;
> -    bool abstract;
> -    QDict *minfo;
> -    QObject *qobj;
> -    QString *qstr;
> -    QBool *qbool;
> -
> -    for (p = qlist_first(list); p; p = qlist_next(p)) {
> -        minfo = qobject_to(QDict, qlist_entry_obj(p));
> -        qobj = qdict_get(minfo, "name");
> -        qstr = qobject_to(QString, qobj);
> -        name = qstring_get_str(qstr);
> -
> -        qobj = qdict_get(minfo, "abstract");
> -        if (qobj) {
> -            qbool = qobject_to(QBool, qobj);
> -            abstract = qbool_get_bool(qbool);
> -        } else {
> -            abstract = false;
> -        }
> -
> -        apply_to_node(name, is_machine, abstract);
> -        qobj = qdict_get(minfo, "alias");
> -        if (qobj) {
> -            qstr = qobject_to(QString, qobj);
> -            name = qstring_get_str(qstr);
> -            apply_to_node(name, is_machine, abstract);
> -        }
> -    }
> -}
>  
>  /**
>   * qos_set_machines_devices_available(): sets availability of qgraph
> @@ -129,10 +75,6 @@ static void qos_set_machines_devices_available(void)
>      qobject_unref(response);
>  }
>  
> -static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
> -{
> -    return obj->get_driver(obj, "memory");
> -}
>  
>  static void restart_qemu_or_continue(char *path)
>  {
> @@ -159,78 +101,6 @@ void qos_invalidate_command_line(void)
>      old_path = NULL;
>  }
>  
> -/**
> - * allocate_objects(): given an array of nodes @arg,
> - * walks the path invoking all constructors and
> - * passing the corresponding parameter in order to
> - * continue the objects allocation.
> - * Once the test is reached, return the object it consumes.
> - *
> - * Since the machine and QEDGE_CONSUMED_BY nodes allocate
> - * memory in the constructor, g_test_queue_destroy is used so
> - * that after execution they can be safely free'd.  (The test's
> - * ->before callback is also welcome to use g_test_queue_destroy).
> - *
> - * Note: as specified in walk_path() too, @arg is an array of
> - * char *, where arg[0] is a pointer to the command line
> - * string that will be used to properly start QEMU when executing
> - * the test, and the remaining elements represent the actual objects
> - * that will be allocated.
> - */
> -static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
> -{
> -    int current = 0;
> -    QGuestAllocator *alloc;
> -    QOSGraphObject *parent = NULL;
> -    QOSGraphEdge *edge;
> -    QOSGraphNode *node;
> -    void *edge_arg;
> -    void *obj;
> -
> -    node = qos_graph_get_node(path[current]);
> -    g_assert(node->type == QNODE_MACHINE);
> -
> -    obj = qos_machine_new(node, qts);
> -    qos_object_queue_destroy(obj);
> -
> -    alloc = get_machine_allocator(obj);
> -    if (p_alloc) {
> -        *p_alloc = alloc;
> -    }
> -
> -    for (;;) {
> -        if (node->type != QNODE_INTERFACE) {
> -            qos_object_start_hw(obj);
> -            parent = obj;
> -        }
> -
> -        /* follow edge and get object for next node constructor */
> -        current++;
> -        edge = qos_graph_get_edge(path[current - 1], path[current]);
> -        node = qos_graph_get_node(path[current]);
> -
> -        if (node->type == QNODE_TEST) {
> -            g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
> -            return obj;
> -        }
> -
> -        switch (qos_graph_edge_get_type(edge)) {
> -        case QEDGE_PRODUCES:
> -            obj = parent->get_driver(parent, path[current]);
> -            break;
> -
> -        case QEDGE_CONSUMED_BY:
> -            edge_arg = qos_graph_edge_get_arg(edge);
> -            obj = qos_driver_new(node, obj, alloc, edge_arg);
> -            qos_object_queue_destroy(obj);
> -            break;
> -
> -        case QEDGE_CONTAINS:
> -            obj = parent->get_device(parent, path[current]);
> -            break;
> -        }
> -    }
> -}
>  
>  /* The argument to run_one_test, which is the test function that is registered
>   * with GTest, is a vector of strings.  The first item is the initial command
> 



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c
  2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
@ 2020-01-30 14:39   ` Stefan Hajnoczi
  2020-01-30 15:06   ` Alex Bennée
  2020-02-05 14:02   ` Darren Kenny
  2 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2020-01-30 14:39 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1045 bytes --]

On Wed, Jan 29, 2020 at 05:34:11AM +0000, Bulekov, Alexander wrote:
> A program might rely on functions implemented in vl.c, but implement its
> own main(). By placing main into a separate source file, there are no
> complaints about duplicate main()s when linking against vl.o. For
> example, the virtual-device fuzzer uses a main() provided by libfuzzer,
> and needs to perform some initialization before running the softmmu
> initialization. Now, main simply calls three vl.c functions which
> handle the guest initialization, main loop and cleanup.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
>  Makefile                |  1 +
>  Makefile.objs           |  2 ++
>  Makefile.target         |  2 +-
>  include/sysemu/sysemu.h |  4 ++++
>  main.c                  | 53 +++++++++++++++++++++++++++++++++++++++++
>  vl.c                    | 36 +++++++---------------------
>  6 files changed, 70 insertions(+), 28 deletions(-)
>  create mode 100644 main.c

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-29  5:34 ` [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest Bulekov, Alexander
@ 2020-01-30 14:42   ` Stefan Hajnoczi
  2020-01-30 17:24     ` Paolo Bonzini
  2020-02-05 13:58   ` Darren Kenny
  2020-06-18  7:34   ` Thomas Huth
  2 siblings, 1 reply; 55+ messages in thread
From: Stefan Hajnoczi @ 2020-01-30 14:42 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1152 bytes --]

On Wed, Jan 29, 2020 at 05:34:22AM +0000, Bulekov, Alexander wrote:
> The qtest-based fuzzer makes use of forking to reset-state between
> tests. Keep the callback enabled, so the call_rcu thread gets created
> within the child process.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
>  vl.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/vl.c b/vl.c
> index bb77935f04..cf8e2d3ebb 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
>      set_memory_options(&ram_slots, &maxram_size, machine_class);
>  
>      os_daemonize();
> -    rcu_disable_atfork();
> +
> +    /*
> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> +     */
> +    if (!qtest_enabled()) {
> +        rcu_disable_atfork();
> +    }

I haven't reviewed the details of whether resources are leaked across
fork but in general it makes sense that we want an RCU thread in the
fork child:

Acked-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 19/21] fuzz: add virtio-net fuzz target
  2020-01-29  5:34 ` [PATCH v8 19/21] fuzz: add virtio-net fuzz target Bulekov, Alexander
@ 2020-01-30 14:43   ` Stefan Hajnoczi
  2020-02-05 13:57   ` Darren Kenny
  1 sibling, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2020-01-30 14:43 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: Thomas Huth, pbonzini, bsd, qemu-devel, Laurent Vivier

[-- Attachment #1: Type: text/plain, Size: 546 bytes --]

On Wed, Jan 29, 2020 at 05:34:27AM +0000, Bulekov, Alexander wrote:
> The virtio-net fuzz target feeds inputs to all three virtio-net
> virtqueues, and uses forking to avoid leaking state between fuzz runs.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
>  tests/qtest/fuzz/Makefile.include  |   1 +
>  tests/qtest/fuzz/virtio_net_fuzz.c | 195 +++++++++++++++++++++++++++++
>  2 files changed, 196 insertions(+)
>  create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 00/21] Add virtual device fuzzing support
  2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
                   ` (20 preceding siblings ...)
  2020-01-29  5:34 ` [PATCH v8 21/21] fuzz: add documentation to docs/devel/ Bulekov, Alexander
@ 2020-01-30 14:44 ` Stefan Hajnoczi
  21 siblings, 0 replies; 55+ messages in thread
From: Stefan Hajnoczi @ 2020-01-30 14:44 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, darren.kenny

[-- Attachment #1: Type: text/plain, Size: 511 bytes --]

On Wed, Jan 29, 2020 at 05:34:11AM +0000, Bulekov, Alexander wrote:
> This series adds a framework for coverage-guided fuzzing of
> virtual-devices. Fuzzing targets are based on qtest and can make use of
> the libqos abstractions.
> 
> V8:
>  * Small fixes to the virtio-net.
>  * Keep rcu_atfork when not using qtest.

I'm happy with this series.  No doubt there will be changes but I think
it's time to get this merged so others can more easily participate in
the device fuzzing effort.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c
  2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
  2020-01-30 14:39   ` Stefan Hajnoczi
@ 2020-01-30 15:06   ` Alex Bennée
  2020-01-30 17:44     ` Alexander Bulekov
  2020-02-05 14:02   ` Darren Kenny
  2 siblings, 1 reply; 55+ messages in thread
From: Alex Bennée @ 2020-01-30 15:06 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, stefanha


Bulekov, Alexander <alxndr@bu.edu> writes:

> A program might rely on functions implemented in vl.c, but implement its
> own main(). By placing main into a separate source file, there are no
> complaints about duplicate main()s when linking against vl.o. For
> example, the virtual-device fuzzer uses a main() provided by libfuzzer,
> and needs to perform some initialization before running the softmmu
> initialization. Now, main simply calls three vl.c functions which
> handle the guest initialization, main loop and cleanup.
>
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
<snip>
>  main.c                  | 53 +++++++++++++++++++++++++++++++++++++++++
<snip>
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -84,6 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
>  # qapi
>  
>  common-obj-y += qapi/
> +
> +softmmu-obj-y = main.o
>  endif
>  
<snip>
> diff --git a/main.c b/main.c
> new file mode 100644
> index 0000000000..f10ceda541
> --- /dev/null
> +++ b/main.c
> @@ -0,0 +1,53 @@
> +/*
> + * QEMU System Emulator
> + *
> + * Copyright (c) 2003-2008 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "sysemu/sysemu.h"
> +
> +#ifdef CONFIG_SDL
> +#if defined(__APPLE__) || defined(main)
> +#include <SDL.h>
> +int main(int argc, char **argv)
> +{
> +    return qemu_main(argc, argv, NULL);
> +}
> +#undef main
> +#define main qemu_main
> +#endif
> +#endif /* CONFIG_SDL */
> +
> +#ifdef CONFIG_COCOA
> +#undef main
> +#define main qemu_main
> +#endif /* CONFIG_COCOA */
> +
> +int main(int argc, char **argv, char **envp)
> +{
> +    qemu_init(argc, argv, envp);
> +    qemu_main_loop();
> +    qemu_cleanup();
> +
> +    return 0;
> +}
<snip>

Can we put the main in a project appropriate sub-directory so it's on
the same order as linux-user/main.c?

I guess the new directory could be "softmmu" which matches the directory
or "system" which matches the binary name. I'd lean towards the latter
as softmmu is very specifically not this bit.

-- 
Alex Bennée


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-30 14:42   ` Stefan Hajnoczi
@ 2020-01-30 17:24     ` Paolo Bonzini
  2020-01-30 17:42       ` Alexander Bulekov
  0 siblings, 1 reply; 55+ messages in thread
From: Paolo Bonzini @ 2020-01-30 17:24 UTC (permalink / raw)
  To: Stefan Hajnoczi, Bulekov, Alexander; +Cc: bsd, qemu-devel


[-- Attachment #1.1: Type: text/plain, Size: 1440 bytes --]

On 30/01/20 15:42, Stefan Hajnoczi wrote:
>> +
>> +    /*
>> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
>> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
>> +     */
>> +    if (!qtest_enabled()) {
>> +        rcu_disable_atfork();
>> +    }
> I haven't reviewed the details of whether resources are leaked across
> fork but in general it makes sense that we want an RCU thread in the
> fork child:

Note that there is a possible deadlock between fork and synchronize_rcu
(see commit 73c6e40, "rcu: completely disable pthread_atfork callbacks
as soon as possible", 2016-01-27):

- the CPU thread is inside a RCU critical section and wants to take the
BQL in order to do MMIO

- the I/O thread, which is owning the BQL, forks and calls
rcu_init_lock, which tries to take the rcu_sync_lock

- the call_rcu thread has taken rcu_sync_lock in synchronize_rcu, but
synchronize_rcu needs the CPU thread to end the critical section before
returning.

Therefore it would be best if the fork server could fork before a single
CPU instruction is executed, and then rcu_disable_atfork could be moved
right after the fork server is started (just like right now we do it
right after os_daemonize).  We probably talked about this before, but
how do you ensure that the fork server is started before threads are
created (apart from the RCU thread)?

Paolo


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-30 17:24     ` Paolo Bonzini
@ 2020-01-30 17:42       ` Alexander Bulekov
  2020-01-30 18:14         ` Paolo Bonzini
  0 siblings, 1 reply; 55+ messages in thread
From: Alexander Bulekov @ 2020-01-30 17:42 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: bsd, qemu-devel, Stefan Hajnoczi

On 200130 1824, Paolo Bonzini wrote:
> On 30/01/20 15:42, Stefan Hajnoczi wrote:
> >> +
> >> +    /*
> >> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> >> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> >> +     */
> >> +    if (!qtest_enabled()) {
> >> +        rcu_disable_atfork();
> >> +    }
> > I haven't reviewed the details of whether resources are leaked across
> > fork but in general it makes sense that we want an RCU thread in the
> > fork child:
> 
> Note that there is a possible deadlock between fork and synchronize_rcu
> (see commit 73c6e40, "rcu: completely disable pthread_atfork callbacks
> as soon as possible", 2016-01-27):
> 
> - the CPU thread is inside a RCU critical section and wants to take the
> BQL in order to do MMIO
> 
> - the I/O thread, which is owning the BQL, forks and calls
> rcu_init_lock, which tries to take the rcu_sync_lock
> 
> - the call_rcu thread has taken rcu_sync_lock in synchronize_rcu, but
> synchronize_rcu needs the CPU thread to end the critical section before
> returning.
> 
> Therefore it would be best if the fork server could fork before a single
> CPU instruction is executed, and then rcu_disable_atfork could be moved
> right after the fork server is started (just like right now we do it
> right after os_daemonize).  We probably talked about this before, but
> how do you ensure that the fork server is started before threads are
> created (apart from the RCU thread)?

With QTest, is this still a concern, since there are no CPU instructions
involved? Sometimes the fork-server starts after some I/O has already
occured (eg mapping BARs and setting up VQs for virtio-net). I know we
briefly talked about threads at some point, and it seems that iothreads
may be a concern, if any are started before fork. Other than that, since
there is no TCG/CPU thread, are there any other threads that could be
a concern?
-Alex

> Paolo
> 





^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c
  2020-01-30 15:06   ` Alex Bennée
@ 2020-01-30 17:44     ` Alexander Bulekov
  2020-01-30 18:41       ` Alex Bennée
  0 siblings, 1 reply; 55+ messages in thread
From: Alexander Bulekov @ 2020-01-30 17:44 UTC (permalink / raw)
  To: Alex Bennée; +Cc: pbonzini, bsd, qemu-devel, stefanha

On 200130 1506, Alex Bennée wrote:
> 
> Bulekov, Alexander <alxndr@bu.edu> writes:
> 
> > A program might rely on functions implemented in vl.c, but implement its
> > own main(). By placing main into a separate source file, there are no
> > complaints about duplicate main()s when linking against vl.o. For
> > example, the virtual-device fuzzer uses a main() provided by libfuzzer,
> > and needs to perform some initialization before running the softmmu
> > initialization. Now, main simply calls three vl.c functions which
> > handle the guest initialization, main loop and cleanup.
> >
> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> > ---
> <snip>
> >  main.c                  | 53 +++++++++++++++++++++++++++++++++++++++++
> <snip>
> > --- a/Makefile.objs
> > +++ b/Makefile.objs
> > @@ -84,6 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
> >  # qapi
> >  
> >  common-obj-y += qapi/
> > +
> > +softmmu-obj-y = main.o
> >  endif
> >  
> <snip>
> > diff --git a/main.c b/main.c
> > new file mode 100644
> > index 0000000000..f10ceda541
> > --- /dev/null
> > +++ b/main.c
> > @@ -0,0 +1,53 @@
> > +/*
> > + * QEMU System Emulator
> > + *
> > + * Copyright (c) 2003-2008 Fabrice Bellard
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a copy
> > + * of this software and associated documentation files (the "Software"), to deal
> > + * in the Software without restriction, including without limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> > + * copies of the Software, and to permit persons to whom the Software is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> > + * THE SOFTWARE.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu-common.h"
> > +#include "sysemu/sysemu.h"
> > +
> > +#ifdef CONFIG_SDL
> > +#if defined(__APPLE__) || defined(main)
> > +#include <SDL.h>
> > +int main(int argc, char **argv)
> > +{
> > +    return qemu_main(argc, argv, NULL);
> > +}
> > +#undef main
> > +#define main qemu_main
> > +#endif
> > +#endif /* CONFIG_SDL */
> > +
> > +#ifdef CONFIG_COCOA
> > +#undef main
> > +#define main qemu_main
> > +#endif /* CONFIG_COCOA */
> > +
> > +int main(int argc, char **argv, char **envp)
> > +{
> > +    qemu_init(argc, argv, envp);
> > +    qemu_main_loop();
> > +    qemu_cleanup();
> > +
> > +    return 0;
> > +}
> <snip>
> 
> Can we put the main in a project appropriate sub-directory so it's on
> the same order as linux-user/main.c?
> 
> I guess the new directory could be "softmmu" which matches the directory
> or "system" which matches the binary name. I'd lean towards the latter
> as softmmu is very specifically not this bit.
Will do - should vl.c move into this directory, as well?
-Alex

> -- 
> Alex Bennée


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-30 17:42       ` Alexander Bulekov
@ 2020-01-30 18:14         ` Paolo Bonzini
  0 siblings, 0 replies; 55+ messages in thread
From: Paolo Bonzini @ 2020-01-30 18:14 UTC (permalink / raw)
  To: Alexander Bulekov; +Cc: bsd, qemu-devel, Stefan Hajnoczi

On 30/01/20 18:42, Alexander Bulekov wrote:
> With QTest, is this still a concern, since there are no CPU instructions
> involved? Sometimes the fork-server starts after some I/O has already
> occured (eg mapping BARs and setting up VQs for virtio-net). I know we
> briefly talked about threads at some point, and it seems that iothreads
> may be a concern, if any are started before fork. Other than that, since
> there is no TCG/CPU thread, are there any other threads that could be
> a concern?
> -Alex

There is a CPU thread, it just does not do MMIO.  However, it may still
execute code via run_on_cpu.  It's quite unlikely to have the deadlock,
but if it were possible to force an early start of the fork server (at
the point of os_daemonize() would be ideal) it would be cleaner and it
would allow reverting this patch.

This is not a NACK, just some extra info.

Paolo



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c
  2020-01-30 17:44     ` Alexander Bulekov
@ 2020-01-30 18:41       ` Alex Bennée
  0 siblings, 0 replies; 55+ messages in thread
From: Alex Bennée @ 2020-01-30 18:41 UTC (permalink / raw)
  To: Alexander Bulekov; +Cc: pbonzini, bsd, qemu-devel, stefanha


Alexander Bulekov <alxndr@bu.edu> writes:

> On 200130 1506, Alex Bennée wrote:
>> 
>> Bulekov, Alexander <alxndr@bu.edu> writes:
>> 
>> > A program might rely on functions implemented in vl.c, but implement its
>> > own main(). By placing main into a separate source file, there are no
>> > complaints about duplicate main()s when linking against vl.o. For
>> > example, the virtual-device fuzzer uses a main() provided by libfuzzer,
>> > and needs to perform some initialization before running the softmmu
>> > initialization. Now, main simply calls three vl.c functions which
>> > handle the guest initialization, main loop and cleanup.
>> >
>> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>> > ---
>> <snip>
>> >  main.c                  | 53 +++++++++++++++++++++++++++++++++++++++++
>> <snip>
>> > --- a/Makefile.objs
>> > +++ b/Makefile.objs
>> > @@ -84,6 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
>> >  # qapi
>> >  
>> >  common-obj-y += qapi/
>> > +
>> > +softmmu-obj-y = main.o
>> >  endif
>> >  
>> <snip>
>> > diff --git a/main.c b/main.c
>> > new file mode 100644
>> > index 0000000000..f10ceda541
>> > --- /dev/null
>> > +++ b/main.c
>> > @@ -0,0 +1,53 @@
>> > +/*
>> > + * QEMU System Emulator
>> > + *
>> > + * Copyright (c) 2003-2008 Fabrice Bellard
>> > + *
>> > + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> > + * of this software and associated documentation files (the "Software"), to deal
>> > + * in the Software without restriction, including without limitation the rights
>> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> > + * copies of the Software, and to permit persons to whom the Software is
>> > + * furnished to do so, subject to the following conditions:
>> > + *
>> > + * The above copyright notice and this permission notice shall be included in
>> > + * all copies or substantial portions of the Software.
>> > + *
>> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> > + * THE SOFTWARE.
>> > + */
>> > +
>> > +#include "qemu/osdep.h"
>> > +#include "qemu-common.h"
>> > +#include "sysemu/sysemu.h"
>> > +
>> > +#ifdef CONFIG_SDL
>> > +#if defined(__APPLE__) || defined(main)
>> > +#include <SDL.h>
>> > +int main(int argc, char **argv)
>> > +{
>> > +    return qemu_main(argc, argv, NULL);
>> > +}
>> > +#undef main
>> > +#define main qemu_main
>> > +#endif
>> > +#endif /* CONFIG_SDL */
>> > +
>> > +#ifdef CONFIG_COCOA
>> > +#undef main
>> > +#define main qemu_main
>> > +#endif /* CONFIG_COCOA */
>> > +
>> > +int main(int argc, char **argv, char **envp)
>> > +{
>> > +    qemu_init(argc, argv, envp);
>> > +    qemu_main_loop();
>> > +    qemu_cleanup();
>> > +
>> > +    return 0;
>> > +}
>> <snip>
>> 
>> Can we put the main in a project appropriate sub-directory so it's on
>> the same order as linux-user/main.c?
>> 
>> I guess the new directory could be "softmmu" which matches the directory
>> or "system" which matches the binary name. I'd lean towards the latter
>> as softmmu is very specifically not this bit.
> Will do - should vl.c move into this directory, as well?
> -Alex

Might as well so it doesn't get lonely ;-)

There has been a slow process of moving bits and pieces of the root
directory into a respective sub-directories over the years (decades?) so
this is a good opportunity to clean this bit up.

>
>> -- 
>> Alex Bennée


-- 
Alex Bennée


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 03/21] fuzz: add FUZZ_TARGET module type
  2020-01-29  5:34 ` [PATCH v8 03/21] fuzz: add FUZZ_TARGET module type Bulekov, Alexander
@ 2020-02-05 11:28   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:28 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, stefanha

On Wed, Jan 29, 2020 at 05:34:13AM +0000, Bulekov, Alexander wrote:
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> include/qemu/module.h | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
>diff --git a/include/qemu/module.h b/include/qemu/module.h
>index 65ba596e46..684753d808 100644
>--- a/include/qemu/module.h
>+++ b/include/qemu/module.h
>@@ -46,6 +46,7 @@ typedef enum {
>     MODULE_INIT_TRACE,
>     MODULE_INIT_XEN_BACKEND,
>     MODULE_INIT_LIBQOS,
>+    MODULE_INIT_FUZZ_TARGET,
>     MODULE_INIT_MAX
> } module_init_type;
>
>@@ -56,7 +57,8 @@ typedef enum {
> #define xen_backend_init(function) module_init(function, \
>                                                MODULE_INIT_XEN_BACKEND)
> #define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
>-
>+#define fuzz_target_init(function) module_init(function, \
>+                                               MODULE_INIT_FUZZ_TARGET)
> #define block_module_load_one(lib) module_load_one("block-", lib)
> #define ui_module_load_one(lib) module_load_one("ui-", lib)
> #define audio_module_load_one(lib) module_load_one("audio-", lib)
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 04/21] qtest: add qtest_server_send abstraction
  2020-01-29  5:34 ` [PATCH v8 04/21] qtest: add qtest_server_send abstraction Bulekov, Alexander
@ 2020-02-05 11:30   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:30 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:14AM +0000, Bulekov, Alexander wrote:
>qtest_server_send is a function pointer specifying the handler used to
>transmit data to the qtest client. In the standard configuration, this
>calls the CharBackend handler, but now it is possible for other types of
>handlers, e.g direct-function calls if the qtest client and server
>exist within the same process (inproc)
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>Acked-by: Thomas Huth <thuth@redhat.com>
>---
> include/sysemu/qtest.h |  3 +++
> qtest.c                | 18 ++++++++++++++++--
> 2 files changed, 19 insertions(+), 2 deletions(-)
>
>diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
>index 5ed09c80b1..e2f1047fd7 100644
>--- a/include/sysemu/qtest.h
>+++ b/include/sysemu/qtest.h
>@@ -26,4 +26,7 @@ bool qtest_driver(void);
>
> void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
>
>+void qtest_server_set_send_handler(void (*send)(void *, const char *),
>+                                 void *opaque);
>+
> #endif
>diff --git a/qtest.c b/qtest.c
>index 12432f99cf..938c3746d6 100644
>--- a/qtest.c
>+++ b/qtest.c
>@@ -42,6 +42,8 @@ static GString *inbuf;
> static int irq_levels[MAX_IRQ];
> static qemu_timeval start_time;
> static bool qtest_opened;
>+static void (*qtest_server_send)(void*, const char*);
>+static void *qtest_server_send_opaque;
>
> #define FMT_timeval "%ld.%06ld"
>
>@@ -228,8 +230,10 @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...)
>     va_end(ap);
> }
>
>-static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
>+static void qtest_server_char_be_send(void *opaque, const char *str)
> {
>+    size_t len = strlen(str);
>+    CharBackend* chr = (CharBackend *)opaque;
>     qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
>     if (qtest_log_fp && qtest_opened) {
>         fprintf(qtest_log_fp, "%s", str);
>@@ -238,7 +242,7 @@ static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
>
> static void qtest_send(CharBackend *chr, const char *str)
> {
>-    do_qtest_send(chr, str, strlen(str));
>+    qtest_server_send(qtest_server_send_opaque, str);
> }
>
> static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
>@@ -783,6 +787,16 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
>     qemu_chr_fe_set_echo(&qtest_chr, true);
>
>     inbuf = g_string_new("");
>+
>+    if (!qtest_server_send) {
>+        qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
>+    }
>+}
>+
>+void qtest_server_set_send_handler(void (*send)(void*, const char*), void *opaque)
>+{
>+    qtest_server_send = send;
>+    qtest_server_send_opaque = opaque;
> }
>
> bool qtest_driver(void)
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 05/21] libqtest: add a layer of abstraction to send/recv
  2020-01-29  5:34 ` [PATCH v8 05/21] libqtest: add a layer of abstraction to send/recv Bulekov, Alexander
@ 2020-02-05 11:32   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:32 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:15AM +0000, Bulekov, Alexander wrote:
>This makes it simple to swap the transport functions for qtest commands
>to and from the qtest client. For example, now it is possible to
>directly pass qtest commands to a server handler that exists within the
>same process, without the standard way of writing to a file descriptor.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
>---
> tests/qtest/libqtest.c | 48 ++++++++++++++++++++++++++++++++++--------
> 1 file changed, 39 insertions(+), 9 deletions(-)
>
>diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
>index 76c9f8eade..e5056a1d0f 100644
>--- a/tests/qtest/libqtest.c
>+++ b/tests/qtest/libqtest.c
>@@ -35,6 +35,15 @@
> #define SOCKET_TIMEOUT 50
> #define SOCKET_MAX_FDS 16
>
>+
>+typedef void (*QTestSendFn)(QTestState *s, const char *buf);
>+typedef GString* (*QTestRecvFn)(QTestState *);
>+
>+typedef struct QTestClientTransportOps {
>+    QTestSendFn     send;      /* for sending qtest commands */
>+    QTestRecvFn     recv_line; /* for receiving qtest command responses */
>+} QTestTransportOps;
>+
> struct QTestState
> {
>     int fd;
>@@ -45,6 +54,7 @@ struct QTestState
>     bool big_endian;
>     bool irq_level[MAX_IRQ];
>     GString *rx;
>+    QTestTransportOps ops;
> };
>
> static GHookList abrt_hooks;
>@@ -52,6 +62,14 @@ static struct sigaction sigact_old;
>
> static int qtest_query_target_endianness(QTestState *s);
>
>+static void qtest_client_socket_send(QTestState*, const char *buf);
>+static void socket_send(int fd, const char *buf, size_t size);
>+
>+static GString *qtest_client_socket_recv_line(QTestState *);
>+
>+static void qtest_client_set_tx_handler(QTestState *s, QTestSendFn send);
>+static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv);
>+
> static int init_socket(const char *socket_path)
> {
>     struct sockaddr_un addr;
>@@ -234,6 +252,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
>     sock = init_socket(socket_path);
>     qmpsock = init_socket(qmp_socket_path);
>
>+    qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
>+    qtest_client_set_tx_handler(s, qtest_client_socket_send);
>+
>     qtest_add_abrt_handler(kill_qemu_hook_func, s);
>
>     command = g_strdup_printf("exec %s "
>@@ -379,13 +400,9 @@ static void socket_send(int fd, const char *buf, size_t size)
>     }
> }
>
>-static void socket_sendf(int fd, const char *fmt, va_list ap)
>+static void qtest_client_socket_send(QTestState *s, const char *buf)
> {
>-    gchar *str = g_strdup_vprintf(fmt, ap);
>-    size_t size = strlen(str);
>-
>-    socket_send(fd, str, size);
>-    g_free(str);
>+    socket_send(s->fd, buf, strlen(buf));
> }
>
> static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
>@@ -393,8 +410,11 @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
>     va_list ap;
>
>     va_start(ap, fmt);
>-    socket_sendf(s->fd, fmt, ap);
>+    gchar *str = g_strdup_vprintf(fmt, ap);
>     va_end(ap);
>+
>+    s->ops.send(s, str);
>+    g_free(str);
> }
>
> /* Sends a message and file descriptors to the socket.
>@@ -431,7 +451,7 @@ static void socket_send_fds(int socket_fd, int *fds, size_t fds_num,
>     g_assert_cmpint(ret, >, 0);
> }
>
>-static GString *qtest_recv_line(QTestState *s)
>+static GString *qtest_client_socket_recv_line(QTestState *s)
> {
>     GString *line;
>     size_t offset;
>@@ -468,7 +488,7 @@ static gchar **qtest_rsp(QTestState *s, int expected_args)
>     int i;
>
> redo:
>-    line = qtest_recv_line(s);
>+    line = s->ops.recv_line(s);
>     words = g_strsplit(line->str, " ", 0);
>     g_string_free(line, TRUE);
>
>@@ -1337,3 +1357,13 @@ void qmp_assert_error_class(QDict *rsp, const char *class)
>
>     qobject_unref(rsp);
> }
>+
>+static void qtest_client_set_tx_handler(QTestState *s,
>+                    QTestSendFn send)
>+{
>+    s->ops.send = send;
>+}
>+static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
>+{
>+    s->ops.recv_line = recv;
>+}
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 06/21] libqtest: make bufwrite rely on the TransportOps
  2020-01-29  5:34 ` [PATCH v8 06/21] libqtest: make bufwrite rely on the TransportOps Bulekov, Alexander
@ 2020-02-05 11:36   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:36 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:16AM +0000, Bulekov, Alexander wrote:
>When using qtest "in-process" communication, qtest_sendf directly calls
>a function in the server (qtest.c). Previously, bufwrite used
>socket_send, which bypasses the TransportOps enabling the call into
>qtest.c. This change replaces the socket_send calls with ops->send,
>maintaining the benefits of the direct socket_send call, while adding
>support for in-process qtest calls.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> tests/qtest/libqtest.c | 71 ++++++++++++++++++++++++++++++++++++++++--
> tests/qtest/libqtest.h |  4 +++
> 2 files changed, 73 insertions(+), 2 deletions(-)
>
>diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
>index e5056a1d0f..49075b55a1 100644
>--- a/tests/qtest/libqtest.c
>+++ b/tests/qtest/libqtest.c
>@@ -37,10 +37,18 @@
>
>
> typedef void (*QTestSendFn)(QTestState *s, const char *buf);
>+typedef void (*ExternalSendFn)(void *s, const char *buf);
> typedef GString* (*QTestRecvFn)(QTestState *);
>
> typedef struct QTestClientTransportOps {
>     QTestSendFn     send;      /* for sending qtest commands */
>+
>+    /*
>+     * use external_send to send qtest command strings through functions which
>+     * do not accept a QTestState as the first parameter.
>+     */
>+    ExternalSendFn  external_send;
>+
>     QTestRecvFn     recv_line; /* for receiving qtest command responses */
> } QTestTransportOps;
>
>@@ -1078,8 +1086,8 @@ void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
>
>     bdata = g_base64_encode(data, size);
>     qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
>-    socket_send(s->fd, bdata, strlen(bdata));
>-    socket_send(s->fd, "\n", 1);
>+    s->ops.send(s, bdata);
>+    s->ops.send(s, "\n");
>     qtest_rsp(s, 0);
>     g_free(bdata);
> }
>@@ -1367,3 +1375,62 @@ static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
> {
>     s->ops.recv_line = recv;
> }
>+/* A type-safe wrapper for s->send() */
>+static void send_wrapper(QTestState *s, const char *buf)
>+{
>+    s->ops.external_send(s, buf);
>+}
>+
>+static GString *qtest_client_inproc_recv_line(QTestState *s)
>+{
>+    GString *line;
>+    size_t offset;
>+    char *eol;
>+
>+    eol = strchr(s->rx->str, '\n');
>+    offset = eol - s->rx->str;
>+    line = g_string_new_len(s->rx->str, offset);
>+    g_string_erase(s->rx, 0, offset + 1);
>+    return line;
>+}
>+
>+QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
>+                    void (*send)(void*, const char*))
>+{
>+    QTestState *qts;
>+    qts = g_new0(QTestState, 1);
>+    *s = qts; /* Expose qts early on, since the query endianness relies on it */
>+    qts->wstatus = 0;
>+    for (int i = 0; i < MAX_IRQ; i++) {
>+        qts->irq_level[i] = false;
>+    }
>+
>+    qtest_client_set_rx_handler(qts, qtest_client_inproc_recv_line);
>+
>+    /* send() may not have a matching protoype, so use a type-safe wrapper */
>+    qts->ops.external_send = send;
>+    qtest_client_set_tx_handler(qts, send_wrapper);
>+
>+    qts->big_endian = qtest_query_target_endianness(qts);
>+
>+    /*
>+     * Set a dummy path for QTEST_QEMU_BINARY. Doesn't need to exist, but this
>+     * way, qtest_get_arch works for inproc qtest.
>+     */
>+    gchar *bin_path = g_strconcat("/qemu-system-", arch, NULL);
>+    setenv("QTEST_QEMU_BINARY", bin_path, 0);
>+    g_free(bin_path);
>+
>+    return qts;
>+}
>+
>+void qtest_client_inproc_recv(void *opaque, const char *str)
>+{
>+    QTestState *qts = *(QTestState **)opaque;
>+
>+    if (!qts->rx) {
>+        qts->rx = g_string_new(NULL);
>+    }
>+    g_string_append(qts->rx, str);
>+    return;
>+}
>diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
>index c9e21e05b3..f5cf93c386 100644
>--- a/tests/qtest/libqtest.h
>+++ b/tests/qtest/libqtest.h
>@@ -729,4 +729,8 @@ bool qtest_probe_child(QTestState *s);
>  */
> void qtest_set_expected_status(QTestState *s, int status);
>
>+QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
>+                    void (*send)(void*, const char*));
>+
>+void qtest_client_inproc_recv(void *opaque, const char *str);
> #endif
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 07/21] qtest: add in-process incoming command handler
  2020-01-29  5:34 ` [PATCH v8 07/21] qtest: add in-process incoming command handler Bulekov, Alexander
@ 2020-02-05 11:39   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:39 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:17AM +0000, Bulekov, Alexander wrote:
>The handler allows a qtest client to send commands to the server by
>directly calling a function, rather than using a file/CharBackend
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> include/sysemu/qtest.h |  1 +
> qtest.c                | 13 +++++++++++++
> 2 files changed, 14 insertions(+)
>
>diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
>index e2f1047fd7..eedd3664f0 100644
>--- a/include/sysemu/qtest.h
>+++ b/include/sysemu/qtest.h
>@@ -28,5 +28,6 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
>
> void qtest_server_set_send_handler(void (*send)(void *, const char *),
>                                  void *opaque);
>+void qtest_server_inproc_recv(void *opaque, const char *buf);
>
> #endif
>diff --git a/qtest.c b/qtest.c
>index 938c3746d6..ad6eb6a526 100644
>--- a/qtest.c
>+++ b/qtest.c
>@@ -803,3 +803,16 @@ bool qtest_driver(void)
> {
>     return qtest_chr.chr != NULL;
> }
>+
>+void qtest_server_inproc_recv(void *dummy, const char *buf)
>+{
>+    static GString *gstr;
>+    if (!gstr) {
>+        gstr = g_string_new(NULL);
>+    }
>+    g_string_append(gstr, buf);
>+    if (gstr->str[gstr->len - 1] == '\n') {
>+        qtest_process_inbuf(NULL, gstr);
>+        g_string_truncate(gstr, 0);
>+    }
>+}
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 08/21] libqos: rename i2c_send and i2c_recv
  2020-01-29  5:34 ` [PATCH v8 08/21] libqos: rename i2c_send and i2c_recv Bulekov, Alexander
@ 2020-02-05 11:40   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:40 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:18AM +0000, Bulekov, Alexander wrote:
>The names i2c_send and i2c_recv collide with functions defined in
>hw/i2c/core.c. This causes an error when linking against libqos and
>softmmu simultaneously (for example when using qtest inproc). Rename the
>libqos functions to avoid this.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Acked-by: Thomas Huth <thuth@redhat.com>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> tests/qtest/libqos/i2c.c   | 10 +++++-----
> tests/qtest/libqos/i2c.h   |  4 ++--
> tests/qtest/pca9552-test.c | 10 +++++-----
> 3 files changed, 12 insertions(+), 12 deletions(-)
>
>diff --git a/tests/qtest/libqos/i2c.c b/tests/qtest/libqos/i2c.c
>index 156114e745..38f800dbab 100644
>--- a/tests/qtest/libqos/i2c.c
>+++ b/tests/qtest/libqos/i2c.c
>@@ -10,12 +10,12 @@
> #include "libqos/i2c.h"
> #include "libqtest.h"
>
>-void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
>+void qi2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
> {
>     i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
> }
>
>-void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
>+void qi2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
> {
>     i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
> }
>@@ -23,8 +23,8 @@ void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
> void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
>                     uint8_t *buf, uint16_t len)
> {
>-    i2c_send(i2cdev, &reg, 1);
>-    i2c_recv(i2cdev, buf, len);
>+    qi2c_send(i2cdev, &reg, 1);
>+    qi2c_recv(i2cdev, buf, len);
> }
>
> void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
>@@ -33,7 +33,7 @@ void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
>     uint8_t *cmd = g_malloc(len + 1);
>     cmd[0] = reg;
>     memcpy(&cmd[1], buf, len);
>-    i2c_send(i2cdev, cmd, len + 1);
>+    qi2c_send(i2cdev, cmd, len + 1);
>     g_free(cmd);
> }
>
>diff --git a/tests/qtest/libqos/i2c.h b/tests/qtest/libqos/i2c.h
>index 945b65b34c..c65f087834 100644
>--- a/tests/qtest/libqos/i2c.h
>+++ b/tests/qtest/libqos/i2c.h
>@@ -47,8 +47,8 @@ struct QI2CDevice {
> void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
> void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
>
>-void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
>-void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
>+void qi2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
>+void qi2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
>
> void i2c_read_block(QI2CDevice *dev, uint8_t reg,
>                     uint8_t *buf, uint16_t len);
>diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
>index 4b800d3c3e..d80ed93cd3 100644
>--- a/tests/qtest/pca9552-test.c
>+++ b/tests/qtest/pca9552-test.c
>@@ -32,22 +32,22 @@ static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc)
>
>     pca9552_init(i2cdev);
>
>-    i2c_send(i2cdev, &reg, 1);
>+    qi2c_send(i2cdev, &reg, 1);
>
>     /* PCA9552_LS0 */
>-    i2c_recv(i2cdev, &resp, 1);
>+    qi2c_recv(i2cdev, &resp, 1);
>     g_assert_cmphex(resp, ==, 0x54);
>
>     /* PCA9552_LS1 */
>-    i2c_recv(i2cdev, &resp, 1);
>+    qi2c_recv(i2cdev, &resp, 1);
>     g_assert_cmphex(resp, ==, 0x55);
>
>     /* PCA9552_LS2 */
>-    i2c_recv(i2cdev, &resp, 1);
>+    qi2c_recv(i2cdev, &resp, 1);
>     g_assert_cmphex(resp, ==, 0x55);
>
>     /* PCA9552_LS3 */
>-    i2c_recv(i2cdev, &resp, 1);
>+    qi2c_recv(i2cdev, &resp, 1);
>     g_assert_cmphex(resp, ==, 0x54);
> }
>
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 11/21] fuzz: add fuzzer skeleton
  2020-01-29  5:34 ` [PATCH v8 11/21] fuzz: add fuzzer skeleton Bulekov, Alexander
@ 2020-02-05 11:55   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 11:55 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

Hi Alex,

On Wed, Jan 29, 2020 at 05:34:20AM +0000, Bulekov, Alexander wrote:
>tests/fuzz/fuzz.c serves as the entry point for the virtual-device
>fuzzer. Namely, libfuzzer invokes the LLVMFuzzerInitialize and
>LLVMFuzzerTestOneInput functions, both of which are defined in this
>file. This change adds a "FuzzTarget" struct, along with the
>fuzz_add_target function, which should be used to define new fuzz
>targets.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
>---
> tests/qtest/fuzz/Makefile.include |   6 +
> tests/qtest/fuzz/fuzz.c           | 179 ++++++++++++++++++++++++++++++
> tests/qtest/fuzz/fuzz.h           |  95 ++++++++++++++++
> 3 files changed, 280 insertions(+)
> create mode 100644 tests/qtest/fuzz/Makefile.include
> create mode 100644 tests/qtest/fuzz/fuzz.c
> create mode 100644 tests/qtest/fuzz/fuzz.h
>
>diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
>new file mode 100644
>index 0000000000..8632bb89f4
>--- /dev/null
>+++ b/tests/qtest/fuzz/Makefile.include
>@@ -0,0 +1,6 @@
>+QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
>+
>+fuzz-obj-y += tests/qtest/libqtest.o
>+fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
>+
>+FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
>diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c
>new file mode 100644
>index 0000000000..0d78ac8d36
>--- /dev/null
>+++ b/tests/qtest/fuzz/fuzz.c
>@@ -0,0 +1,179 @@
>+/*
>+ * fuzzing driver
>+ *
>+ * Copyright Red Hat Inc., 2019
>+ *
>+ * Authors:
>+ *  Alexander Bulekov   <alxndr@bu.edu>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ *
>+ */
>+
>+#include "qemu/osdep.h"
>+
>+#include <wordexp.h>
>+
>+#include "sysemu/qtest.h"
>+#include "sysemu/runstate.h"
>+#include "sysemu/sysemu.h"
>+#include "qemu/main-loop.h"
>+#include "tests/qtest/libqtest.h"
>+#include "tests/qtest/libqos/qgraph.h"
>+#include "fuzz.h"
>+
>+#define MAX_EVENT_LOOPS 10
>+
>+typedef struct FuzzTargetState {
>+        FuzzTarget *target;
>+        QSLIST_ENTRY(FuzzTargetState) target_list;
>+} FuzzTargetState;
>+
>+typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList;
>+
>+static const char *fuzz_arch = TARGET_NAME;
>+
>+static FuzzTargetList *fuzz_target_list;
>+static FuzzTarget *fuzz_target;
>+static QTestState *fuzz_qts;
>+
>+
>+
>+void flush_events(QTestState *s)
>+{
>+    int i = MAX_EVENT_LOOPS;
>+    while (g_main_context_pending(NULL) && i-- > 0) {
>+        main_loop_wait(false);
>+    }
>+}
>+
>+static QTestState *qtest_setup(void)
>+{
>+    qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts);
>+    return qtest_inproc_init(&fuzz_qts, false, fuzz_arch,
>+            &qtest_server_inproc_recv);
>+}
>+
>+void fuzz_add_target(const FuzzTarget *target)
>+{
>+    FuzzTargetState *tmp;
>+    FuzzTargetState *target_state;
>+    if (!fuzz_target_list) {
>+        fuzz_target_list = g_new0(FuzzTargetList, 1);
>+    }
>+
>+    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
>+        if (g_strcmp0(tmp->target->name, target->name) == 0) {
>+            fprintf(stderr, "Error: Fuzz target name %s already in use\n",
>+                    target->name);
>+            abort();
>+        }
>+    }
>+    target_state = g_new0(FuzzTargetState, 1);
>+    target_state->target = g_new0(FuzzTarget, 1);
>+    *(target_state->target) = *target;

It's a nit, but I would be wary of doing a structure assignment
where the structure contains pointers (or in this case, strings). I
know that they are declared as const char* in the structure, and the
most common use-case today is during init only, but it is usually
safer to clone such a structure, and possibly create functions to
clone, and possibly free.

It's not vital, so I'm happy to give a Rb based on the current
code, but something worth considering for the future.

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

Thanks,

Darren.

>+    QSLIST_INSERT_HEAD(fuzz_target_list, target_state, target_list);
>+}
>+
>+
>+
>+static void usage(char *path)
>+{
>+    printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path);
>+    printf("where FUZZ_TARGET is one of:\n");
>+    FuzzTargetState *tmp;
>+    if (!fuzz_target_list) {
>+        fprintf(stderr, "Fuzz target list not initialized\n");
>+        abort();
>+    }
>+    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
>+        printf(" * %s  : %s\n", tmp->target->name,
>+                tmp->target->description);
>+    }
>+    exit(0);
>+}
>+
>+static FuzzTarget *fuzz_get_target(char* name)
>+{
>+    FuzzTargetState *tmp;
>+    if (!fuzz_target_list) {
>+        fprintf(stderr, "Fuzz target list not initialized\n");
>+        abort();
>+    }
>+
>+    QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
>+        if (strcmp(tmp->target->name, name) == 0) {
>+            return tmp->target;
>+        }
>+    }
>+    return NULL;
>+}
>+
>+
>+/* Executed for each fuzzing-input */
>+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size)
>+{
>+    /*
>+     * Do the pre-fuzz-initialization before the first fuzzing iteration,
>+     * instead of before the actual fuzz loop. This is needed since libfuzzer
>+     * may fork off additional workers, prior to the fuzzing loop, and if
>+     * pre_fuzz() sets up e.g. shared memory, this should be done for the
>+     * individual worker processes
>+     */
>+    static int pre_fuzz_done;
>+    if (!pre_fuzz_done && fuzz_target->pre_fuzz) {
>+        fuzz_target->pre_fuzz(fuzz_qts);
>+        pre_fuzz_done = true;
>+    }
>+
>+    fuzz_target->fuzz(fuzz_qts, Data, Size);
>+    return 0;
>+}
>+
>+/* Executed once, prior to fuzzing */
>+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
>+{
>+
>+    char *target_name;
>+
>+    /* Initialize qgraph and modules */
>+    qos_graph_init();
>+    module_call_init(MODULE_INIT_FUZZ_TARGET);
>+    module_call_init(MODULE_INIT_QOM);
>+    module_call_init(MODULE_INIT_LIBQOS);
>+
>+    if (*argc <= 1) {
>+        usage(**argv);
>+    }
>+
>+    /* Identify the fuzz target */
>+    target_name = (*argv)[1];
>+    if (!strstr(target_name, "--fuzz-target=")) {
>+        usage(**argv);
>+    }
>+
>+    target_name += strlen("--fuzz-target=");
>+
>+    fuzz_target = fuzz_get_target(target_name);
>+    if (!fuzz_target) {
>+        usage(**argv);
>+    }
>+
>+    fuzz_qts = qtest_setup();
>+
>+    if (fuzz_target->pre_vm_init) {
>+        fuzz_target->pre_vm_init();
>+    }
>+
>+    /* Run QEMU's softmmu main with the fuzz-target dependent arguments */
>+    const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target);
>+
>+    /* Split the runcmd into an argv and argc */
>+    wordexp_t result;
>+    wordexp(init_cmdline, &result, 0);
>+
>+    qemu_init(result.we_wordc, result.we_wordv, NULL);
>+
>+    return 0;
>+}
>diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h
>new file mode 100644
>index 0000000000..03901d414e
>--- /dev/null
>+++ b/tests/qtest/fuzz/fuzz.h
>@@ -0,0 +1,95 @@
>+/*
>+ * fuzzing driver
>+ *
>+ * Copyright Red Hat Inc., 2019
>+ *
>+ * Authors:
>+ *  Alexander Bulekov   <alxndr@bu.edu>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ *
>+ */
>+
>+#ifndef FUZZER_H_
>+#define FUZZER_H_
>+
>+#include "qemu/osdep.h"
>+#include "qemu/units.h"
>+#include "qapi/error.h"
>+
>+#include "tests/qtest/libqtest.h"
>+
>+/**
>+ * A libfuzzer fuzzing target
>+ *
>+ * The QEMU fuzzing binary is built with all available targets, each
>+ * with a unique @name that can be specified on the command-line to
>+ * select which target should run.
>+ *
>+ * A target must implement ->fuzz() to process a random input.  If QEMU
>+ * crashes in ->fuzz() then libfuzzer will record a failure.
>+ *
>+ * Fuzzing targets are registered with fuzz_add_target():
>+ *
>+ *   static const FuzzTarget fuzz_target = {
>+ *       .name = "my-device-fifo",
>+ *       .description = "Fuzz the FIFO buffer registers of my-device",
>+ *       ...
>+ *   };
>+ *
>+ *   static void register_fuzz_target(void)
>+ *   {
>+ *       fuzz_add_target(&fuzz_target);
>+ *   }
>+ *   fuzz_target_init(register_fuzz_target);
>+ */
>+typedef struct FuzzTarget {
>+    const char *name;         /* target identifier (passed to --fuzz-target=)*/
>+    const char *description;  /* help text */
>+
>+
>+    /*
>+     * returns the arg-list that is passed to qemu/softmmu init()
>+     * Cannot be NULL
>+     */
>+    const char* (*get_init_cmdline)(struct FuzzTarget *);
>+
>+    /*
>+     * will run once, prior to running qemu/softmmu init.
>+     * eg: set up shared-memory for communication with the child-process
>+     * Can be NULL
>+     */
>+    void(*pre_vm_init)(void);
>+
>+    /*
>+     * will run once, after QEMU has been initialized, prior to the fuzz-loop.
>+     * eg: detect the memory map
>+     * Can be NULL
>+     */
>+    void(*pre_fuzz)(QTestState *);
>+
>+    /*
>+     * accepts and executes an input from libfuzzer. this is repeatedly
>+     * executed during the fuzzing loop. Its should handle setup, input
>+     * execution and cleanup.
>+     * Cannot be NULL
>+     */
>+    void(*fuzz)(QTestState *, const unsigned char *, size_t);
>+
>+} FuzzTarget;
>+
>+void flush_events(QTestState *);
>+void reboot(QTestState *);
>+
>+/*
>+ * makes a copy of *target and adds it to the target-list.
>+ * i.e. fine to set up target on the caller's stack
>+ */
>+void fuzz_add_target(const FuzzTarget *target);
>+
>+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size);
>+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp);
>+
>+#endif
>+
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 12/21] exec: keep ram block across fork when using qtest
  2020-01-29  5:34 ` [PATCH v8 12/21] exec: keep ram block across fork when using qtest Bulekov, Alexander
@ 2020-02-05 13:00   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:00 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, stefanha, Richard Henderson

On Wed, Jan 29, 2020 at 05:34:21AM +0000, Bulekov, Alexander wrote:
>Ram blocks were marked MADV_DONTFORK breaking fuzzing-tests which
>execute each test-input in a forked process.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> exec.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
>diff --git a/exec.c b/exec.c
>index d4b769d0d4..99368f175b 100644
>--- a/exec.c
>+++ b/exec.c
>@@ -35,6 +35,7 @@
> #include "sysemu/kvm.h"
> #include "sysemu/sysemu.h"
> #include "sysemu/tcg.h"
>+#include "sysemu/qtest.h"
> #include "qemu/timer.h"
> #include "qemu/config-file.h"
> #include "qemu/error-report.h"
>@@ -2306,8 +2307,15 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
>     if (new_block->host) {
>         qemu_ram_setup_dump(new_block->host, new_block->max_length);
>         qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE);
>-        /* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */
>-        qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK);
>+        /*
>+         * MADV_DONTFORK is also needed by KVM in absence of synchronous MMU
>+         * Configure it unless the machine is a qtest server, in which case
>+         * KVM is not used and it may be forked (eg for fuzzing purposes).
>+         */
>+        if (!qtest_enabled()) {
>+            qemu_madvise(new_block->host, new_block->max_length,
>+                         QEMU_MADV_DONTFORK);
>+        }
>         ram_block_notify_add(new_block->host, new_block->max_length);
>     }
> }
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets
  2020-01-29  5:34 ` [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets Bulekov, Alexander
@ 2020-02-05 13:18   ` Darren Kenny
  2020-02-11 16:19     ` Alexander Bulekov
  0 siblings, 1 reply; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:18 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:24AM +0000, Bulekov, Alexander wrote:
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
>---
> tests/qtest/fuzz/Makefile.include |   2 +
> tests/qtest/fuzz/qos_fuzz.c       | 229 ++++++++++++++++++++++++++++++
> tests/qtest/fuzz/qos_fuzz.h       |  33 +++++
> 3 files changed, 264 insertions(+)
> create mode 100644 tests/qtest/fuzz/qos_fuzz.c
> create mode 100644 tests/qtest/fuzz/qos_fuzz.h
>
>diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
>index a90915d56d..e3bdd33ff4 100644
>--- a/tests/qtest/fuzz/Makefile.include
>+++ b/tests/qtest/fuzz/Makefile.include
>@@ -1,8 +1,10 @@
> QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
>
> fuzz-obj-y += tests/qtest/libqtest.o
>+fuzz-obj-y += $(libqos-obj-y)
> fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
> fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
>+fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
>
> FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
>
>diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c
>new file mode 100644
>index 0000000000..efdcc6e9d3
>--- /dev/null
>+++ b/tests/qtest/fuzz/qos_fuzz.c
>@@ -0,0 +1,229 @@
>+/*
>+ * QOS-assisted fuzzing helpers
>+ *
>+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License version 2 as published by the Free Software Foundation.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
>+ */
>+
>+#include "qemu/osdep.h"
>+#include "qemu/units.h"
>+#include "qapi/error.h"
>+#include "qemu-common.h"
>+#include "exec/memory.h"
>+#include "exec/address-spaces.h"
>+#include "sysemu/sysemu.h"
>+#include "qemu/main-loop.h"
>+
>+#include "tests/qtest/libqtest.h"
>+#include "tests/qtest/libqos/malloc.h"
>+#include "tests/qtest/libqos/qgraph.h"
>+#include "tests/qtest/libqos/qgraph_internal.h"
>+#include "tests/qtest/libqos/qos_external.h"
>+
>+#include "fuzz.h"
>+#include "qos_fuzz.h"
>+
>+#include "qapi/qapi-commands-machine.h"
>+#include "qapi/qapi-commands-qom.h"
>+#include "qapi/qmp/qlist.h"
>+
>+
>+void *fuzz_qos_obj;
>+QGuestAllocator *fuzz_qos_alloc;
>+
>+static const char *fuzz_target_name;
>+static char **fuzz_path_vec;
>+
>+/*
>+ * Replaced the qmp commands with direct qmp_marshal calls.
>+ * Probably there is a better way to do this
>+ */
>+static void qos_set_machines_devices_available(void)
>+{
>+    QDict *req = qdict_new();
>+    QObject *response;
>+    QDict *args = qdict_new();
>+    QList *lst;
>+    Error *err = NULL;
>+
>+    qmp_marshal_query_machines(NULL, &response, &err);
>+    assert(!err);
>+    lst = qobject_to(QList, response);
>+    apply_to_qlist(lst, true);
>+
>+    qobject_unref(response);
>+
>+
>+    qdict_put_str(req, "execute", "qom-list-types");
>+    qdict_put_str(args, "implements", "device");
>+    qdict_put_bool(args, "abstract", true);
>+    qdict_put_obj(req, "arguments", (QObject *) args);
>+
>+    qmp_marshal_qom_list_types(args, &response, &err);
>+    assert(!err);
>+    lst = qobject_to(QList, response);
>+    apply_to_qlist(lst, false);
>+    qobject_unref(response);
>+    qobject_unref(req);
>+}
>+
>+static char **current_path;
>+
>+void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc)
>+{
>+    return allocate_objects(qts, current_path + 1, p_alloc);
>+}
>+
>+static const char *qos_build_main_args(void)
>+{
>+    char **path = fuzz_path_vec;

Is it possible that fuzz_path_vec is not valid here? Specifically,
how likely is it that walk_path() won't ever set it, or that it
results in a possible previous value being used since we don't reset
it before calling qos_graph_foreach_test_path() in
qos_get_cmdline().

Thanks,

Darren.

>+    QOSGraphNode *test_node;
>+    GString *cmd_line = g_string_new(path[0]);
>+    void *test_arg;
>+
>+    /* Before test */
>+    current_path = path;
>+    test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]);
>+    test_arg = test_node->u.test.arg;
>+    if (test_node->u.test.before) {
>+        test_arg = test_node->u.test.before(cmd_line, test_arg);
>+    }
>+    /* Prepend the arguments that we need */
>+    g_string_prepend(cmd_line,
>+            TARGET_NAME " -display none -machine accel=qtest -m 64 ");
>+    return cmd_line->str;
>+}
>+
>+/*
>+ * This function is largely a copy of qos-test.c:walk_path. Since walk_path
>+ * is itself a callback, its a little annoying to add another argument/layer of
>+ * indirection
>+ */
>+static void walk_path(QOSGraphNode *orig_path, int len)
>+{
>+    QOSGraphNode *path;
>+    QOSGraphEdge *edge;
>+
>+    /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */
>+    QOSEdgeType etype = QEDGE_CONSUMED_BY;
>+
>+    /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */
>+    char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2));
>+    int path_vec_size = 0;
>+
>+    char *after_cmd, *before_cmd, *after_device;
>+    GString *after_device_str = g_string_new("");
>+    char *node_name = orig_path->name, *path_str;
>+
>+    GString *cmd_line = g_string_new("");
>+    GString *cmd_line2 = g_string_new("");
>+
>+    path = qos_graph_get_node(node_name); /* root */
>+    node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */
>+
>+    path_vec[path_vec_size++] = node_name;
>+    path_vec[path_vec_size++] = qos_get_machine_type(node_name);
>+
>+    for (;;) {
>+        path = qos_graph_get_node(node_name);
>+        if (!path->path_edge) {
>+            break;
>+        }
>+
>+        node_name = qos_graph_edge_get_dest(path->path_edge);
>+
>+        /* append node command line + previous edge command line */
>+        if (path->command_line && etype == QEDGE_CONSUMED_BY) {
>+            g_string_append(cmd_line, path->command_line);
>+            g_string_append(cmd_line, after_device_str->str);
>+            g_string_truncate(after_device_str, 0);
>+        }
>+
>+        path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge);
>+        /* detect if edge has command line args */
>+        after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge);
>+        after_device = qos_graph_edge_get_extra_device_opts(path->path_edge);
>+        before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge);
>+        edge = qos_graph_get_edge(path->name, node_name);
>+        etype = qos_graph_edge_get_type(edge);
>+
>+        if (before_cmd) {
>+            g_string_append(cmd_line, before_cmd);
>+        }
>+        if (after_cmd) {
>+            g_string_append(cmd_line2, after_cmd);
>+        }
>+        if (after_device) {
>+            g_string_append(after_device_str, after_device);
>+        }
>+    }
>+
>+    path_vec[path_vec_size++] = NULL;
>+    g_string_append(cmd_line, after_device_str->str);
>+    g_string_free(after_device_str, true);
>+
>+    g_string_append(cmd_line, cmd_line2->str);
>+    g_string_free(cmd_line2, true);
>+
>+    /*
>+     * here position 0 has <arch>/<machine>, position 1 has <machine>.
>+     * The path must not have the <arch>, qtest_add_data_func adds it.
>+     */
>+    path_str = g_strjoinv("/", path_vec + 1);
>+
>+    /* Check that this is the test we care about: */
>+    char *test_name = strrchr(path_str, '/') + 1;
>+    if (strcmp(test_name, fuzz_target_name) == 0) {
>+        /*
>+         * put arch/machine in position 1 so run_one_test can do its work
>+         * and add the command line at position 0.
>+         */
>+        path_vec[1] = path_vec[0];
>+        path_vec[0] = g_string_free(cmd_line, false);
>+
>+        fuzz_path_vec = path_vec;
>+    } else {
>+        g_free(path_vec);
>+    }
>+
>+    g_free(path_str);
>+}
>+
>+static const char *qos_get_cmdline(FuzzTarget *t)
>+{
>+    /*
>+     * Set a global variable that we use to identify the qos_path for our
>+     * fuzz_target
>+     */
>+    fuzz_target_name = t->name;
>+    qos_set_machines_devices_available();
>+    qos_graph_foreach_test_path(walk_path);
>+    return qos_build_main_args();
>+}
>+
>+void fuzz_add_qos_target(
>+        FuzzTarget *fuzz_opts,
>+        const char *interface,
>+        QOSGraphTestOptions *opts
>+        )
>+{
>+    qos_add_test(fuzz_opts->name, interface, NULL, opts);
>+    fuzz_opts->get_init_cmdline = qos_get_cmdline;
>+    fuzz_add_target(fuzz_opts);
>+}
>+
>+void qos_init_path(QTestState *s)
>+{
>+    fuzz_qos_obj = qos_allocate_objects(s , &fuzz_qos_alloc);
>+}
>diff --git a/tests/qtest/fuzz/qos_fuzz.h b/tests/qtest/fuzz/qos_fuzz.h
>new file mode 100644
>index 0000000000..477f11b02b
>--- /dev/null
>+++ b/tests/qtest/fuzz/qos_fuzz.h
>@@ -0,0 +1,33 @@
>+/*
>+ * QOS-assisted fuzzing helpers
>+ *
>+ * Copyright Red Hat Inc., 2019
>+ *
>+ * Authors:
>+ *  Alexander Bulekov   <alxndr@bu.edu>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#ifndef _QOS_FUZZ_H_
>+#define _QOS_FUZZ_H_
>+
>+#include "tests/qtest/fuzz/fuzz.h"
>+#include "tests/qtest/libqos/qgraph.h"
>+
>+int qos_fuzz(const unsigned char *Data, size_t Size);
>+void qos_setup(void);
>+
>+extern void *fuzz_qos_obj;
>+extern QGuestAllocator *fuzz_qos_alloc;
>+
>+void fuzz_add_qos_target(
>+        FuzzTarget *fuzz_opts,
>+        const char *interface,
>+        QOSGraphTestOptions *opts
>+        );
>+
>+void qos_init_path(QTestState *);
>+
>+#endif
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 17/21] fuzz: add configure flag --enable-fuzzing
  2020-01-29  5:34 ` [PATCH v8 17/21] fuzz: add configure flag --enable-fuzzing Bulekov, Alexander
@ 2020-02-05 13:19   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:19 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: pbonzini, bsd, Philippe Mathieu-Daudé, qemu-devel, stefanha

On Wed, Jan 29, 2020 at 05:34:25AM +0000, Bulekov, Alexander wrote:
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
>Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> configure | 39 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 39 insertions(+)
>
>diff --git a/configure b/configure
>index 08c3a1c1f0..1a1e57eb61 100755
>--- a/configure
>+++ b/configure
>@@ -504,6 +504,7 @@ debug_mutex="no"
> libpmem=""
> default_devices="yes"
> plugins="no"
>+fuzzing="no"
>
> supported_cpu="no"
> supported_os="no"
>@@ -634,6 +635,15 @@ int main(void) { return 0; }
> EOF
> }
>
>+write_c_fuzzer_skeleton() {
>+    cat > $TMPC <<EOF
>+#include <stdint.h>
>+#include <sys/types.h>
>+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
>+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; }
>+EOF
>+}
>+
> if check_define __linux__ ; then
>   targetos="Linux"
> elif check_define _WIN32 ; then
>@@ -1540,6 +1550,10 @@ for opt do
>   ;;
>   --disable-containers) use_containers="no"
>   ;;
>+  --enable-fuzzing) fuzzing=yes
>+  ;;
>+  --disable-fuzzing) fuzzing=no
>+  ;;
>   *)
>       echo "ERROR: unknown option $opt"
>       echo "Try '$0 --help' for more information"
>@@ -5992,6 +6006,15 @@ EOF
>   fi
> fi
>
>+##########################################
>+# checks for fuzzer
>+if test "$fuzzing" = "yes" ; then
>+  write_c_fuzzer_skeleton
>+  if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address,fuzzer" ""; then
>+      have_fuzzer=yes
>+  fi
>+fi
>+
> ##########################################
> # check for libpmem
>
>@@ -6576,6 +6599,7 @@ echo "libpmem support   $libpmem"
> echo "libudev           $libudev"
> echo "default devices   $default_devices"
> echo "plugin support    $plugins"
>+echo "fuzzing support   $fuzzing"
>
> if test "$supported_cpu" = "no"; then
>     echo
>@@ -7400,6 +7424,16 @@ fi
> if test "$sheepdog" = "yes" ; then
>   echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
> fi
>+if test "$fuzzing" = "yes" ; then
>+  if test "$have_fuzzer" = "yes"; then
>+    FUZZ_LDFLAGS=" -fsanitize=address,fuzzer"
>+    FUZZ_CFLAGS=" -fsanitize=address,fuzzer"
>+    CFLAGS=" -fsanitize=address,fuzzer-no-link"
>+  else
>+    error_exit "Your compiler doesn't support -fsanitize=address,fuzzer"
>+    exit 1
>+  fi
>+fi
>
> if test "$plugins" = "yes" ; then
>     echo "CONFIG_PLUGIN=y" >> $config_host_mak
>@@ -7502,6 +7536,11 @@ if test "$libudev" != "no"; then
>     echo "CONFIG_LIBUDEV=y" >> $config_host_mak
>     echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak
> fi
>+if test "$fuzzing" != "no"; then
>+    echo "CONFIG_FUZZ=y" >> $config_host_mak
>+    echo "FUZZ_CFLAGS=$FUZZ_CFLAGS" >> $config_host_mak
>+    echo "FUZZ_LDFLAGS=$FUZZ_LDFLAGS" >> $config_host_mak
>+fi
>
> if test "$edk2_blobs" = "yes" ; then
>   echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak
>-- 
>2.23.0
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 18/21] fuzz: add i440fx fuzz targets
  2020-01-29  5:34 ` [PATCH v8 18/21] fuzz: add i440fx fuzz targets Bulekov, Alexander
@ 2020-02-05 13:26   ` Darren Kenny
  2020-02-06 14:47     ` Alexander Bulekov
  0 siblings, 1 reply; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:26 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:26AM +0000, Bulekov, Alexander wrote:
>These three targets should simply fuzz reads/writes to a couple ioports,
>but they mostly serve as examples of different ways to write targets.
>They demonstrate using qtest and qos for fuzzing, as well as using
>rebooting and forking to reset state, or not resetting it at all.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

A couple of nit below w.r.t. commenting on how the fuzzed data is
being processed.

>---
> tests/qtest/fuzz/Makefile.include |   3 +
> tests/qtest/fuzz/i440fx_fuzz.c    | 178 ++++++++++++++++++++++++++++++
> 2 files changed, 181 insertions(+)
> create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c
>
>diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
>index e3bdd33ff4..38b8cdd9f1 100644
>--- a/tests/qtest/fuzz/Makefile.include
>+++ b/tests/qtest/fuzz/Makefile.include
>@@ -6,6 +6,9 @@ fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
> fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
> fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
>
>+# Targets
>+fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
>+
> FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
>
> # Linker Script to force coverage-counters into known regions which we can mark
>diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c
>new file mode 100644
>index 0000000000..c7791182b8
>--- /dev/null
>+++ b/tests/qtest/fuzz/i440fx_fuzz.c
>@@ -0,0 +1,178 @@
>+/*
>+ * I440FX Fuzzing Target
>+ *
>+ * Copyright Red Hat Inc., 2019
>+ *
>+ * Authors:
>+ *  Alexander Bulekov   <alxndr@bu.edu>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#include "qemu/osdep.h"
>+
>+#include "qemu/main-loop.h"
>+#include "tests/qtest/libqtest.h"
>+#include "tests/qtest/libqos/pci.h"
>+#include "tests/qtest/libqos/pci-pc.h"
>+#include "fuzz.h"
>+#include "fuzz/qos_fuzz.h"
>+#include "fuzz/fork_fuzz.h"
>+
>+
>+#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
>+#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
>+
>+enum action_id {
>+    WRITEB,
>+    WRITEW,
>+    WRITEL,
>+    READB,
>+    READW,
>+    READL,
>+    ACTION_MAX
>+};
>+

While it eventually becomes clear what is happening in these
functions, it does take several attempts at reading it to understand
what is going on.

For that reason, it might be worth a couple of comments for future
maintainers as to what is going on.

Thanks,

Darren.

>+static void i440fx_fuzz_qtest(QTestState *s,
>+        const unsigned char *Data, size_t Size) {
>+    typedef struct QTestFuzzAction {
>+        uint32_t value;
>+        uint8_t id;
>+        uint8_t addr;
>+    } QTestFuzzAction;
>+    QTestFuzzAction a;
>+
>+    while (Size >= sizeof(a)) {
>+        memcpy(&a, Data, sizeof(a));
>+        uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
>+                                      I440FX_PCI_HOST_BRIDGE_DATA;
>+        switch (a.id % ACTION_MAX) {
>+        case WRITEB:
>+            qtest_outb(s, addr, (uint8_t)a.value);
>+            break;
>+        case WRITEW:
>+            qtest_outw(s, addr, (uint16_t)a.value);
>+            break;
>+        case WRITEL:
>+            qtest_outl(s, addr, (uint32_t)a.value);
>+            break;
>+        case READB:
>+            qtest_inb(s, addr);
>+            break;
>+        case READW:
>+            qtest_inw(s, addr);
>+            break;
>+        case READL:
>+            qtest_inl(s, addr);
>+            break;
>+        }
>+        Size -= sizeof(a);
>+        Data += sizeof(a);
>+    }
>+    flush_events(s);
>+}
>+
>+static void i440fx_fuzz_qos(QTestState *s,
>+        const unsigned char *Data, size_t Size) {
>+
>+    typedef struct QOSFuzzAction {
>+        uint32_t value;
>+        int devfn;
>+        uint8_t offset;
>+        uint8_t id;
>+    } QOSFuzzAction;
>+
>+    static QPCIBus *bus;
>+    if (!bus) {
>+        bus = qpci_new_pc(s, fuzz_qos_alloc);
>+    }
>+
>+    QOSFuzzAction a;
>+    while (Size >= sizeof(a)) {
>+        memcpy(&a, Data, sizeof(a));
>+        switch (a.id % ACTION_MAX) {
>+        case WRITEB:
>+            bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
>+            break;
>+        case WRITEW:
>+            bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
>+            break;
>+        case WRITEL:
>+            bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
>+            break;
>+        case READB:
>+            bus->config_readb(bus, a.devfn, a.offset);
>+            break;
>+        case READW:
>+            bus->config_readw(bus, a.devfn, a.offset);
>+            break;
>+        case READL:
>+            bus->config_readl(bus, a.devfn, a.offset);
>+            break;
>+        }
>+        Size -= sizeof(a);
>+        Data += sizeof(a);
>+    }
>+    flush_events(s);
>+}
>+
>+static void i440fx_fuzz_qos_fork(QTestState *s,
>+        const unsigned char *Data, size_t Size) {
>+    if (fork() == 0) {
>+        i440fx_fuzz_qos(s, Data, Size);
>+        _Exit(0);
>+    } else {
>+        wait(NULL);
>+    }
>+}
>+
>+static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
>+                                       "-m 0 -display none";
>+static const char *i440fx_argv(FuzzTarget *t)
>+{
>+    return i440fx_qtest_argv;
>+}
>+
>+static void fork_init(void)
>+{
>+    counter_shm_init();
>+}
>+
>+static void register_pci_fuzz_targets(void)
>+{
>+    /* Uses simple qtest commands and reboots to reset state */
>+    fuzz_add_target(&(FuzzTarget){
>+                .name = "i440fx-qtest-reboot-fuzz",
>+                .description = "Fuzz the i440fx using raw qtest commands and"
>+                               "rebooting after each run",
>+                .get_init_cmdline = i440fx_argv,
>+                .fuzz = i440fx_fuzz_qtest});
>+
>+    /* Uses libqos and forks to prevent state leakage */
>+    fuzz_add_qos_target(&(FuzzTarget){
>+                .name = "i440fx-qos-fork-fuzz",
>+                .description = "Fuzz the i440fx using raw qtest commands and"
>+                               "rebooting after each run",
>+                .pre_vm_init = &fork_init,
>+                .fuzz = i440fx_fuzz_qos_fork,},
>+                "i440FX-pcihost",
>+                &(QOSGraphTestOptions){}
>+                );
>+
>+    /*
>+     * Uses libqos. Doesn't do anything to reset state. Note that if we were to
>+     * reboot after each run, we would also have to redo the qos-related
>+     * initialization (qos_init_path)
>+     */
>+    fuzz_add_qos_target(&(FuzzTarget){
>+                .name = "i440fx-qos-noreset-fuzz",
>+                .description = "Fuzz the i440fx using raw qtest commands and"
>+                               "rebooting after each run",
>+                .fuzz = i440fx_fuzz_qos,},
>+                "i440FX-pcihost",
>+                &(QOSGraphTestOptions){}
>+                );
>+}
>+
>+fuzz_target_init(register_pci_fuzz_targets);
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 20/21] fuzz: add virtio-scsi fuzz target
  2020-01-29  5:34 ` [PATCH v8 20/21] fuzz: add virtio-scsi " Bulekov, Alexander
@ 2020-02-05 13:31   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:31 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:28AM +0000, Bulekov, Alexander wrote:
>The virtio-scsi fuzz target sets up and fuzzes the available virtio-scsi
>queues. After an element is placed on a queue, the fuzzer can select
>whether to perform a kick, or continue adding elements.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

Similar comments below here about maybe documenting how the fuzz
data is being mapped into actions.

>---
> tests/qtest/fuzz/Makefile.include   |   1 +
> tests/qtest/fuzz/virtio_scsi_fuzz.c | 200 ++++++++++++++++++++++++++++
> 2 files changed, 201 insertions(+)
> create mode 100644 tests/qtest/fuzz/virtio_scsi_fuzz.c
>
>diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
>index 77385777ef..cde3e9636c 100644
>--- a/tests/qtest/fuzz/Makefile.include
>+++ b/tests/qtest/fuzz/Makefile.include
>@@ -9,6 +9,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
> # Targets
> fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
> fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
>+fuzz-obj-y += tests/qtest/fuzz/virtio_scsi_fuzz.o
>
> FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
>
>diff --git a/tests/qtest/fuzz/virtio_scsi_fuzz.c b/tests/qtest/fuzz/virtio_scsi_fuzz.c
>new file mode 100644
>index 0000000000..ee7ca5448c
>--- /dev/null
>+++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c
>@@ -0,0 +1,200 @@
>+/*
>+ * virtio-serial Fuzzing Target
>+ *
>+ * Copyright Red Hat Inc., 2019
>+ *
>+ * Authors:
>+ *  Alexander Bulekov   <alxndr@bu.edu>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#include "qemu/osdep.h"
>+
>+#include "tests/qtest/libqtest.h"
>+#include "tests/qtest/libqos/virtio-net.h"
>+#include "libqos/virtio-scsi.h"
>+#include "libqos/virtio.h"
>+#include "libqos/virtio-pci.h"
>+#include "standard-headers/linux/virtio_ids.h"
>+#include "standard-headers/linux/virtio_pci.h"
>+#include "standard-headers/linux/virtio_scsi.h"
>+#include "fuzz.h"
>+#include "fork_fuzz.h"
>+#include "qos_fuzz.h"
>+
>+#define PCI_SLOT                0x02
>+#define PCI_FN                  0x00
>+#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
>+
>+#define MAX_NUM_QUEUES 64
>+
>+/* Based on tests/virtio-scsi-test.c */
>+typedef struct {
>+    int num_queues;
>+    QVirtQueue *vq[MAX_NUM_QUEUES + 2];
>+} QVirtioSCSIQueues;
>+
>+static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask)
>+{
>+    QVirtioSCSIQueues *vs;
>+    uint64_t feat;
>+    int i;
>+
>+    vs = g_new0(QVirtioSCSIQueues, 1);
>+
>+    feat = qvirtio_get_features(dev);
>+    if (mask) {
>+        feat &= ~QVIRTIO_F_BAD_FEATURE | mask;
>+    } else {
>+        feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
>+    }
>+    qvirtio_set_features(dev, feat);
>+
>+    vs->num_queues = qvirtio_config_readl(dev, 0);
>+
>+    for (i = 0; i < vs->num_queues + 2; i++) {
>+        vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i);
>+    }
>+
>+    qvirtio_set_driver_ok(dev);
>+
>+    return vs;
>+}
>+
>+static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues,
>+        const unsigned char *Data, size_t Size)
>+{
>+    typedef struct vq_action {
>+        uint8_t queue;
>+        uint8_t length;
>+        uint8_t write;
>+        uint8_t next;
>+        uint8_t kick;
>+    } vq_action;
>+
>+    uint32_t free_head[MAX_NUM_QUEUES + 2] = {0};
>+    QGuestAllocator *t_alloc = fuzz_qos_alloc;
>+
>+    QVirtioSCSI *scsi = fuzz_qos_obj;
>+    QVirtioDevice *dev = scsi->vdev;
>+    QVirtQueue *q;
>+    vq_action vqa;
>+    while (Size >= sizeof(vqa)) {
>+        memcpy(&vqa, Data, sizeof(vqa));
>+
>+        Data += sizeof(vqa);
>+        Size -= sizeof(vqa);
>+
>+        vqa.queue = vqa.queue % queues->num_queues;
>+        vqa.length = vqa.length >= Size ? Size : vqa.length;
>+        vqa.write = vqa.write & 1;
>+        vqa.next = vqa.next & 1;
>+        vqa.kick = vqa.kick & 1;
>+
>+
>+        q = queues->vq[vqa.queue];
>+
>+        uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
>+        qtest_memwrite(s, req_addr, Data, vqa.length);
>+        if (free_head[vqa.queue] == 0) {
>+            free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
>+                    vqa.write, vqa.next);
>+        } else {
>+            qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
>+        }
>+
>+        if (vqa.kick) {
>+            qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
>+            free_head[vqa.queue] = 0;
>+        }
>+        Data += vqa.length;
>+        Size -= vqa.length;
>+    }
>+    for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
>+        if (free_head[i]) {
>+            qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
>+        }
>+    }
>+}
>+
>+static void virtio_scsi_fork_fuzz(QTestState *s,
>+        const unsigned char *Data, size_t Size)
>+{
>+    QVirtioSCSI *scsi = fuzz_qos_obj;
>+    static QVirtioSCSIQueues *queues;
>+    if (!queues) {
>+        queues = qvirtio_scsi_init(scsi->vdev, 0);
>+    }
>+    if (fork() == 0) {
>+        virtio_scsi_fuzz(s, queues, Data, Size);
>+        flush_events(s);
>+        _Exit(0);
>+    } else {
>+        wait(NULL);
>+    }
>+}
>+
>+static void virtio_scsi_with_flag_fuzz(QTestState *s,
>+        const unsigned char *Data, size_t Size)
>+{
>+    QVirtioSCSI *scsi = fuzz_qos_obj;
>+    static QVirtioSCSIQueues *queues;
>+
>+    if (fork() == 0) {
>+        if (Size >= sizeof(uint64_t)) {
>+            queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data);
>+            virtio_scsi_fuzz(s, queues,
>+                             Data + sizeof(uint64_t), Size - sizeof(uint64_t));
>+            flush_events(s);
>+        }
>+        _Exit(0);
>+    } else {
>+        wait(NULL);
>+    }
>+}
>+
>+static void virtio_scsi_pre_fuzz(QTestState *s)
>+{
>+    qos_init_path(s);
>+    counter_shm_init();
>+}
>+
>+static void *virtio_scsi_test_setup(GString *cmd_line, void *arg)
>+{
>+    g_string_append(cmd_line,
>+                    " -drive file=blkdebug::null-co://,"
>+                    "file.image.read-zeroes=on,"
>+                    "if=none,id=dr1,format=raw,file.align=4k "
>+                    "-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
>+    return arg;
>+}
>+
>+
>+static void register_virtio_scsi_fuzz_targets(void)
>+{
>+    fuzz_add_qos_target(&(FuzzTarget){
>+                .name = "virtio-scsi-fuzz",
>+                .description = "Fuzz the virtio-net virtual queues, forking"
>+                                "for each fuzz run",
>+                .pre_vm_init = &counter_shm_init,
>+                .pre_fuzz = &virtio_scsi_pre_fuzz,
>+                .fuzz = virtio_scsi_fork_fuzz,},
>+                "virtio-scsi",
>+                &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
>+                );
>+
>+    fuzz_add_qos_target(&(FuzzTarget){
>+                .name = "virtio-scsi-flags-fuzz",
>+                .description = "Fuzz the virtio-net virtual queues, forking"
>+                "for each fuzz run (also fuzzes the virtio flags)",
>+                .pre_vm_init = &counter_shm_init,
>+                .pre_fuzz = &virtio_scsi_pre_fuzz,
>+                .fuzz = virtio_scsi_with_flag_fuzz,},
>+                "virtio-scsi",
>+                &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
>+                );
>+}
>+
>+fuzz_target_init(register_virtio_scsi_fuzz_targets);
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 21/21] fuzz: add documentation to docs/devel/
  2020-01-29  5:34 ` [PATCH v8 21/21] fuzz: add documentation to docs/devel/ Bulekov, Alexander
@ 2020-02-05 13:33   ` Darren Kenny
  0 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:33 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, stefanha

On Wed, Jan 29, 2020 at 05:34:29AM +0000, Bulekov, Alexander wrote:
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> docs/devel/fuzzing.txt | 116 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 116 insertions(+)
> create mode 100644 docs/devel/fuzzing.txt
>
>diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt
>new file mode 100644
>index 0000000000..324d2cd92b
>--- /dev/null
>+++ b/docs/devel/fuzzing.txt
>@@ -0,0 +1,116 @@
>+= Fuzzing =
>+
>+== Introduction ==
>+
>+This document describes the virtual-device fuzzing infrastructure in QEMU and
>+how to use it to implement additional fuzzers.
>+
>+== Basics ==
>+
>+Fuzzing operates by passing inputs to an entry point/target function. The
>+fuzzer tracks the code coverage triggered by the input. Based on these
>+findings, the fuzzer mutates the input and repeats the fuzzing.
>+
>+To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer
>+is an _in-process_ fuzzer. For the developer, this means that it is their
>+responsibility to ensure that state is reset between fuzzing-runs.
>+
>+== Building the fuzzers ==
>+
>+NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is
>+much faster, since the page-map has a smaller size. This is due to the fact that
>+AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results
>+in a large page-map, and a much slower fork().
>+
>+To build the fuzzers, install a recent version of clang:
>+Configure with (substitute the clang binaries with the version you installed):
>+
>+    CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing
>+
>+Fuzz targets are built similarly to system/softmmu:
>+
>+    make i386-softmmu/fuzz
>+
>+This builds ./i386-softmmu/qemu-fuzz-i386
>+
>+The first option to this command is: --fuzz_taget=FUZZ_NAME
>+To list all of the available fuzzers run qemu-fuzz-i386 with no arguments.
>+
>+eg:
>+    ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz
>+
>+Internally, libfuzzer parses all arguments that do not begin with "--".
>+Information about these is available by passing -help=1
>+
>+Now the only thing left to do is wait for the fuzzer to trigger potential
>+crashes.
>+
>+== Adding a new fuzzer ==
>+Coverage over virtual devices can be improved by adding additional fuzzers.
>+Fuzzers are kept in tests/qtest/fuzz/ and should be added to
>+tests/qtest/fuzz/Makefile.include
>+
>+Fuzzers can rely on both qtest and libqos to communicate with virtual devices.
>+
>+1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``.
>+
>+2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers
>+for reference.
>+
>+3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the
>+corresponding object to fuzz-obj-y
>+
>+Fuzzers can be more-or-less thought of as special qtest programs which can
>+modify the qtest commands and/or qtest command arguments based on inputs
>+provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the
>+fuzzer loops over the byte-array interpreting it as a list of qtest commands,
>+addresses, or values.
>+
>+= Implementation Details =
>+
>+== The Fuzzer's Lifecycle ==
>+
>+The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's
>+own main(), which performs some setup, and calls the entrypoints:
>+
>+LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the
>+necessary state
>+
>+LLVMFuzzerTestOneInput: called for each fuzzing run. Processes the input and
>+resets the state at the end of each run.
>+
>+In more detail:
>+
>+LLVMFuzzerInitialize parses the arguments to the fuzzer (must start with two
>+dashes, so they are ignored by libfuzzer main()). Currently, the arguments
>+select the fuzz target. Then, the qtest client is initialized. If the target
>+requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
>+Then the QGraph is walked and the QEMU cmd_line is determined and saved.
>+
>+After this, the vl.c:qemu__main is called to set up the guest. There are
>+target-specific hooks that can be called before and after qemu_main, for
>+additional setup(e.g. PCI setup, or VM snapshotting).
>+
>+LLVMFuzzerTestOneInput: Uses qtest/qos functions to act based on the fuzz
>+input. It is also responsible for manually calling the main loop/main_loop_wait
>+to ensure that bottom halves are executed and any cleanup required before the
>+next input.
>+
>+Since the same process is reused for many fuzzing runs, QEMU state needs to
>+be reset at the end of each run. There are currently two implemented
>+options for resetting state:
>+1. Reboot the guest between runs.
>+   Pros: Straightforward and fast for simple fuzz targets.
>+   Cons: Depending on the device, does not reset all device state. If the
>+   device requires some initialization prior to being ready for fuzzing
>+   (common for QOS-based targets), this initialization needs to be done after
>+   each reboot.
>+   Example target: i440fx-qtest-reboot-fuzz
>+2. Run each test case in a separate forked process and copy the coverage
>+   information back to the parent. This is fairly similar to AFL's "deferred"
>+   fork-server mode [3]
>+   Pros: Relatively fast. Devices only need to be initialized once. No need
>+   to do slow reboots or vmloads.
>+   Cons: Not officially supported by libfuzzer. Does not work well for devices
>+   that rely on dedicated threads.
>+   Example target: virtio-net-fork-fuzz
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external
  2020-01-29  5:34 ` [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external Bulekov, Alexander
  2020-01-29 10:03   ` Paolo Bonzini
@ 2020-02-05 13:34   ` Darren Kenny
  1 sibling, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:34 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini,
	Philippe Mathieu-Daudé

On Wed, Jan 29, 2020 at 05:34:19AM +0000, Bulekov, Alexander wrote:
>The moved functions are not specific to qos-test and might be useful
>elsewhere. For example the virtual-device fuzzer makes use of them for
>qos-assisted fuzz-targets.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
>Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> tests/qtest/Makefile.include      |   1 +
> tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++++++++++++
> tests/qtest/libqos/qos_external.h |  28 +++++
> tests/qtest/qos-test.c            | 132 +----------------------
> 4 files changed, 198 insertions(+), 131 deletions(-)
> create mode 100644 tests/qtest/libqos/qos_external.c
> create mode 100644 tests/qtest/libqos/qos_external.h
>
>diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
>index 08a48c1252..bdc93d3866 100644
>--- a/tests/qtest/Makefile.include
>+++ b/tests/qtest/Makefile.include
>@@ -172,6 +172,7 @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/u
> # qos devices:
> libqos-obj-y =  $(libqgraph-obj-y)
> libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
>+libqos-obj-y += tests/qtest/libqos/qos_external.o
> libqos-obj-y += tests/qtest/libqos/e1000e.o
> libqos-obj-y += tests/qtest/libqos/i2c.o
> libqos-obj-y += tests/qtest/libqos/i2c-imx.o
>diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c
>new file mode 100644
>index 0000000000..398556dde0
>--- /dev/null
>+++ b/tests/qtest/libqos/qos_external.c
>@@ -0,0 +1,168 @@
>+/*
>+ * libqos driver framework
>+ *
>+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License version 2 as published by the Free Software Foundation.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
>+ */
>+
>+#include "qemu/osdep.h"
>+#include <getopt.h>
>+#include "libqtest.h"
>+#include "qapi/qmp/qdict.h"
>+#include "qapi/qmp/qbool.h"
>+#include "qapi/qmp/qstring.h"
>+#include "qemu/module.h"
>+#include "qapi/qmp/qlist.h"
>+#include "libqos/malloc.h"
>+#include "libqos/qgraph.h"
>+#include "libqos/qgraph_internal.h"
>+#include "libqos/qos_external.h"
>+
>+
>+
>+void apply_to_node(const char *name, bool is_machine, bool is_abstract)
>+{
>+    char *machine_name = NULL;
>+    if (is_machine) {
>+        const char *arch = qtest_get_arch();
>+        machine_name = g_strconcat(arch, "/", name, NULL);
>+        name = machine_name;
>+    }
>+    qos_graph_node_set_availability(name, true);
>+    if (is_abstract) {
>+        qos_delete_cmd_line(name);
>+    }
>+    g_free(machine_name);
>+}
>+
>+/**
>+ * apply_to_qlist(): using QMP queries QEMU for a list of
>+ * machines and devices available, and sets the respective node
>+ * as true. If a node is found, also all its produced and contained
>+ * child are marked available.
>+ *
>+ * See qos_graph_node_set_availability() for more info
>+ */
>+void apply_to_qlist(QList *list, bool is_machine)
>+{
>+    const QListEntry *p;
>+    const char *name;
>+    bool abstract;
>+    QDict *minfo;
>+    QObject *qobj;
>+    QString *qstr;
>+    QBool *qbool;
>+
>+    for (p = qlist_first(list); p; p = qlist_next(p)) {
>+        minfo = qobject_to(QDict, qlist_entry_obj(p));
>+        qobj = qdict_get(minfo, "name");
>+        qstr = qobject_to(QString, qobj);
>+        name = qstring_get_str(qstr);
>+
>+        qobj = qdict_get(minfo, "abstract");
>+        if (qobj) {
>+            qbool = qobject_to(QBool, qobj);
>+            abstract = qbool_get_bool(qbool);
>+        } else {
>+            abstract = false;
>+        }
>+
>+        apply_to_node(name, is_machine, abstract);
>+        qobj = qdict_get(minfo, "alias");
>+        if (qobj) {
>+            qstr = qobject_to(QString, qobj);
>+            name = qstring_get_str(qstr);
>+            apply_to_node(name, is_machine, abstract);
>+        }
>+    }
>+}
>+
>+QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
>+{
>+    return obj->get_driver(obj, "memory");
>+}
>+
>+/**
>+ * allocate_objects(): given an array of nodes @arg,
>+ * walks the path invoking all constructors and
>+ * passing the corresponding parameter in order to
>+ * continue the objects allocation.
>+ * Once the test is reached, return the object it consumes.
>+ *
>+ * Since the machine and QEDGE_CONSUMED_BY nodes allocate
>+ * memory in the constructor, g_test_queue_destroy is used so
>+ * that after execution they can be safely free'd.  (The test's
>+ * ->before callback is also welcome to use g_test_queue_destroy).
>+ *
>+ * Note: as specified in walk_path() too, @arg is an array of
>+ * char *, where arg[0] is a pointer to the command line
>+ * string that will be used to properly start QEMU when executing
>+ * the test, and the remaining elements represent the actual objects
>+ * that will be allocated.
>+ */
>+void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
>+{
>+    int current = 0;
>+    QGuestAllocator *alloc;
>+    QOSGraphObject *parent = NULL;
>+    QOSGraphEdge *edge;
>+    QOSGraphNode *node;
>+    void *edge_arg;
>+    void *obj;
>+
>+    node = qos_graph_get_node(path[current]);
>+    g_assert(node->type == QNODE_MACHINE);
>+
>+    obj = qos_machine_new(node, qts);
>+    qos_object_queue_destroy(obj);
>+
>+    alloc = get_machine_allocator(obj);
>+    if (p_alloc) {
>+        *p_alloc = alloc;
>+    }
>+
>+    for (;;) {
>+        if (node->type != QNODE_INTERFACE) {
>+            qos_object_start_hw(obj);
>+            parent = obj;
>+        }
>+
>+        /* follow edge and get object for next node constructor */
>+        current++;
>+        edge = qos_graph_get_edge(path[current - 1], path[current]);
>+        node = qos_graph_get_node(path[current]);
>+
>+        if (node->type == QNODE_TEST) {
>+            g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
>+            return obj;
>+        }
>+
>+        switch (qos_graph_edge_get_type(edge)) {
>+        case QEDGE_PRODUCES:
>+            obj = parent->get_driver(parent, path[current]);
>+            break;
>+
>+        case QEDGE_CONSUMED_BY:
>+            edge_arg = qos_graph_edge_get_arg(edge);
>+            obj = qos_driver_new(node, obj, alloc, edge_arg);
>+            qos_object_queue_destroy(obj);
>+            break;
>+
>+        case QEDGE_CONTAINS:
>+            obj = parent->get_device(parent, path[current]);
>+            break;
>+        }
>+    }
>+}
>+
>diff --git a/tests/qtest/libqos/qos_external.h b/tests/qtest/libqos/qos_external.h
>new file mode 100644
>index 0000000000..7b44930c55
>--- /dev/null
>+++ b/tests/qtest/libqos/qos_external.h
>@@ -0,0 +1,28 @@
>+/*
>+ * libqos driver framework
>+ *
>+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License version 2 as published by the Free Software Foundation.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
>+ */
>+
>+#ifndef QOS_EXTERNAL_H
>+#define QOS_EXTERNAL_H
>+#include "libqos/qgraph.h"
>+
>+void apply_to_node(const char *name, bool is_machine, bool is_abstract);
>+void apply_to_qlist(QList *list, bool is_machine);
>+QGuestAllocator *get_machine_allocator(QOSGraphObject *obj);
>+void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc);
>+
>+#endif
>diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c
>index fd70d73ea5..ad193f43a5 100644
>--- a/tests/qtest/qos-test.c
>+++ b/tests/qtest/qos-test.c
>@@ -27,65 +27,11 @@
> #include "libqos/malloc.h"
> #include "libqos/qgraph.h"
> #include "libqos/qgraph_internal.h"
>+#include "libqos/qos_external.h"
>
> static char *old_path;
>
>-static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
>-{
>-    char *machine_name = NULL;
>-    if (is_machine) {
>-        const char *arch = qtest_get_arch();
>-        machine_name = g_strconcat(arch, "/", name, NULL);
>-        name = machine_name;
>-    }
>-    qos_graph_node_set_availability(name, true);
>-    if (is_abstract) {
>-        qos_delete_cmd_line(name);
>-    }
>-    g_free(machine_name);
>-}
>
>-/**
>- * apply_to_qlist(): using QMP queries QEMU for a list of
>- * machines and devices available, and sets the respective node
>- * as true. If a node is found, also all its produced and contained
>- * child are marked available.
>- *
>- * See qos_graph_node_set_availability() for more info
>- */
>-static void apply_to_qlist(QList *list, bool is_machine)
>-{
>-    const QListEntry *p;
>-    const char *name;
>-    bool abstract;
>-    QDict *minfo;
>-    QObject *qobj;
>-    QString *qstr;
>-    QBool *qbool;
>-
>-    for (p = qlist_first(list); p; p = qlist_next(p)) {
>-        minfo = qobject_to(QDict, qlist_entry_obj(p));
>-        qobj = qdict_get(minfo, "name");
>-        qstr = qobject_to(QString, qobj);
>-        name = qstring_get_str(qstr);
>-
>-        qobj = qdict_get(minfo, "abstract");
>-        if (qobj) {
>-            qbool = qobject_to(QBool, qobj);
>-            abstract = qbool_get_bool(qbool);
>-        } else {
>-            abstract = false;
>-        }
>-
>-        apply_to_node(name, is_machine, abstract);
>-        qobj = qdict_get(minfo, "alias");
>-        if (qobj) {
>-            qstr = qobject_to(QString, qobj);
>-            name = qstring_get_str(qstr);
>-            apply_to_node(name, is_machine, abstract);
>-        }
>-    }
>-}
>
> /**
>  * qos_set_machines_devices_available(): sets availability of qgraph
>@@ -129,10 +75,6 @@ static void qos_set_machines_devices_available(void)
>     qobject_unref(response);
> }
>
>-static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
>-{
>-    return obj->get_driver(obj, "memory");
>-}
>
> static void restart_qemu_or_continue(char *path)
> {
>@@ -159,78 +101,6 @@ void qos_invalidate_command_line(void)
>     old_path = NULL;
> }
>
>-/**
>- * allocate_objects(): given an array of nodes @arg,
>- * walks the path invoking all constructors and
>- * passing the corresponding parameter in order to
>- * continue the objects allocation.
>- * Once the test is reached, return the object it consumes.
>- *
>- * Since the machine and QEDGE_CONSUMED_BY nodes allocate
>- * memory in the constructor, g_test_queue_destroy is used so
>- * that after execution they can be safely free'd.  (The test's
>- * ->before callback is also welcome to use g_test_queue_destroy).
>- *
>- * Note: as specified in walk_path() too, @arg is an array of
>- * char *, where arg[0] is a pointer to the command line
>- * string that will be used to properly start QEMU when executing
>- * the test, and the remaining elements represent the actual objects
>- * that will be allocated.
>- */
>-static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
>-{
>-    int current = 0;
>-    QGuestAllocator *alloc;
>-    QOSGraphObject *parent = NULL;
>-    QOSGraphEdge *edge;
>-    QOSGraphNode *node;
>-    void *edge_arg;
>-    void *obj;
>-
>-    node = qos_graph_get_node(path[current]);
>-    g_assert(node->type == QNODE_MACHINE);
>-
>-    obj = qos_machine_new(node, qts);
>-    qos_object_queue_destroy(obj);
>-
>-    alloc = get_machine_allocator(obj);
>-    if (p_alloc) {
>-        *p_alloc = alloc;
>-    }
>-
>-    for (;;) {
>-        if (node->type != QNODE_INTERFACE) {
>-            qos_object_start_hw(obj);
>-            parent = obj;
>-        }
>-
>-        /* follow edge and get object for next node constructor */
>-        current++;
>-        edge = qos_graph_get_edge(path[current - 1], path[current]);
>-        node = qos_graph_get_node(path[current]);
>-
>-        if (node->type == QNODE_TEST) {
>-            g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
>-            return obj;
>-        }
>-
>-        switch (qos_graph_edge_get_type(edge)) {
>-        case QEDGE_PRODUCES:
>-            obj = parent->get_driver(parent, path[current]);
>-            break;
>-
>-        case QEDGE_CONSUMED_BY:
>-            edge_arg = qos_graph_edge_get_arg(edge);
>-            obj = qos_driver_new(node, obj, alloc, edge_arg);
>-            qos_object_queue_destroy(obj);
>-            break;
>-
>-        case QEDGE_CONTAINS:
>-            obj = parent->get_device(parent, path[current]);
>-            break;
>-        }
>-    }
>-}
>
> /* The argument to run_one_test, which is the test function that is registered
>  * with GTest, is a vector of strings.  The first item is the initial command
>-- 
>2.23.0
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 19/21] fuzz: add virtio-net fuzz target
  2020-01-29  5:34 ` [PATCH v8 19/21] fuzz: add virtio-net fuzz target Bulekov, Alexander
  2020-01-30 14:43   ` Stefan Hajnoczi
@ 2020-02-05 13:57   ` Darren Kenny
  2020-02-06 14:51     ` Alexander Bulekov
  1 sibling, 1 reply; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:57 UTC (permalink / raw)
  To: Bulekov, Alexander
  Cc: Laurent Vivier, Thomas Huth, qemu-devel, bsd, stefanha, pbonzini

On Wed, Jan 29, 2020 at 05:34:27AM +0000, Bulekov, Alexander wrote:
>The virtio-net fuzz target feeds inputs to all three virtio-net
>virtqueues, and uses forking to avoid leaking state between fuzz runs.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>---
> tests/qtest/fuzz/Makefile.include  |   1 +
> tests/qtest/fuzz/virtio_net_fuzz.c | 195 +++++++++++++++++++++++++++++
> 2 files changed, 196 insertions(+)
> create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c
>
>diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
>index 38b8cdd9f1..77385777ef 100644
>--- a/tests/qtest/fuzz/Makefile.include
>+++ b/tests/qtest/fuzz/Makefile.include
>@@ -8,6 +8,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
>
> # Targets
> fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
>+fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
>
> FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
>
>diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c
>new file mode 100644
>index 0000000000..e4e3e50865
>--- /dev/null
>+++ b/tests/qtest/fuzz/virtio_net_fuzz.c
>@@ -0,0 +1,195 @@
>+/*
>+ * virtio-net Fuzzing Target
>+ *
>+ * Copyright Red Hat Inc., 2019
>+ *
>+ * Authors:
>+ *  Alexander Bulekov   <alxndr@bu.edu>
>+ *
>+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
>+ * See the COPYING file in the top-level directory.
>+ */
>+
>+#include "qemu/osdep.h"
>+
>+#include "standard-headers/linux/virtio_config.h"
>+#include "tests/qtest/libqtest.h"
>+#include "tests/qtest/libqos/virtio-net.h"
>+#include "fuzz.h"
>+#include "fork_fuzz.h"
>+#include "qos_fuzz.h"
>+
>+
>+#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
>+#define QVIRTIO_RX_VQ 0
>+#define QVIRTIO_TX_VQ 1
>+#define QVIRTIO_CTRL_VQ 2
>+
>+static int sockfds[2];
>+static bool sockfds_initialized;
>+
>+static void virtio_net_fuzz_multi(QTestState *s,
>+        const unsigned char *Data, size_t Size, bool check_used)
>+{
>+    typedef struct vq_action {
>+        uint8_t queue;
>+        uint8_t length;
>+        uint8_t write;
>+        uint8_t next;
>+        uint8_t rx;
>+    } vq_action;
>+
>+    uint32_t free_head = 0;
>+
>+    QGuestAllocator *t_alloc = fuzz_qos_alloc;
>+
>+    QVirtioNet *net_if = fuzz_qos_obj;
>+    QVirtioDevice *dev = net_if->vdev;
>+    QVirtQueue *q;
>+    vq_action vqa;
>+    while (Size >= sizeof(vqa)) {
>+        memcpy(&vqa, Data, sizeof(vqa));
>+        Data += sizeof(vqa);
>+        Size -= sizeof(vqa);
>+
>+        q = net_if->queues[vqa.queue % 3];
>+
>+        vqa.length = vqa.length >= Size ? Size :  vqa.length;
>+
>+        /*
>+         * Only attempt to write incoming packets, when using the socket
>+         * backend. Otherwise, always place the input on a virtqueue.
>+         */
>+        if (vqa.rx && sockfds_initialized) {
>+            write(sockfds[0], Data, vqa.length);
>+        } else {
>+            vqa.rx = 0;
>+            uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
>+            /*
>+             * If checking used ring, ensure that the fuzzer doesn't trigger
>+             * trivial asserion failure on zero-zied buffer
>+             */
>+            qtest_memwrite(s, req_addr, Data, vqa.length);
>+
>+
>+            free_head = qvirtqueue_add(s, q, req_addr, vqa.length,
>+                    vqa.write, vqa.next);
>+            qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
>+            qvirtqueue_kick(s, dev, q, free_head);
>+        }
>+
>+        /*
>+         * normally, we could just use qvirtio_wait_used_elem, but since we
>+         * must manually run the main-loop for all the bhs to run, we use
>+         * this hack with flush_events(), to run the main_loop
>+         */
>+        gint64 start_time = g_get_monotonic_time();

NIT: It's a little unclear using a for(;;) when in reality, for most
use-cases, other than virtio_net_fork_fuzz_check_used() it will
never really loop around.

Maybe it would be clearer if the parts that never repeat were
outside the loop? e.g.

    /* Run the main loop */
    qtest_clock_step(s, 100);
    flush_events(s);

    /* Poll the used vring only if we added to the TX or CTRL vq */
    while (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
        uint32_t got_desc_idx;
        /* Input led to a virtio_error */
        if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
            break;
        }
        if (dev->bus->get_queue_isr_status(dev, q) &&
                qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
            g_assert_cmpint(got_desc_idx, ==, free_head);
            break;
        }
        g_assert(g_get_monotonic_time() - start_time
                 <= QVIRTIO_NET_TIMEOUT_US);

        /* Run the main loop */
        qtest_clock_step(s, 100);
        flush_events(s);
    }

Unless I'm reading it incorrectly that should do the same thing.

Thanks,

Darren.

>+        for (;;) {
>+            /* Run the main loop */
>+            qtest_clock_step(s, 100);
>+            flush_events(s);
>+            /* Poll the used vring only if we added to the TX or CTRL vq */
>+            if (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
>+                uint32_t got_desc_idx;
>+                /* Input led to a virtio_error */
>+                if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
>+                    break;
>+                }
>+                if (dev->bus->get_queue_isr_status(dev, q) &&
>+                        qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
>+                    g_assert_cmpint(got_desc_idx, ==, free_head);
>+                    break;
>+                }
>+                g_assert(g_get_monotonic_time() - start_time
>+                         <= QVIRTIO_NET_TIMEOUT_US);
>+            } else {
>+                break;
>+            }
>+        }
>+        Data += vqa.length;
>+        Size -= vqa.length;
>+    }
>+}
>+
>+static void virtio_net_fork_fuzz(QTestState *s,
>+        const unsigned char *Data, size_t Size)
>+{
>+    if (fork() == 0) {
>+        virtio_net_fuzz_multi(s, Data, Size, false);
>+        flush_events(s);
>+        _Exit(0);
>+    } else {
>+        wait(NULL);
>+    }
>+}
>+
>+static void virtio_net_fork_fuzz_check_used(QTestState *s,
>+        const unsigned char *Data, size_t Size)
>+{
>+    if (fork() == 0) {
>+        virtio_net_fuzz_multi(s, Data, Size, true);
>+        flush_events(s);
>+        _Exit(0);
>+    } else {
>+        wait(NULL);
>+    }
>+}
>+
>+static void virtio_net_pre_fuzz(QTestState *s)
>+{
>+    qos_init_path(s);
>+    counter_shm_init();
>+}
>+
>+static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg)
>+{
>+    int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds);
>+    g_assert_cmpint(ret, !=, -1);
>+    fcntl(sockfds[0], F_SETFL, O_NONBLOCK);
>+    sockfds_initialized = true;
>+    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
>+                           sockfds[1]);
>+    return arg;
>+}
>+
>+static void *virtio_net_test_setup_user(GString *cmd_line, void *arg)
>+{
>+    g_string_append_printf(cmd_line, " -netdev user,id=hs0 ");
>+    return arg;
>+}
>+
>+static void register_virtio_net_fuzz_targets(void)
>+{
>+    fuzz_add_qos_target(&(FuzzTarget){
>+            .name = "virtio-net-socket",
>+            .description = "Fuzz the virtio-net virtual queues. Fuzz incoming "
>+            "traffic using the socket backend",
>+            .pre_fuzz = &virtio_net_pre_fuzz,
>+            .fuzz = virtio_net_fork_fuzz,},
>+            "virtio-net",
>+            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
>+            );
>+
>+    fuzz_add_qos_target(&(FuzzTarget){
>+            .name = "virtio-net-socket-check-used",
>+            .description = "Fuzz the virtio-net virtual queues. Wait for the "
>+            "descriptors to be used. Timeout may indicate improperly handled "
>+            "input",
>+            .pre_fuzz = &virtio_net_pre_fuzz,
>+            .fuzz = virtio_net_fork_fuzz_check_used,},
>+            "virtio-net",
>+            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
>+            );
>+    fuzz_add_qos_target(&(FuzzTarget){
>+            .name = "virtio-net-slirp",
>+            .description = "Fuzz the virtio-net virtual queues with the slirp "
>+            " backend. Warning: May result in network traffic emitted from the "
>+            " process. Run in an isolated network environment.",

NIT: s/" /"/    (fix double-spaces)

>+            .pre_fuzz = &virtio_net_pre_fuzz,
>+            .fuzz = virtio_net_fork_fuzz,},
>+            "virtio-net",
>+            &(QOSGraphTestOptions){.before = virtio_net_test_setup_user}
>+            );
>+}
>+
>+fuzz_target_init(register_virtio_net_fuzz_targets);
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-29  5:34 ` [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest Bulekov, Alexander
  2020-01-30 14:42   ` Stefan Hajnoczi
@ 2020-02-05 13:58   ` Darren Kenny
  2020-06-18  7:34   ` Thomas Huth
  2 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 13:58 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, stefanha

On Wed, Jan 29, 2020 at 05:34:22AM +0000, Bulekov, Alexander wrote:
>The qtest-based fuzzer makes use of forking to reset-state between
>tests. Keep the callback enabled, so the call_rcu thread gets created
>within the child process.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> vl.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
>diff --git a/vl.c b/vl.c
>index bb77935f04..cf8e2d3ebb 100644
>--- a/vl.c
>+++ b/vl.c
>@@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
>     set_memory_options(&ram_slots, &maxram_size, machine_class);
>
>     os_daemonize();
>-    rcu_disable_atfork();
>+
>+    /*
>+     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
>+     * may be forked testing purposes (e.g. fork-server based fuzzing)
>+     */
>+    if (!qtest_enabled()) {
>+        rcu_disable_atfork();
>+    }
>
>     if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
>         error_reportf_err(err, "cannot create PID file: ");
>-- 
>2.23.0
>
>


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c
  2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
  2020-01-30 14:39   ` Stefan Hajnoczi
  2020-01-30 15:06   ` Alex Bennée
@ 2020-02-05 14:02   ` Darren Kenny
  2 siblings, 0 replies; 55+ messages in thread
From: Darren Kenny @ 2020-02-05 14:02 UTC (permalink / raw)
  To: Bulekov, Alexander; +Cc: pbonzini, bsd, qemu-devel, stefanha

On Wed, Jan 29, 2020 at 05:34:11AM +0000, Bulekov, Alexander wrote:
>A program might rely on functions implemented in vl.c, but implement its
>own main(). By placing main into a separate source file, there are no
>complaints about duplicate main()s when linking against vl.o. For
>example, the virtual-device fuzzer uses a main() provided by libfuzzer,
>and needs to perform some initialization before running the softmmu
>initialization. Now, main simply calls three vl.c functions which
>handle the guest initialization, main loop and cleanup.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
>---
> Makefile                |  1 +
> Makefile.objs           |  2 ++
> Makefile.target         |  2 +-
> include/sysemu/sysemu.h |  4 ++++
> main.c                  | 53 +++++++++++++++++++++++++++++++++++++++++
> vl.c                    | 36 +++++++---------------------
> 6 files changed, 70 insertions(+), 28 deletions(-)
> create mode 100644 main.c
>
>diff --git a/Makefile b/Makefile
>index 32bd554480..e6de7a47bb 100644
>--- a/Makefile
>+++ b/Makefile
>@@ -473,6 +473,7 @@ $(SOFTMMU_ALL_RULES): $(chardev-obj-y)
> $(SOFTMMU_ALL_RULES): $(crypto-obj-y)
> $(SOFTMMU_ALL_RULES): $(io-obj-y)
> $(SOFTMMU_ALL_RULES): config-all-devices.mak
>+$(SOFTMMU_ALL_RULES): $(softmmu-main-y)
> ifdef DECOMPRESS_EDK2_BLOBS
> $(SOFTMMU_ALL_RULES): $(edk2-decompressed)
> endif
>diff --git a/Makefile.objs b/Makefile.objs
>index 7c1e50f9d6..5ab166fed5 100644
>--- a/Makefile.objs
>+++ b/Makefile.objs
>@@ -84,6 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
> # qapi
>
> common-obj-y += qapi/
>+
>+softmmu-obj-y = main.o
> endif
>
> #######################################################################
>diff --git a/Makefile.target b/Makefile.target
>index 6e61f607b1..8dcf3dddd8 100644
>--- a/Makefile.target
>+++ b/Makefile.target
>@@ -202,7 +202,7 @@ endif
> COMMON_LDADDS = ../libqemuutil.a
>
> # build either PROG or PROGW
>-$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
>+$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-obj-y)
> 	$(call LINK, $(filter-out %.mak, $^))
> ifdef CONFIG_DARWIN
> 	$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
>diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
>index 80c57fdc4e..270df5fa34 100644
>--- a/include/sysemu/sysemu.h
>+++ b/include/sysemu/sysemu.h
>@@ -118,6 +118,10 @@ QemuOpts *qemu_get_machine_opts(void);
>
> bool defaults_enabled(void);
>
>+void qemu_init(int argc, char **argv, char **envp);
>+void qemu_main_loop(void);
>+void qemu_cleanup(void);
>+
> extern QemuOptsList qemu_legacy_drive_opts;
> extern QemuOptsList qemu_common_drive_opts;
> extern QemuOptsList qemu_drive_opts;
>diff --git a/main.c b/main.c
>new file mode 100644
>index 0000000000..f10ceda541
>--- /dev/null
>+++ b/main.c
>@@ -0,0 +1,53 @@
>+/*
>+ * QEMU System Emulator
>+ *
>+ * Copyright (c) 2003-2008 Fabrice Bellard

I don't know the rules but, maybe that should also be extended to
2019/2020 since this is a new file.

Otherwise,

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

Thanks,

Darren.


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 18/21] fuzz: add i440fx fuzz targets
  2020-02-05 13:26   ` Darren Kenny
@ 2020-02-06 14:47     ` Alexander Bulekov
  0 siblings, 0 replies; 55+ messages in thread
From: Alexander Bulekov @ 2020-02-06 14:47 UTC (permalink / raw)
  To: qemu-devel, Laurent Vivier, Thomas Huth, bsd, stefanha, pbonzini

On 200205 1326, Darren Kenny wrote:
> On Wed, Jan 29, 2020 at 05:34:26AM +0000, Bulekov, Alexander wrote:
> > These three targets should simply fuzz reads/writes to a couple ioports,
> > but they mostly serve as examples of different ways to write targets.
> > They demonstrate using qtest and qos for fuzzing, as well as using
> > rebooting and forking to reset state, or not resetting it at all.
> > 
> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> > Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> 
> Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
> 
> A couple of nit below w.r.t. commenting on how the fuzzed data is
> being processed.
> 
> > ---
> > tests/qtest/fuzz/Makefile.include |   3 +
> > tests/qtest/fuzz/i440fx_fuzz.c    | 178 ++++++++++++++++++++++++++++++
> > 2 files changed, 181 insertions(+)
> > create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c
> > 
> > diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
> > index e3bdd33ff4..38b8cdd9f1 100644
> > --- a/tests/qtest/fuzz/Makefile.include
> > +++ b/tests/qtest/fuzz/Makefile.include
> > @@ -6,6 +6,9 @@ fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
> > fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
> > fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
> > 
> > +# Targets
> > +fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
> > +
> > FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
> > 
> > # Linker Script to force coverage-counters into known regions which we can mark
> > diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c
> > new file mode 100644
> > index 0000000000..c7791182b8
> > --- /dev/null
> > +++ b/tests/qtest/fuzz/i440fx_fuzz.c
> > @@ -0,0 +1,178 @@
> > +/*
> > + * I440FX Fuzzing Target
> > + *
> > + * Copyright Red Hat Inc., 2019
> > + *
> > + * Authors:
> > + *  Alexander Bulekov   <alxndr@bu.edu>
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +
> > +#include "qemu/main-loop.h"
> > +#include "tests/qtest/libqtest.h"
> > +#include "tests/qtest/libqos/pci.h"
> > +#include "tests/qtest/libqos/pci-pc.h"
> > +#include "fuzz.h"
> > +#include "fuzz/qos_fuzz.h"
> > +#include "fuzz/fork_fuzz.h"
> > +
> > +
> > +#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
> > +#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
> > +
> > +enum action_id {
> > +    WRITEB,
> > +    WRITEW,
> > +    WRITEL,
> > +    READB,
> > +    READW,
> > +    READL,
> > +    ACTION_MAX
> > +};
> > +
> 
> While it eventually becomes clear what is happening in these
> functions, it does take several attempts at reading it to understand
> what is going on.
> 
> For that reason, it might be worth a couple of comments for future
> maintainers as to what is going on.
Yes - I haven't touched this target in a while. It could definitely
use some comments. Since the port-io input space isn't enormous, maybe
it makes sends to fuzz all of port-io, instead of just the I440FX
addresses.
-Alex

> Thanks,
> 
> Darren.
> 
> > +static void i440fx_fuzz_qtest(QTestState *s,
> > +        const unsigned char *Data, size_t Size) {
> > +    typedef struct QTestFuzzAction {
> > +        uint32_t value;
> > +        uint8_t id;
> > +        uint8_t addr;
> > +    } QTestFuzzAction;
> > +    QTestFuzzAction a;
> > +
> > +    while (Size >= sizeof(a)) {
> > +        memcpy(&a, Data, sizeof(a));
> > +        uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
> > +                                      I440FX_PCI_HOST_BRIDGE_DATA;
> > +        switch (a.id % ACTION_MAX) {
> > +        case WRITEB:
> > +            qtest_outb(s, addr, (uint8_t)a.value);
> > +            break;
> > +        case WRITEW:
> > +            qtest_outw(s, addr, (uint16_t)a.value);
> > +            break;
> > +        case WRITEL:
> > +            qtest_outl(s, addr, (uint32_t)a.value);
> > +            break;
> > +        case READB:
> > +            qtest_inb(s, addr);
> > +            break;
> > +        case READW:
> > +            qtest_inw(s, addr);
> > +            break;
> > +        case READL:
> > +            qtest_inl(s, addr);
> > +            break;
> > +        }
> > +        Size -= sizeof(a);
> > +        Data += sizeof(a);
> > +    }
> > +    flush_events(s);
> > +}
> > +
> > +static void i440fx_fuzz_qos(QTestState *s,
> > +        const unsigned char *Data, size_t Size) {
> > +
> > +    typedef struct QOSFuzzAction {
> > +        uint32_t value;
> > +        int devfn;
> > +        uint8_t offset;
> > +        uint8_t id;
> > +    } QOSFuzzAction;
> > +
> > +    static QPCIBus *bus;
> > +    if (!bus) {
> > +        bus = qpci_new_pc(s, fuzz_qos_alloc);
> > +    }
> > +
> > +    QOSFuzzAction a;
> > +    while (Size >= sizeof(a)) {
> > +        memcpy(&a, Data, sizeof(a));
> > +        switch (a.id % ACTION_MAX) {
> > +        case WRITEB:
> > +            bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
> > +            break;
> > +        case WRITEW:
> > +            bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
> > +            break;
> > +        case WRITEL:
> > +            bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
> > +            break;
> > +        case READB:
> > +            bus->config_readb(bus, a.devfn, a.offset);
> > +            break;
> > +        case READW:
> > +            bus->config_readw(bus, a.devfn, a.offset);
> > +            break;
> > +        case READL:
> > +            bus->config_readl(bus, a.devfn, a.offset);
> > +            break;
> > +        }
> > +        Size -= sizeof(a);
> > +        Data += sizeof(a);
> > +    }
> > +    flush_events(s);
> > +}
> > +
> > +static void i440fx_fuzz_qos_fork(QTestState *s,
> > +        const unsigned char *Data, size_t Size) {
> > +    if (fork() == 0) {
> > +        i440fx_fuzz_qos(s, Data, Size);
> > +        _Exit(0);
> > +    } else {
> > +        wait(NULL);
> > +    }
> > +}
> > +
> > +static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
> > +                                       "-m 0 -display none";
> > +static const char *i440fx_argv(FuzzTarget *t)
> > +{
> > +    return i440fx_qtest_argv;
> > +}
> > +
> > +static void fork_init(void)
> > +{
> > +    counter_shm_init();
> > +}
> > +
> > +static void register_pci_fuzz_targets(void)
> > +{
> > +    /* Uses simple qtest commands and reboots to reset state */
> > +    fuzz_add_target(&(FuzzTarget){
> > +                .name = "i440fx-qtest-reboot-fuzz",
> > +                .description = "Fuzz the i440fx using raw qtest commands and"
> > +                               "rebooting after each run",
> > +                .get_init_cmdline = i440fx_argv,
> > +                .fuzz = i440fx_fuzz_qtest});
> > +
> > +    /* Uses libqos and forks to prevent state leakage */
> > +    fuzz_add_qos_target(&(FuzzTarget){
> > +                .name = "i440fx-qos-fork-fuzz",
> > +                .description = "Fuzz the i440fx using raw qtest commands and"
> > +                               "rebooting after each run",
> > +                .pre_vm_init = &fork_init,
> > +                .fuzz = i440fx_fuzz_qos_fork,},
> > +                "i440FX-pcihost",
> > +                &(QOSGraphTestOptions){}
> > +                );
> > +
> > +    /*
> > +     * Uses libqos. Doesn't do anything to reset state. Note that if we were to
> > +     * reboot after each run, we would also have to redo the qos-related
> > +     * initialization (qos_init_path)
> > +     */
> > +    fuzz_add_qos_target(&(FuzzTarget){
> > +                .name = "i440fx-qos-noreset-fuzz",
> > +                .description = "Fuzz the i440fx using raw qtest commands and"
> > +                               "rebooting after each run",
> > +                .fuzz = i440fx_fuzz_qos,},
> > +                "i440FX-pcihost",
> > +                &(QOSGraphTestOptions){}
> > +                );
> > +}
> > +
> > +fuzz_target_init(register_pci_fuzz_targets);
> > -- 
> > 2.23.0
> > 
> > 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 19/21] fuzz: add virtio-net fuzz target
  2020-02-05 13:57   ` Darren Kenny
@ 2020-02-06 14:51     ` Alexander Bulekov
  0 siblings, 0 replies; 55+ messages in thread
From: Alexander Bulekov @ 2020-02-06 14:51 UTC (permalink / raw)
  To: qemu-devel, Laurent Vivier, Thomas Huth, bsd, stefanha, pbonzini

On 200205 1357, Darren Kenny wrote:
> On Wed, Jan 29, 2020 at 05:34:27AM +0000, Bulekov, Alexander wrote:
> > The virtio-net fuzz target feeds inputs to all three virtio-net
> > virtqueues, and uses forking to avoid leaking state between fuzz runs.
> > 
> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> > ---
> > tests/qtest/fuzz/Makefile.include  |   1 +
> > tests/qtest/fuzz/virtio_net_fuzz.c | 195 +++++++++++++++++++++++++++++
> > 2 files changed, 196 insertions(+)
> > create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c
> > 
> > diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
> > index 38b8cdd9f1..77385777ef 100644
> > --- a/tests/qtest/fuzz/Makefile.include
> > +++ b/tests/qtest/fuzz/Makefile.include
> > @@ -8,6 +8,7 @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
> > 
> > # Targets
> > fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
> > +fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
> > 
> > FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
> > 
> > diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c
> > new file mode 100644
> > index 0000000000..e4e3e50865
> > --- /dev/null
> > +++ b/tests/qtest/fuzz/virtio_net_fuzz.c
> > @@ -0,0 +1,195 @@
> > +/*
> > + * virtio-net Fuzzing Target
> > + *
> > + * Copyright Red Hat Inc., 2019
> > + *
> > + * Authors:
> > + *  Alexander Bulekov   <alxndr@bu.edu>
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +
> > +#include "standard-headers/linux/virtio_config.h"
> > +#include "tests/qtest/libqtest.h"
> > +#include "tests/qtest/libqos/virtio-net.h"
> > +#include "fuzz.h"
> > +#include "fork_fuzz.h"
> > +#include "qos_fuzz.h"
> > +
> > +
> > +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
> > +#define QVIRTIO_RX_VQ 0
> > +#define QVIRTIO_TX_VQ 1
> > +#define QVIRTIO_CTRL_VQ 2
> > +
> > +static int sockfds[2];
> > +static bool sockfds_initialized;
> > +
> > +static void virtio_net_fuzz_multi(QTestState *s,
> > +        const unsigned char *Data, size_t Size, bool check_used)
> > +{
> > +    typedef struct vq_action {
> > +        uint8_t queue;
> > +        uint8_t length;
> > +        uint8_t write;
> > +        uint8_t next;
> > +        uint8_t rx;
> > +    } vq_action;
> > +
> > +    uint32_t free_head = 0;
> > +
> > +    QGuestAllocator *t_alloc = fuzz_qos_alloc;
> > +
> > +    QVirtioNet *net_if = fuzz_qos_obj;
> > +    QVirtioDevice *dev = net_if->vdev;
> > +    QVirtQueue *q;
> > +    vq_action vqa;
> > +    while (Size >= sizeof(vqa)) {
> > +        memcpy(&vqa, Data, sizeof(vqa));
> > +        Data += sizeof(vqa);
> > +        Size -= sizeof(vqa);
> > +
> > +        q = net_if->queues[vqa.queue % 3];
> > +
> > +        vqa.length = vqa.length >= Size ? Size :  vqa.length;
> > +
> > +        /*
> > +         * Only attempt to write incoming packets, when using the socket
> > +         * backend. Otherwise, always place the input on a virtqueue.
> > +         */
> > +        if (vqa.rx && sockfds_initialized) {
> > +            write(sockfds[0], Data, vqa.length);
> > +        } else {
> > +            vqa.rx = 0;
> > +            uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
> > +            /*
> > +             * If checking used ring, ensure that the fuzzer doesn't trigger
> > +             * trivial asserion failure on zero-zied buffer
> > +             */
> > +            qtest_memwrite(s, req_addr, Data, vqa.length);
> > +
> > +
> > +            free_head = qvirtqueue_add(s, q, req_addr, vqa.length,
> > +                    vqa.write, vqa.next);
> > +            qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
> > +            qvirtqueue_kick(s, dev, q, free_head);
> > +        }
> > +
> > +        /*
> > +         * normally, we could just use qvirtio_wait_used_elem, but since we
> > +         * must manually run the main-loop for all the bhs to run, we use
> > +         * this hack with flush_events(), to run the main_loop
> > +         */
> > +        gint64 start_time = g_get_monotonic_time();
> 
> NIT: It's a little unclear using a for(;;) when in reality, for most
> use-cases, other than virtio_net_fork_fuzz_check_used() it will
> never really loop around.
> 
> Maybe it would be clearer if the parts that never repeat were
> outside the loop? e.g.
> 
>    /* Run the main loop */
>    qtest_clock_step(s, 100);
>    flush_events(s);
> 
>    /* Poll the used vring only if we added to the TX or CTRL vq */
>    while (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
>        uint32_t got_desc_idx;
>        /* Input led to a virtio_error */
>        if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
>            break;
>        }
>        if (dev->bus->get_queue_isr_status(dev, q) &&
>                qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
>            g_assert_cmpint(got_desc_idx, ==, free_head);
>            break;
>        }
>        g_assert(g_get_monotonic_time() - start_time
>                 <= QVIRTIO_NET_TIMEOUT_US);
> 
>        /* Run the main loop */
>        qtest_clock_step(s, 100);
>        flush_events(s);
>    }
> 
> Unless I'm reading it incorrectly that should do the same thing.
This is more readable and should do the same thing. I'll change it.
-Alex
> Thanks,
> 
> Darren.
> 
> > +        for (;;) {
> > +            /* Run the main loop */
> > +            qtest_clock_step(s, 100);
> > +            flush_events(s);
> > +            /* Poll the used vring only if we added to the TX or CTRL vq */
> > +            if (check_used && !vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
> > +                uint32_t got_desc_idx;
> > +                /* Input led to a virtio_error */
> > +                if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
> > +                    break;
> > +                }
> > +                if (dev->bus->get_queue_isr_status(dev, q) &&
> > +                        qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
> > +                    g_assert_cmpint(got_desc_idx, ==, free_head);
> > +                    break;
> > +                }
> > +                g_assert(g_get_monotonic_time() - start_time
> > +                         <= QVIRTIO_NET_TIMEOUT_US);
> > +            } else {
> > +                break;
> > +            }
> > +        }
> > +        Data += vqa.length;
> > +        Size -= vqa.length;
> > +    }
> > +}
> > +
> > +static void virtio_net_fork_fuzz(QTestState *s,
> > +        const unsigned char *Data, size_t Size)
> > +{
> > +    if (fork() == 0) {
> > +        virtio_net_fuzz_multi(s, Data, Size, false);
> > +        flush_events(s);
> > +        _Exit(0);
> > +    } else {
> > +        wait(NULL);
> > +    }
> > +}
> > +
> > +static void virtio_net_fork_fuzz_check_used(QTestState *s,
> > +        const unsigned char *Data, size_t Size)
> > +{
> > +    if (fork() == 0) {
> > +        virtio_net_fuzz_multi(s, Data, Size, true);
> > +        flush_events(s);
> > +        _Exit(0);
> > +    } else {
> > +        wait(NULL);
> > +    }
> > +}
> > +
> > +static void virtio_net_pre_fuzz(QTestState *s)
> > +{
> > +    qos_init_path(s);
> > +    counter_shm_init();
> > +}
> > +
> > +static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg)
> > +{
> > +    int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds);
> > +    g_assert_cmpint(ret, !=, -1);
> > +    fcntl(sockfds[0], F_SETFL, O_NONBLOCK);
> > +    sockfds_initialized = true;
> > +    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
> > +                           sockfds[1]);
> > +    return arg;
> > +}
> > +
> > +static void *virtio_net_test_setup_user(GString *cmd_line, void *arg)
> > +{
> > +    g_string_append_printf(cmd_line, " -netdev user,id=hs0 ");
> > +    return arg;
> > +}
> > +
> > +static void register_virtio_net_fuzz_targets(void)
> > +{
> > +    fuzz_add_qos_target(&(FuzzTarget){
> > +            .name = "virtio-net-socket",
> > +            .description = "Fuzz the virtio-net virtual queues. Fuzz incoming "
> > +            "traffic using the socket backend",
> > +            .pre_fuzz = &virtio_net_pre_fuzz,
> > +            .fuzz = virtio_net_fork_fuzz,},
> > +            "virtio-net",
> > +            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
> > +            );
> > +
> > +    fuzz_add_qos_target(&(FuzzTarget){
> > +            .name = "virtio-net-socket-check-used",
> > +            .description = "Fuzz the virtio-net virtual queues. Wait for the "
> > +            "descriptors to be used. Timeout may indicate improperly handled "
> > +            "input",
> > +            .pre_fuzz = &virtio_net_pre_fuzz,
> > +            .fuzz = virtio_net_fork_fuzz_check_used,},
> > +            "virtio-net",
> > +            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
> > +            );
> > +    fuzz_add_qos_target(&(FuzzTarget){
> > +            .name = "virtio-net-slirp",
> > +            .description = "Fuzz the virtio-net virtual queues with the slirp "
> > +            " backend. Warning: May result in network traffic emitted from the "
> > +            " process. Run in an isolated network environment.",
> 
> NIT: s/" /"/    (fix double-spaces)
> 
> > +            .pre_fuzz = &virtio_net_pre_fuzz,
> > +            .fuzz = virtio_net_fork_fuzz,},
> > +            "virtio-net",
> > +            &(QOSGraphTestOptions){.before = virtio_net_test_setup_user}
> > +            );
> > +}
> > +
> > +fuzz_target_init(register_virtio_net_fuzz_targets);
> > -- 
> > 2.23.0
> > 
> > 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets
  2020-02-05 13:18   ` Darren Kenny
@ 2020-02-11 16:19     ` Alexander Bulekov
  0 siblings, 0 replies; 55+ messages in thread
From: Alexander Bulekov @ 2020-02-11 16:19 UTC (permalink / raw)
  To: qemu-devel, Laurent Vivier, Thomas Huth, bsd, stefanha, pbonzini

On 200205 1318, Darren Kenny wrote:
> On Wed, Jan 29, 2020 at 05:34:24AM +0000, Bulekov, Alexander wrote:
> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> > Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
> > ---
> > <snip>
> > +    return allocate_objects(qts, current_path + 1, p_alloc);
> > +}
> > +
> > +static const char *qos_build_main_args(void)
> > +{
> > +    char **path = fuzz_path_vec;
> 
> Is it possible that fuzz_path_vec is not valid here? Specifically,
> how likely is it that walk_path() won't ever set it, or that it
> results in a possible previous value being used since we don't reset
> it before calling qos_graph_foreach_test_path() in
> qos_get_cmdline().

Hi Darren,
Maybe this can happen if the target doesn't support a particular device.
I added a check
-Alex
> Thanks,
> 
> Darren.
> 


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-01-29  5:34 ` [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest Bulekov, Alexander
  2020-01-30 14:42   ` Stefan Hajnoczi
  2020-02-05 13:58   ` Darren Kenny
@ 2020-06-18  7:34   ` Thomas Huth
  2020-06-18 15:08     ` Alexander Bulekov
  2 siblings, 1 reply; 55+ messages in thread
From: Thomas Huth @ 2020-06-18  7:34 UTC (permalink / raw)
  To: Bulekov, Alexander, qemu-devel; +Cc: pbonzini, bsd, stefanha

On 29/01/2020 06.34, Bulekov, Alexander wrote:
> The qtest-based fuzzer makes use of forking to reset-state between
> tests. Keep the callback enabled, so the call_rcu thread gets created
> within the child process.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
>  vl.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/vl.c b/vl.c
> index bb77935f04..cf8e2d3ebb 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
>      set_memory_options(&ram_slots, &maxram_size, machine_class);
>  
>      os_daemonize();
> -    rcu_disable_atfork();
> +
> +    /*
> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> +     */
> +    if (!qtest_enabled()) {
> +        rcu_disable_atfork();
> +    }
>  
>      if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
>          error_reportf_err(err, "cannot create PID file: ");

 Hi Alexander,

I think this patch might maybe not work as expected: The qtest_enabled()
has been added before configure_accelerators() is called in main(). So
qtest_enabled() should always return "false" and thus
rcu_disabled_fork() is still called in any case... could you please
double-check whether it works for you and I just made a mistake, or
whether this is a bug indeed?

 Thanks,
  Thomas



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest
  2020-06-18  7:34   ` Thomas Huth
@ 2020-06-18 15:08     ` Alexander Bulekov
  0 siblings, 0 replies; 55+ messages in thread
From: Alexander Bulekov @ 2020-06-18 15:08 UTC (permalink / raw)
  To: Thomas Huth; +Cc: pbonzini, bsd, qemu-devel, stefanha

On 200618 0934, Thomas Huth wrote:
> On 29/01/2020 06.34, Bulekov, Alexander wrote:
> > The qtest-based fuzzer makes use of forking to reset-state between
> > tests. Keep the callback enabled, so the call_rcu thread gets created
> > within the child process.
> > 
> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> > ---
> >  vl.c | 9 ++++++++-
> >  1 file changed, 8 insertions(+), 1 deletion(-)
> > 
> > diff --git a/vl.c b/vl.c
> > index bb77935f04..cf8e2d3ebb 100644
> > --- a/vl.c
> > +++ b/vl.c
> > @@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
> >      set_memory_options(&ram_slots, &maxram_size, machine_class);
> >  
> >      os_daemonize();
> > -    rcu_disable_atfork();
> > +
> > +    /*
> > +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> > +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> > +     */
> > +    if (!qtest_enabled()) {
> > +        rcu_disable_atfork();
> > +    }
> >  
> >      if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
> >          error_reportf_err(err, "cannot create PID file: ");
> 
>  Hi Alexander,
> 
> I think this patch might maybe not work as expected: The qtest_enabled()
> has been added before configure_accelerators() is called in main(). So
> qtest_enabled() should always return "false" and thus
> rcu_disabled_fork() is still called in any case... could you please
> double-check whether it works for you and I just made a mistake, or
> whether this is a bug indeed?
Hi,
This is a problem.. I think we can work around this by calling
rcu_enable_atfork from the fuzzer, after qemu_init. I'll send a patch
soon.
Thanks
-Alex

>  Thanks,
>   Thomas
> 


^ permalink raw reply	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2020-06-18 15:12 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-29  5:34 [PATCH v8 00/21] Add virtual device fuzzing support Bulekov, Alexander
2020-01-29  5:34 ` [PATCH v8 01/21] softmmu: split off vl.c:main() into main.c Bulekov, Alexander
2020-01-30 14:39   ` Stefan Hajnoczi
2020-01-30 15:06   ` Alex Bennée
2020-01-30 17:44     ` Alexander Bulekov
2020-01-30 18:41       ` Alex Bennée
2020-02-05 14:02   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 02/21] module: check module wasn't already initialized Bulekov, Alexander
2020-01-29  5:34 ` [PATCH v8 03/21] fuzz: add FUZZ_TARGET module type Bulekov, Alexander
2020-02-05 11:28   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 04/21] qtest: add qtest_server_send abstraction Bulekov, Alexander
2020-02-05 11:30   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 05/21] libqtest: add a layer of abstraction to send/recv Bulekov, Alexander
2020-02-05 11:32   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 06/21] libqtest: make bufwrite rely on the TransportOps Bulekov, Alexander
2020-02-05 11:36   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 07/21] qtest: add in-process incoming command handler Bulekov, Alexander
2020-02-05 11:39   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 08/21] libqos: rename i2c_send and i2c_recv Bulekov, Alexander
2020-02-05 11:40   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 09/21] libqos: split qos-test and libqos makefile vars Bulekov, Alexander
2020-01-29  5:34 ` [PATCH v8 10/21] libqos: move useful qos-test funcs to qos_external Bulekov, Alexander
2020-01-29 10:03   ` Paolo Bonzini
2020-02-05 13:34   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 11/21] fuzz: add fuzzer skeleton Bulekov, Alexander
2020-02-05 11:55   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 12/21] exec: keep ram block across fork when using qtest Bulekov, Alexander
2020-02-05 13:00   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 13/21] main: keep rcu_atfork callback enabled for qtest Bulekov, Alexander
2020-01-30 14:42   ` Stefan Hajnoczi
2020-01-30 17:24     ` Paolo Bonzini
2020-01-30 17:42       ` Alexander Bulekov
2020-01-30 18:14         ` Paolo Bonzini
2020-02-05 13:58   ` Darren Kenny
2020-06-18  7:34   ` Thomas Huth
2020-06-18 15:08     ` Alexander Bulekov
2020-01-29  5:34 ` [PATCH v8 14/21] fuzz: support for fork-based fuzzing Bulekov, Alexander
2020-01-29  5:34 ` [PATCH v8 16/21] fuzz: add target/fuzz makefile rules Bulekov, Alexander
2020-01-29  5:34 ` [PATCH v8 15/21] fuzz: add support for qos-assisted fuzz targets Bulekov, Alexander
2020-02-05 13:18   ` Darren Kenny
2020-02-11 16:19     ` Alexander Bulekov
2020-01-29  5:34 ` [PATCH v8 17/21] fuzz: add configure flag --enable-fuzzing Bulekov, Alexander
2020-02-05 13:19   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 18/21] fuzz: add i440fx fuzz targets Bulekov, Alexander
2020-02-05 13:26   ` Darren Kenny
2020-02-06 14:47     ` Alexander Bulekov
2020-01-29  5:34 ` [PATCH v8 19/21] fuzz: add virtio-net fuzz target Bulekov, Alexander
2020-01-30 14:43   ` Stefan Hajnoczi
2020-02-05 13:57   ` Darren Kenny
2020-02-06 14:51     ` Alexander Bulekov
2020-01-29  5:34 ` [PATCH v8 20/21] fuzz: add virtio-scsi " Bulekov, Alexander
2020-02-05 13:31   ` Darren Kenny
2020-01-29  5:34 ` [PATCH v8 21/21] fuzz: add documentation to docs/devel/ Bulekov, Alexander
2020-02-05 13:33   ` Darren Kenny
2020-01-30 14:44 ` [PATCH v8 00/21] Add virtual device fuzzing support Stefan Hajnoczi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.