diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
index f259d866c9..5bde793bf2 100644
--- a/tests/qtest/fuzz/Makefile.include
+++ b/tests/qtest/fuzz/Makefile.include
@@ -5,6 +5,7 @@ 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-obj-y += tests/qtest/fuzz/qtest_wrappers.o
 
 # Targets
 fuzz-obj-$(CONFIG_PCI_I440FX) += tests/qtest/fuzz/i440fx_fuzz.o
@@ -16,3 +17,23 @@ 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
+
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_inb
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_inw
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_inl
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_outb
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_outw
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_outl
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_readb
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_readw
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_readl
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_readq
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_writeb
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_writew
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_writel
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_writeq
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_memread
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_bufread
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_memwrite
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_bufwrite
+FUZZ_LDFLAGS += -Wl,-wrap,qtest_memset
diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c
index f5c923852e..88ac88bca9 100644
--- a/tests/qtest/fuzz/fuzz.c
+++ b/tests/qtest/fuzz/fuzz.c
@@ -91,7 +91,10 @@ static void usage(char *path)
         printf(" * %s  : %s\n", tmp->target->name,
                 tmp->target->description);
     }
-    printf("Alternatively, add -target-FUZZ_TARGET to the executable name\n");
+    printf("Alternatively, add -target-FUZZ_TARGET to the executable name\n\n"
+           "Set the environment variable FUZZ_SERIALIZE_QTEST=1 to serialize\n"
+           "QTest commands into an ASCII protocol. Useful for building crash\n"
+           "reproducers, but slows down execution.\n");
     exit(0);
 }
 
@@ -137,6 +140,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
 {
 
     char *target_name;
+    bool serialize = false;
 
     /* Initialize qgraph and modules */
     qos_graph_init();
@@ -157,6 +161,13 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
         usage(**argv);
     }
 
+    /* Should we always serialize qtest commands? */
+    if (getenv("FUZZ_SERIALIZE_QTEST")) {
+        serialize = true;
+    }
+
+    fuzz_qtest_set_serialize(serialize);
+
     /* Identify the fuzz target */
     fuzz_target = fuzz_get_target(target_name);
     if (!fuzz_target) {
diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h
index 03901d414e..72d5710f6c 100644
--- a/tests/qtest/fuzz/fuzz.h
+++ b/tests/qtest/fuzz/fuzz.h
@@ -82,6 +82,9 @@ typedef struct FuzzTarget {
 void flush_events(QTestState *);
 void reboot(QTestState *);
 
+/* Use the QTest ASCII protocol or call address_space API directly?*/
+void fuzz_qtest_set_serialize(bool option);
+
 /*
  * makes a copy of *target and adds it to the target-list.
  * i.e. fine to set up target on the caller's stack
diff --git a/tests/qtest/fuzz/qtest_wrappers.c b/tests/qtest/fuzz/qtest_wrappers.c
new file mode 100644
index 0000000000..713c830cdb
--- /dev/null
+++ b/tests/qtest/fuzz/qtest_wrappers.c
@@ -0,0 +1,252 @@
+/*
+ * qtest function wrappers
+ *
+ * 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 "cpu.h"
+#include "exec/ioport.h"
+
+#include "fuzz.h"
+
+static bool serialize = true;
+
+#define WRAP(RET_TYPE, NAME_AND_ARGS)\
+    RET_TYPE __wrap_##NAME_AND_ARGS;\
+    RET_TYPE __real_##NAME_AND_ARGS;
+
+WRAP(uint8_t  , qtest_inb(QTestState *s, uint16_t addr))
+WRAP(uint16_t , qtest_inw(QTestState *s, uint16_t addr))
+WRAP(uint32_t , qtest_inl(QTestState *s, uint16_t addr))
+WRAP(void     , qtest_outb(QTestState *s, uint16_t addr, uint8_t value))
+WRAP(void     , qtest_outw(QTestState *s, uint16_t addr, uint16_t value))
+WRAP(void     , qtest_outl(QTestState *s, uint16_t addr, uint32_t value))
+WRAP(uint8_t  , qtest_readb(QTestState *s, uint64_t addr))
+WRAP(uint16_t , qtest_readw(QTestState *s, uint64_t addr))
+WRAP(uint32_t , qtest_readl(QTestState *s, uint64_t addr))
+WRAP(uint64_t , qtest_readq(QTestState *s, uint64_t addr))
+WRAP(void     , qtest_writeb(QTestState *s, uint64_t addr, uint8_t value))
+WRAP(void     , qtest_writew(QTestState *s, uint64_t addr, uint16_t value))
+WRAP(void     , qtest_writel(QTestState *s, uint64_t addr, uint32_t value))
+WRAP(void     , qtest_writeq(QTestState *s, uint64_t addr, uint64_t value))
+WRAP(void     , qtest_memread(QTestState *s, uint64_t addr,
+                              void *data, size_t size))
+WRAP(void     , qtest_bufread(QTestState *s, uint64_t addr, void *data,
+                              size_t size))
+WRAP(void     , qtest_memwrite(QTestState *s, uint64_t addr, const void *data,
+                               size_t size))
+WRAP(void,      qtest_bufwrite(QTestState *s, uint64_t addr,
+                               const void *data, size_t size))
+WRAP(void,      qtest_memset(QTestState *s, uint64_t addr,
+                             uint8_t patt, size_t size))
+
+
+uint8_t __wrap_qtest_inb(QTestState *s, uint16_t addr)
+{
+    if (!serialize) {
+        return cpu_inb(addr);
+    } else {
+        return __real_qtest_inb(s, addr);
+    }
+}
+
+uint16_t __wrap_qtest_inw(QTestState *s, uint16_t addr)
+{
+    if (!serialize) {
+        return cpu_inw(addr);
+    } else {
+        return __real_qtest_inw(s, addr);
+    }
+}
+
+uint32_t __wrap_qtest_inl(QTestState *s, uint16_t addr)
+{
+    if (!serialize) {
+        return cpu_inl(addr);
+    } else {
+        return __real_qtest_inl(s, addr);
+    }
+}
+
+void __wrap_qtest_outb(QTestState *s, uint16_t addr, uint8_t value)
+{
+    if (!serialize) {
+        cpu_outb(addr, value);
+    } else {
+        __real_qtest_outb(s, addr, value);
+    }
+}
+
+void __wrap_qtest_outw(QTestState *s, uint16_t addr, uint16_t value)
+{
+    if (!serialize) {
+        cpu_outw(addr, value);
+    } else {
+        __real_qtest_outw(s, addr, value);
+    }
+}
+
+void __wrap_qtest_outl(QTestState *s, uint16_t addr, uint32_t value)
+{
+    if (!serialize) {
+        cpu_outl(addr, value);
+    } else {
+        __real_qtest_outl(s, addr, value);
+    }
+}
+
+uint8_t __wrap_qtest_readb(QTestState *s, uint64_t addr)
+{
+    uint8_t value;
+    if (!serialize) {
+        address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 1);
+        return value;
+    } else {
+        return __real_qtest_readb(s, addr);
+    }
+}
+
+uint16_t __wrap_qtest_readw(QTestState *s, uint64_t addr)
+{
+    uint16_t value;
+    if (!serialize) {
+        address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 2);
+        return value;
+    } else {
+        return __real_qtest_readw(s, addr);
+    }
+}
+
+uint32_t __wrap_qtest_readl(QTestState *s, uint64_t addr)
+{
+    uint32_t value;
+    if (!serialize) {
+        address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 4);
+        return value;
+    } else {
+        return __real_qtest_readl(s, addr);
+    }
+}
+
+uint64_t __wrap_qtest_readq(QTestState *s, uint64_t addr)
+{
+    uint64_t value;
+    if (!serialize) {
+        address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 8);
+        return value;
+    } else {
+        return __real_qtest_readq(s, addr);
+    }
+}
+
+void __wrap_qtest_writeb(QTestState *s, uint64_t addr, uint8_t value)
+{
+    if (!serialize) {
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 1);
+    } else {
+        __real_qtest_writeb(s, addr, value);
+    }
+}
+
+void __wrap_qtest_writew(QTestState *s, uint64_t addr, uint16_t value)
+{
+    if (!serialize) {
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 2);
+    } else {
+        __real_qtest_writew(s, addr, value);
+    }
+}
+
+void __wrap_qtest_writel(QTestState *s, uint64_t addr, uint32_t value)
+{
+    if (!serialize) {
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 4);
+    } else {
+        __real_qtest_writel(s, addr, value);
+    }
+}
+
+void __wrap_qtest_writeq(QTestState *s, uint64_t addr, uint64_t value)
+{
+    if (!serialize) {
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            &value, 8);
+    } else {
+        __real_qtest_writeq(s, addr, value);
+    }
+}
+
+void __wrap_qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size)
+{
+    if (!serialize) {
+        address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
+                           size);
+    } else {
+        __real_qtest_memread(s, addr, data, size);
+    }
+}
+
+void __wrap_qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size)
+{
+    if (!serialize) {
+        address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
+                           size);
+    } else {
+        __real_qtest_bufread(s, addr, data, size);
+    }
+}
+
+void __wrap_qtest_memwrite(QTestState *s, uint64_t addr, const void *data,
+                           size_t size)
+{
+    if (!serialize) {
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            data, size);
+    } else {
+        __real_qtest_memwrite(s, addr, data, size);
+    }
+}
+
+void __wrap_qtest_bufwrite(QTestState *s, uint64_t addr,
+                    const void *data, size_t size)
+{
+    if (!serialize) {
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            data, size);
+    } else {
+        __real_qtest_bufwrite(s, addr, data, size);
+    }
+}
+void __wrap_qtest_memset(QTestState *s, uint64_t addr,
+                         uint8_t patt, size_t size)
+{
+    void *data;
+    if (!serialize) {
+        data = malloc(size);
+        memset(data, patt, size);
+        address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+                            data, size);
+    } else {
+        __real_qtest_memset(s, addr, patt, size);
+    }
+}
+
+void fuzz_qtest_set_serialize(bool option)
+{
+    serialize = option;
+}
