mirror of https://github.com/xemu-project/xemu.git
* Some small qtest fixes
* Oss-fuzz updates * Publish the docs built during gitlab CI to the user's gitlab.io page * Update the OpenBSD VM test to v6.8 * Fix the device-crash-test script to run with the meson build system * Some small s390x fixes -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAl+qc+IRHHRodXRoQHJl ZGhhdC5jb20ACgkQLtnXdP5wLbUPfg//Z4AbWXNwldRTTZM/61kY88meNT6jHVaF narl6v1u+1oLeSlwoHaZb7PZ3auxAuukROaiUWqIzn9udpyvRBB6pXz+JzGpwmWk DUady8vYXg/Q9Yux8MZMgKzhOH484eVM1y9NXQ+lzJT7R3VtbcN0IvlFv20M5d+k S449CuAlXH5b42A6OUpABZYfxm59lWZW91qdejRnLOyqyLrv5GPhro5J3Uadv3l2 lHExdzjSCBovK3ORCR9L6JghLMmN3M6a9VArNas6KTSLvz5Vh/Rnz5otfpf5DiHv D/FOXQjm1aujCmfa41oBZH84aGhZrdK/fhtryJE+OBa7a5mpSOv5mXg7IFKDeCRd p0bmLOo891LdzOHlkZ/p+8uK3CkdsJpxch0/8mLLLvgJCGCNpeq7GJXj7wuPa2RK gc2Ax+XoOR/C5f+x4PbRjHYgI3tTbO8MM9G87QT8TvV3llwpeFs7LRjegoanoAxn OA0hVp+4dEXBPJF0bludSNnJ+NPDUWDKVPW/sTlaVytxSgFw0v5bBtne+CUY8QGN xNX9T3qsSN56phaZlSzB0ktJKid8nvqEBp82/ayAVyhf6cYk4p08f+cS/qb0RpMm 5J7GbmB54HWq8Is6BjG261BvnKfEdhl58YIREbgY0gQopGDXZzazozIysNKcZAnI WzL1cfWPWwc= =a3YE -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/huth-gitlab/tags/pull-request-2020-11-10' into staging * Some small qtest fixes * Oss-fuzz updates * Publish the docs built during gitlab CI to the user's gitlab.io page * Update the OpenBSD VM test to v6.8 * Fix the device-crash-test script to run with the meson build system * Some small s390x fixes # gpg: Signature made Tue 10 Nov 2020 11:05:06 GMT # gpg: using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5 # gpg: issuer "thuth@redhat.com" # gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full] # gpg: aka "Thomas Huth <thuth@redhat.com>" [full] # gpg: aka "Thomas Huth <huth@tuxfamily.org>" [full] # gpg: aka "Thomas Huth <th.huth@posteo.de>" [unknown] # Primary key fingerprint: 27B8 8847 EEE0 2501 18F3 EAB9 2ED9 D774 FE70 2DB5 * remotes/huth-gitlab/tags/pull-request-2020-11-10: s390x: Avoid variable size warning in ipl.h s390x: fix clang 11 warnings in cpu_models.c qtest: Update references to parse_escape() in comments fuzz: add virtio-blk fuzz target docs: add "page source" link to sphinx documentation gitlab: force enable docs build in Fedora, Ubuntu, Debian gitlab: publish the docs built during CI configure: surface deprecated targets in the help output fuzz: Make fork_fuzz.ld compatible with LLVM's LLD scripts/oss-fuzz: give all fuzzers -target names docs/fuzz: update fuzzing documentation post-meson docs/fuzz: rST-ify the fuzzing documentation MAINTAINERS: Add gitlab-pipeline-status script to GitLab CI section gitlab-ci: Drop generic cache rule tests/qtest/tpm: Remove redundant check in the tpm_test_swtpm_test() qtest: Fix bad printf format specifiers device-crash-test: Check if path is actually an executable file tests/vm: update openbsd to release 6.8 meson: always include contrib/libvhost-user Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
879860ca70
|
@ -7,12 +7,6 @@ stages:
|
|||
- build
|
||||
- test
|
||||
|
||||
# We assume GitLab has it's own caching set up for RPM/APT repositories so we
|
||||
# just take care of avocado assets here.
|
||||
cache:
|
||||
paths:
|
||||
- $HOME/avocado/data/cache
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/edk2.yml'
|
||||
- local: '/.gitlab-ci.d/opensbi.yml'
|
||||
|
@ -80,6 +74,7 @@ build-system-ubuntu:
|
|||
TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu
|
||||
moxie-softmmu microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
CONFIGURE_ARGS: --enable-docs
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
|
@ -111,6 +106,7 @@ build-system-debian:
|
|||
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
|
||||
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
CONFIGURE_ARGS: --enable-docs
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
|
@ -139,7 +135,7 @@ build-system-fedora:
|
|||
<<: *native_build_job_definition
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle
|
||||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs
|
||||
TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu
|
||||
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
|
@ -423,3 +419,17 @@ check-dco:
|
|||
- $CI_PROJECT_NAMESPACE == 'qemu-project' && $CI_COMMIT_BRANCH == 'master'
|
||||
variables:
|
||||
GIT_DEPTH: 1000
|
||||
|
||||
pages:
|
||||
image: $CI_REGISTRY_IMAGE/qemu/ubuntu2004:latest
|
||||
stage: test
|
||||
needs:
|
||||
- job: build-system-ubuntu
|
||||
artifacts: true
|
||||
script:
|
||||
- mkdir public
|
||||
- mv build/docs/index.html public/
|
||||
- for i in devel interop specs system tools user ; do mv build/docs/$i public/ ; done
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
|
|
|
@ -2525,7 +2525,7 @@ R: Thomas Huth <thuth@redhat.com>
|
|||
S: Maintained
|
||||
F: tests/qtest/fuzz/
|
||||
F: scripts/oss-fuzz/
|
||||
F: docs/devel/fuzzing.txt
|
||||
F: docs/devel/fuzzing.rst
|
||||
|
||||
Register API
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
|
@ -3156,6 +3156,7 @@ S: Maintained
|
|||
F: .gitlab-ci.yml
|
||||
F: .gitlab-ci.d/crossbuilds.yml
|
||||
F: .gitlab-ci.d/*py
|
||||
F: scripts/ci/gitlab-pipeline-status
|
||||
|
||||
Guest Test Compilation Support
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
|
|
|
@ -1655,9 +1655,11 @@ Standard options:
|
|||
--prefix=PREFIX install in PREFIX [$prefix]
|
||||
--interp-prefix=PREFIX where to find shared libraries, etc.
|
||||
use %M for cpu name [$interp_prefix]
|
||||
--target-list=LIST set target list (default: build everything)
|
||||
--target-list=LIST set target list (default: build all non-deprecated)
|
||||
$(echo Available targets: $default_target_list | \
|
||||
fold -s -w 53 | sed -e 's/^/ /')
|
||||
$(echo Deprecated targets: $deprecated_targets_list | \
|
||||
fold -s -w 53 | sed -e 's/^/ /')
|
||||
--target-list-exclude=LIST exclude a set of targets from the default target-list
|
||||
|
||||
Advanced options (experts only):
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -177,6 +177,7 @@ html_theme_options = {
|
|||
html_sidebars = {
|
||||
'**': [
|
||||
'about.html',
|
||||
'editpage.html',
|
||||
'navigation.html',
|
||||
'searchbox.html',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/devel/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,236 @@
|
|||
========
|
||||
Fuzzing
|
||||
========
|
||||
|
||||
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 maps ~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).
|
||||
Here, enable-sanitizers, is optional but it allows us to reliably detect bugs
|
||||
such as out-of-bounds accesses, use-after-frees, double-frees etc.::
|
||||
|
||||
CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing \
|
||||
--enable-sanitizers
|
||||
|
||||
Fuzz targets are built similarly to system targets::
|
||||
|
||||
make qemu-fuzz-i386
|
||||
|
||||
This builds ``./qemu-fuzz-i386``
|
||||
|
||||
The first option to this command is: ``--fuzz-target=FUZZ_NAME``
|
||||
To list all of the available fuzzers run ``qemu-fuzz-i386`` with no arguments.
|
||||
|
||||
For example::
|
||||
|
||||
./qemu-fuzz-i386 --fuzz-target=virtio-scsi-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.
|
||||
|
||||
Useful libFuzzer flags
|
||||
----------------------
|
||||
|
||||
As mentioned above, libFuzzer accepts some arguments. Passing ``-help=1`` will
|
||||
list the available arguments. In particular, these arguments might be helpful:
|
||||
|
||||
* ``CORPUS_DIR/`` : Specify a directory as the last argument to libFuzzer.
|
||||
libFuzzer stores each "interesting" input in this corpus directory. The next
|
||||
time you run libFuzzer, it will read all of the inputs from the corpus, and
|
||||
continue fuzzing from there. You can also specify multiple directories.
|
||||
libFuzzer loads existing inputs from all specified directories, but will only
|
||||
write new ones to the first one specified.
|
||||
|
||||
* ``-max_len=4096`` : specify the maximum byte-length of the inputs libFuzzer
|
||||
will generate.
|
||||
|
||||
* ``-close_fd_mask={1,2,3}`` : close, stderr, or both. Useful for targets that
|
||||
trigger many debug/error messages, or create output on the serial console.
|
||||
|
||||
* ``-jobs=4 -workers=4`` : These arguments configure libFuzzer to run 4 fuzzers in
|
||||
parallel (4 fuzzing jobs in 4 worker processes). Alternatively, with only
|
||||
``-jobs=N``, libFuzzer automatically spawns a number of workers less than or equal
|
||||
to half the available CPU cores. Replace 4 with a number appropriate for your
|
||||
machine. Make sure to specify a ``CORPUS_DIR``, which will allow the parallel
|
||||
fuzzers to share information about the interesting inputs they find.
|
||||
|
||||
* ``-use_value_profile=1`` : For each comparison operation, libFuzzer computes
|
||||
``(caller_pc&4095) | (popcnt(Arg1 ^ Arg2) << 12)`` and places this in the
|
||||
coverage table. Useful for targets with "magic" constants. If Arg1 came from
|
||||
the fuzzer's input and Arg2 is a magic constant, then each time the Hamming
|
||||
distance between Arg1 and Arg2 decreases, libFuzzer adds the input to the
|
||||
corpus.
|
||||
|
||||
* ``-shrink=1`` : Tries to make elements of the corpus "smaller". Might lead to
|
||||
better coverage performance, depending on the target.
|
||||
|
||||
Note that libFuzzer's exact behavior will depend on the version of
|
||||
clang and libFuzzer used to build the device fuzzers.
|
||||
|
||||
Generating Coverage Reports
|
||||
---------------------------
|
||||
|
||||
Code coverage is a crucial metric for evaluating a fuzzer's performance.
|
||||
libFuzzer's output provides a "cov: " column that provides a total number of
|
||||
unique blocks/edges covered. To examine coverage on a line-by-line basis we
|
||||
can use Clang coverage:
|
||||
|
||||
1. Configure libFuzzer to store a corpus of all interesting inputs (see
|
||||
CORPUS_DIR above)
|
||||
2. ``./configure`` the QEMU build with ::
|
||||
|
||||
--enable-fuzzing \
|
||||
--extra-cflags="-fprofile-instr-generate -fcoverage-mapping"
|
||||
|
||||
3. Re-run the fuzzer. Specify $CORPUS_DIR/* as an argument, telling libfuzzer
|
||||
to execute all of the inputs in $CORPUS_DIR and exit. Once the process
|
||||
exits, you should find a file, "default.profraw" in the working directory.
|
||||
4. Execute these commands to generate a detailed HTML coverage-report::
|
||||
|
||||
llvm-profdata merge -output=default.profdata default.profraw
|
||||
llvm-cov show ./path/to/qemu-fuzz-i386 -instr-profile=default.profdata \
|
||||
--format html -output-dir=/path/to/output/report
|
||||
|
||||
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.
|
||||
|
||||
The Generic Fuzzer
|
||||
------------------
|
||||
|
||||
Writing a fuzz target can be a lot of effort (especially if a device driver has
|
||||
not be built-out within libqos). Many devices can be fuzzed to some degree,
|
||||
without any device-specific code, using the generic-fuzz target.
|
||||
|
||||
The generic-fuzz target is capable of fuzzing devices over their PIO, MMIO,
|
||||
and DMA input-spaces. To apply the generic-fuzz to a device, we need to define
|
||||
two env-variables, at minimum:
|
||||
|
||||
* ``QEMU_FUZZ_ARGS=`` is the set of QEMU arguments used to configure a machine, with
|
||||
the device attached. For example, if we want to fuzz the virtio-net device
|
||||
attached to a pc-i440fx machine, we can specify::
|
||||
|
||||
QEMU_FUZZ_ARGS="-M pc -nodefaults -netdev user,id=user0 \
|
||||
-device virtio-net,netdev=user0"
|
||||
|
||||
* ``QEMU_FUZZ_OBJECTS=`` is a set of space-delimited strings used to identify
|
||||
the MemoryRegions that will be fuzzed. These strings are compared against
|
||||
MemoryRegion names and MemoryRegion owner names, to decide whether each
|
||||
MemoryRegion should be fuzzed. These strings support globbing. For the
|
||||
virtio-net example, we could use one of ::
|
||||
|
||||
QEMU_FUZZ_OBJECTS='virtio-net'
|
||||
QEMU_FUZZ_OBJECTS='virtio*'
|
||||
QEMU_FUZZ_OBJECTS='virtio* pcspk' # Fuzz the virtio devices and the speaker
|
||||
QEMU_FUZZ_OBJECTS='*' # Fuzz the whole machine``
|
||||
|
||||
The ``"info mtree"`` and ``"info qom-tree"`` monitor commands can be especially
|
||||
useful for identifying the ``MemoryRegion`` and ``Object`` names used for
|
||||
matching.
|
||||
|
||||
As a generic rule-of-thumb, the more ``MemoryRegions``/Devices we match, the
|
||||
greater the input-space, and the smaller the probability of finding crashing
|
||||
inputs for individual devices. As such, it is usually a good idea to limit the
|
||||
fuzzer to only a few ``MemoryRegions``.
|
||||
|
||||
To ensure that these env variables have been configured correctly, we can use::
|
||||
|
||||
./qemu-fuzz-i386 --fuzz-target=generic-fuzz -runs=0
|
||||
|
||||
The output should contain a complete list of matched MemoryRegions.
|
||||
|
||||
Implementation Details / Fuzzer 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 ``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:
|
||||
|
||||
- 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``
|
||||
|
||||
- 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``
|
|
@ -1,214 +0,0 @@
|
|||
= 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).
|
||||
Here, enable-sanitizers, is optional but it allows us to reliably detect bugs
|
||||
such as out-of-bounds accesses, use-after-frees, double-frees etc.
|
||||
|
||||
CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing \
|
||||
--enable-sanitizers
|
||||
|
||||
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-target=FUZZ_NAME
|
||||
To list all of the available fuzzers run qemu-fuzz-i386 with no arguments.
|
||||
|
||||
For example:
|
||||
./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-scsi-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.
|
||||
|
||||
== Useful libFuzzer flags ==
|
||||
|
||||
As mentioned above, libFuzzer accepts some arguments. Passing -help=1 will list
|
||||
the available arguments. In particular, these arguments might be helpful:
|
||||
|
||||
$CORPUS_DIR/ : Specify a directory as the last argument to libFuzzer. libFuzzer
|
||||
stores each "interesting" input in this corpus directory. The next time you run
|
||||
libFuzzer, it will read all of the inputs from the corpus, and continue fuzzing
|
||||
from there. You can also specify multiple directories. libFuzzer loads existing
|
||||
inputs from all specified directories, but will only write new ones to the
|
||||
first one specified.
|
||||
|
||||
-max_len=4096 : specify the maximum byte-length of the inputs libFuzzer will
|
||||
generate.
|
||||
|
||||
-close_fd_mask={1,2,3} : close, stderr, or both. Useful for targets that
|
||||
trigger many debug/error messages, or create output on the serial console.
|
||||
|
||||
-jobs=4 -workers=4 : These arguments configure libFuzzer to run 4 fuzzers in
|
||||
parallel (4 fuzzing jobs in 4 worker processes). Alternatively, with only
|
||||
-jobs=N, libFuzzer automatically spawns a number of workers less than or equal
|
||||
to half the available CPU cores. Replace 4 with a number appropriate for your
|
||||
machine. Make sure to specify a $CORPUS_DIR, which will allow the parallel
|
||||
fuzzers to share information about the interesting inputs they find.
|
||||
|
||||
-use_value_profile=1 : For each comparison operation, libFuzzer computes
|
||||
(caller_pc&4095) | (popcnt(Arg1 ^ Arg2) << 12) and places this in the coverage
|
||||
table. Useful for targets with "magic" constants. If Arg1 came from the fuzzer's
|
||||
input and Arg2 is a magic constant, then each time the Hamming distance
|
||||
between Arg1 and Arg2 decreases, libFuzzer adds the input to the corpus.
|
||||
|
||||
-shrink=1 : Tries to make elements of the corpus "smaller". Might lead to
|
||||
better coverage performance, depending on the target.
|
||||
|
||||
Note that libFuzzer's exact behavior will depend on the version of
|
||||
clang and libFuzzer used to build the device fuzzers.
|
||||
|
||||
== Generating Coverage Reports ==
|
||||
Code coverage is a crucial metric for evaluating a fuzzer's performance.
|
||||
libFuzzer's output provides a "cov: " column that provides a total number of
|
||||
unique blocks/edges covered. To examine coverage on a line-by-line basis we
|
||||
can use Clang coverage:
|
||||
|
||||
1. Configure libFuzzer to store a corpus of all interesting inputs (see
|
||||
CORPUS_DIR above)
|
||||
2. ./configure the QEMU build with:
|
||||
--enable-fuzzing \
|
||||
--extra-cflags="-fprofile-instr-generate -fcoverage-mapping"
|
||||
3. Re-run the fuzzer. Specify $CORPUS_DIR/* as an argument, telling libfuzzer
|
||||
to execute all of the inputs in $CORPUS_DIR and exit. Once the process
|
||||
exits, you should find a file, "default.profraw" in the working directory.
|
||||
4. Execute these commands to generate a detailed HTML coverage-report:
|
||||
llvm-profdata merge -output=default.profdata default.profraw
|
||||
llvm-cov show ./path/to/qemu-fuzz-i386 -instr-profile=default.profdata \
|
||||
--format html -output-dir=/path/to/output/report
|
||||
|
||||
== 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.
|
||||
|
||||
== The Generic Fuzzer ==
|
||||
Writing a fuzz target can be a lot of effort (especially if a device driver has
|
||||
not be built-out within libqos). Many devices can be fuzzed to some degree,
|
||||
without any device-specific code, using the generic-fuzz target.
|
||||
|
||||
The generic-fuzz target is capable of fuzzing devices over their PIO, MMIO,
|
||||
and DMA input-spaces. To apply the generic-fuzz to a device, we need to define
|
||||
two env-variables, at minimum:
|
||||
|
||||
QEMU_FUZZ_ARGS= is the set of QEMU arguments used to configure a machine, with
|
||||
the device attached. For example, if we want to fuzz the virtio-net device
|
||||
attached to a pc-i440fx machine, we can specify:
|
||||
QEMU_FUZZ_ARGS="-M pc -nodefaults -netdev user,id=user0 \
|
||||
-device virtio-net,netdev=user0"
|
||||
|
||||
QEMU_FUZZ_OBJECTS= is a set of space-delimited strings used to identify the
|
||||
MemoryRegions that will be fuzzed. These strings are compared against
|
||||
MemoryRegion names and MemoryRegion owner names, to decide whether each
|
||||
MemoryRegion should be fuzzed. These strings support globbing. For the
|
||||
virtio-net example, we could use QEMU_FUZZ_OBJECTS=
|
||||
* 'virtio-net'
|
||||
* 'virtio*'
|
||||
* 'virtio* pcspk' (Fuzz the virtio devices and the PC speaker...)
|
||||
* '*' (Fuzz the whole machine)
|
||||
|
||||
The "info mtree" and "info qom-tree" monitor commands can be especially useful
|
||||
for identifying the MemoryRegion and Object names used for matching.
|
||||
|
||||
As a generic rule-of-thumb, the more MemoryRegions/Devices we match, the greater
|
||||
the input-space, and the smaller the probability of finding crashing inputs for
|
||||
individual devices. As such, it is usually a good idea to limit the fuzzer to
|
||||
only a few MemoryRegions.
|
||||
|
||||
To ensure that these env variables have been configured correctly, we can use:
|
||||
|
||||
./qemu-fuzz-i386 --fuzz-target=generic-fuzz -runs=0
|
||||
|
||||
The output should contain a complete list of matched MemoryRegions.
|
||||
|
||||
= 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
|
|
@ -22,6 +22,7 @@ Contents:
|
|||
stable-process
|
||||
testing
|
||||
qtest
|
||||
fuzzing
|
||||
decodetree
|
||||
secure-coding-practices
|
||||
tcg
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/interop/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/specs/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/system/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/tools/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div id="editpage">
|
||||
<ul>
|
||||
<li><a href="https://gitlab.com/qemu-project/qemu/-/blob/master/docs/user/{{pagename}}.rst">Page source</a></li>
|
||||
</ul>
|
||||
</div>
|
|
@ -32,7 +32,7 @@ struct IPLBlockPV {
|
|||
uint32_t num_comp; /* 0x74 */
|
||||
uint64_t pv_header_addr; /* 0x78 */
|
||||
uint64_t pv_header_len; /* 0x80 */
|
||||
struct IPLBlockPVComp components[];
|
||||
struct IPLBlockPVComp components[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPV IPLBlockPV;
|
||||
|
||||
|
@ -63,7 +63,7 @@ struct IplBlockFcp {
|
|||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[];
|
||||
uint8_t scp_data[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
|
|
|
@ -1450,11 +1450,7 @@ trace_events_subdirs += [
|
|||
'util',
|
||||
]
|
||||
|
||||
vhost_user = not_found
|
||||
if 'CONFIG_VHOST_USER' in config_host
|
||||
subdir('contrib/libvhost-user')
|
||||
endif
|
||||
|
||||
subdir('contrib/libvhost-user')
|
||||
subdir('qapi')
|
||||
subdir('qobject')
|
||||
subdir('stubs')
|
||||
|
|
|
@ -383,7 +383,9 @@ def binariesToTest(args, testcase):
|
|||
if args.qemu:
|
||||
r = args.qemu
|
||||
else:
|
||||
r = glob.glob('./qemu-system-*')
|
||||
r = [f.path for f in os.scandir('.')
|
||||
if f.name.startswith('qemu-system-') and
|
||||
f.is_file() and os.access(f, os.X_OK)]
|
||||
return r
|
||||
|
||||
|
||||
|
|
|
@ -62,9 +62,6 @@ fi
|
|||
|
||||
mkdir -p "$DEST_DIR/lib/" # Copy the shared libraries here
|
||||
|
||||
mkdir -p "$DEST_DIR/bin/" # Copy executables that shouldn't
|
||||
# be treated as fuzzers by oss-fuzz here
|
||||
|
||||
# Build once to get the list of dynamic lib paths, and copy them over
|
||||
../configure --disable-werror --cc="$CC" --cxx="$CXX" --enable-fuzzing \
|
||||
--prefix="$DEST_DIR" --bindir="$DEST_DIR" --datadir="$DEST_DIR/data/" \
|
||||
|
@ -91,20 +88,23 @@ make "-j$(nproc)" qemu-fuzz-i386 V=1
|
|||
# Copy over the datadir
|
||||
cp -r ../pc-bios/ "$DEST_DIR/pc-bios"
|
||||
|
||||
cp "./qemu-fuzz-i386" "$DEST_DIR/bin/qemu-fuzz-i386.base"
|
||||
targets=$(./qemu-fuzz-i386 | awk '$1 ~ /\*/ {print $2}')
|
||||
base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)"
|
||||
|
||||
cp "./qemu-fuzz-i386" "$base_copy"
|
||||
|
||||
# Run the fuzzer with no arguments, to print the help-string and get the list
|
||||
# of available fuzz-targets. Copy over the qemu-fuzz-i386, naming it according
|
||||
# to each available fuzz target (See 05509c8e6d fuzz: select fuzz target using
|
||||
# executable name)
|
||||
for target in $(./qemu-fuzz-i386 | awk '$1 ~ /\*/ {print $2}');
|
||||
for target in $(echo "$targets" | tail -n +2);
|
||||
do
|
||||
# Ignore the generic-fuzz target, as it requires some environment variables
|
||||
# to be configured. We have some generic-fuzz-{pc-q35, floppy, ...} targets
|
||||
# that are thin wrappers around this target that set the required
|
||||
# environment variables according to predefined configs.
|
||||
if [ "$target" != "generic-fuzz" ]; then
|
||||
ln "$DEST_DIR/bin/qemu-fuzz-i386.base" \
|
||||
ln $base_copy \
|
||||
"$DEST_DIR/qemu-fuzz-i386-target-$target"
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -986,7 +986,7 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp)
|
|||
static void get_feature(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
S390Feat feat = (S390Feat) opaque;
|
||||
S390Feat feat = (S390Feat) (uintptr_t) opaque;
|
||||
S390CPU *cpu = S390_CPU(obj);
|
||||
bool value;
|
||||
|
||||
|
@ -1003,7 +1003,7 @@ static void get_feature(Object *obj, Visitor *v, const char *name,
|
|||
static void set_feature(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
S390Feat feat = (S390Feat) opaque;
|
||||
S390Feat feat = (S390Feat) (uintptr_t) opaque;
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
S390CPU *cpu = S390_CPU(obj);
|
||||
bool value;
|
||||
|
@ -1037,7 +1037,7 @@ static void set_feature(Object *obj, Visitor *v, const char *name,
|
|||
static void get_feature_group(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
S390FeatGroup group = (S390FeatGroup) opaque;
|
||||
S390FeatGroup group = (S390FeatGroup) (uintptr_t) opaque;
|
||||
const S390FeatGroupDef *def = s390_feat_group_def(group);
|
||||
S390CPU *cpu = S390_CPU(obj);
|
||||
S390FeatBitmap tmp;
|
||||
|
@ -1058,7 +1058,7 @@ static void get_feature_group(Object *obj, Visitor *v, const char *name,
|
|||
static void set_feature_group(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
S390FeatGroup group = (S390FeatGroup) opaque;
|
||||
S390FeatGroup group = (S390FeatGroup) (uintptr_t) opaque;
|
||||
const S390FeatGroupDef *def = s390_feat_group_def(group);
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
S390CPU *cpu = S390_CPU(obj);
|
||||
|
|
|
@ -536,7 +536,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
|
|||
if (kvm_supports_sve) {
|
||||
g_assert(vls != 0);
|
||||
max_vq = 64 - __builtin_clzll(vls);
|
||||
sprintf(max_name, "sve%d", max_vq * 128);
|
||||
sprintf(max_name, "sve%u", max_vq * 128);
|
||||
|
||||
/* Enabling a supported length is of course fine. */
|
||||
assert_sve_vls(qts, "host", vls, "{ %s: true }", max_name);
|
||||
|
@ -556,7 +556,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
|
|||
* unless all larger, supported vector lengths are also
|
||||
* disabled.
|
||||
*/
|
||||
sprintf(name, "sve%d", vq * 128);
|
||||
sprintf(name, "sve%u", vq * 128);
|
||||
error = g_strdup_printf("cannot disable %s", name);
|
||||
assert_error(qts, "host", error,
|
||||
"{ %s: true, %s: false }",
|
||||
|
@ -569,7 +569,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
|
|||
* we need at least one vector length enabled.
|
||||
*/
|
||||
vq = __builtin_ffsll(vls);
|
||||
sprintf(name, "sve%d", vq * 128);
|
||||
sprintf(name, "sve%u", vq * 128);
|
||||
error = g_strdup_printf("cannot disable %s", name);
|
||||
assert_error(qts, "host", error, "{ %s: false }", name);
|
||||
g_free(error);
|
||||
|
@ -581,7 +581,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
|
|||
}
|
||||
}
|
||||
if (vq <= SVE_MAX_VQ) {
|
||||
sprintf(name, "sve%d", vq * 128);
|
||||
sprintf(name, "sve%u", vq * 128);
|
||||
error = g_strdup_printf("cannot enable %s", name);
|
||||
assert_error(qts, "host", error, "{ %s: true }", name);
|
||||
g_free(error);
|
||||
|
|
|
@ -16,6 +16,11 @@ SECTIONS
|
|||
/* Lowest stack counter */
|
||||
*(__sancov_lowest_stack);
|
||||
}
|
||||
}
|
||||
INSERT AFTER .data;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data.fuzz_ordered :
|
||||
{
|
||||
/*
|
||||
|
@ -34,6 +39,11 @@ SECTIONS
|
|||
*/
|
||||
*(.bss._ZN6fuzzer3TPCE);
|
||||
}
|
||||
}
|
||||
INSERT AFTER .data.fuzz_start;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.data.fuzz_end : ALIGN(4K)
|
||||
{
|
||||
__FUZZ_COUNTERS_END = .;
|
||||
|
@ -43,4 +53,4 @@ SECTIONS
|
|||
* Don't overwrite the SECTIONS in the default linker script. Instead insert the
|
||||
* above into the default script
|
||||
*/
|
||||
INSERT AFTER .data;
|
||||
INSERT AFTER .data.fuzz_ordered;
|
||||
|
|
|
@ -5,6 +5,7 @@ specific_fuzz_ss.add(files('fuzz.c', 'fork_fuzz.c', 'qos_fuzz.c',
|
|||
specific_fuzz_ss.add(when: 'CONFIG_I440FX', if_true: files('i440fx_fuzz.c'))
|
||||
specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('virtio_net_fuzz.c'))
|
||||
specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio_scsi_fuzz.c'))
|
||||
specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio_blk_fuzz.c'))
|
||||
specific_fuzz_ss.add(files('generic_fuzz.c'))
|
||||
|
||||
fork_fuzz = declare_dependency(
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* virtio-blk Fuzzing Target
|
||||
*
|
||||
* Copyright Red Hat Inc., 2020
|
||||
*
|
||||
* Based on virtio-scsi-fuzz target.
|
||||
*
|
||||
* 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/libqos/libqtest.h"
|
||||
#include "tests/qtest/libqos/virtio-blk.h"
|
||||
#include "tests/qtest/libqos/virtio.h"
|
||||
#include "tests/qtest/libqos/virtio-pci.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "standard-headers/linux/virtio_pci.h"
|
||||
#include "standard-headers/linux/virtio_blk.h"
|
||||
#include "fuzz.h"
|
||||
#include "fork_fuzz.h"
|
||||
#include "qos_fuzz.h"
|
||||
|
||||
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
|
||||
#define PCI_SLOT 0x02
|
||||
#define PCI_FN 0x00
|
||||
|
||||
#define MAX_NUM_QUEUES 64
|
||||
|
||||
/* Based on tests/qtest/virtio-blk-test.c. */
|
||||
typedef struct {
|
||||
int num_queues;
|
||||
QVirtQueue *vq[MAX_NUM_QUEUES + 2];
|
||||
} QVirtioBlkQueues;
|
||||
|
||||
static QVirtioBlkQueues *qvirtio_blk_init(QVirtioDevice *dev, uint64_t mask)
|
||||
{
|
||||
QVirtioBlkQueues *vs;
|
||||
uint64_t features;
|
||||
|
||||
vs = g_new0(QVirtioBlkQueues, 1);
|
||||
|
||||
features = qvirtio_get_features(dev);
|
||||
if (!mask) {
|
||||
mask = ~((1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||
(1u << VIRTIO_RING_F_EVENT_IDX) |
|
||||
(1u << VIRTIO_BLK_F_SCSI));
|
||||
}
|
||||
mask |= ~QVIRTIO_F_BAD_FEATURE;
|
||||
features &= mask;
|
||||
qvirtio_set_features(dev, features);
|
||||
|
||||
vs->num_queues = 1;
|
||||
vs->vq[0] = qvirtqueue_setup(dev, fuzz_qos_alloc, 0);
|
||||
|
||||
qvirtio_set_driver_ok(dev);
|
||||
|
||||
return vs;
|
||||
}
|
||||
|
||||
static void virtio_blk_fuzz(QTestState *s, QVirtioBlkQueues* queues,
|
||||
const unsigned char *Data, size_t Size)
|
||||
{
|
||||
/*
|
||||
* Data is a sequence of random bytes. We split them up into "actions",
|
||||
* followed by data:
|
||||
* [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
|
||||
* The length of the data is specified by the preceding vqa.length
|
||||
*/
|
||||
typedef struct vq_action {
|
||||
uint8_t queue;
|
||||
uint8_t length;
|
||||
uint8_t write;
|
||||
uint8_t next;
|
||||
uint8_t kick;
|
||||
} vq_action;
|
||||
|
||||
/* Keep track of the free head for each queue we interact with */
|
||||
bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
|
||||
uint32_t free_head[MAX_NUM_QUEUES + 2];
|
||||
|
||||
QGuestAllocator *t_alloc = fuzz_qos_alloc;
|
||||
|
||||
QVirtioBlk *blk = fuzz_qos_obj;
|
||||
QVirtioDevice *dev = blk->vdev;
|
||||
QVirtQueue *q;
|
||||
vq_action vqa;
|
||||
while (Size >= sizeof(vqa)) {
|
||||
/* Copy the action, so we can normalize length, queue and flags */
|
||||
memcpy(&vqa, Data, sizeof(vqa));
|
||||
|
||||
Data += sizeof(vqa);
|
||||
Size -= sizeof(vqa);
|
||||
|
||||
vqa.queue = vqa.queue % queues->num_queues;
|
||||
/* Cap length at the number of remaining bytes in data */
|
||||
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];
|
||||
|
||||
/* Copy the data into ram, and place it on the virtqueue */
|
||||
uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
|
||||
qtest_memwrite(s, req_addr, Data, vqa.length);
|
||||
if (vq_touched[vqa.queue] == 0) {
|
||||
vq_touched[vqa.queue] = 1;
|
||||
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;
|
||||
}
|
||||
/* In the end, kick each queue we interacted with */
|
||||
for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
|
||||
if (vq_touched[i]) {
|
||||
qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_blk_fork_fuzz(QTestState *s,
|
||||
const unsigned char *Data, size_t Size)
|
||||
{
|
||||
QVirtioBlk *blk = fuzz_qos_obj;
|
||||
static QVirtioBlkQueues *queues;
|
||||
if (!queues) {
|
||||
queues = qvirtio_blk_init(blk->vdev, 0);
|
||||
}
|
||||
if (fork() == 0) {
|
||||
virtio_blk_fuzz(s, queues, Data, Size);
|
||||
flush_events(s);
|
||||
_Exit(0);
|
||||
} else {
|
||||
flush_events(s);
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_blk_with_flag_fuzz(QTestState *s,
|
||||
const unsigned char *Data, size_t Size)
|
||||
{
|
||||
QVirtioBlk *blk = fuzz_qos_obj;
|
||||
static QVirtioBlkQueues *queues;
|
||||
|
||||
if (fork() == 0) {
|
||||
if (Size >= sizeof(uint64_t)) {
|
||||
queues = qvirtio_blk_init(blk->vdev, *(uint64_t *)Data);
|
||||
virtio_blk_fuzz(s, queues,
|
||||
Data + sizeof(uint64_t), Size - sizeof(uint64_t));
|
||||
flush_events(s);
|
||||
}
|
||||
_Exit(0);
|
||||
} else {
|
||||
flush_events(s);
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_blk_pre_fuzz(QTestState *s)
|
||||
{
|
||||
qos_init_path(s);
|
||||
counter_shm_init();
|
||||
}
|
||||
|
||||
static void drive_destroy(void *path)
|
||||
{
|
||||
unlink(path);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
static char *drive_create(void)
|
||||
{
|
||||
int fd, ret;
|
||||
char *t_path = g_strdup("/tmp/qtest.XXXXXX");
|
||||
|
||||
/* Create a temporary raw image */
|
||||
fd = mkstemp(t_path);
|
||||
g_assert_cmpint(fd, >=, 0);
|
||||
ret = ftruncate(fd, TEST_IMAGE_SIZE);
|
||||
g_assert_cmpint(ret, ==, 0);
|
||||
close(fd);
|
||||
|
||||
g_test_queue_destroy(drive_destroy, t_path);
|
||||
return t_path;
|
||||
}
|
||||
|
||||
static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
|
||||
{
|
||||
char *tmp_path = drive_create();
|
||||
|
||||
g_string_append_printf(cmd_line,
|
||||
" -drive if=none,id=drive0,file=%s,"
|
||||
"format=raw,auto-read-only=off ",
|
||||
tmp_path);
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void register_virtio_blk_fuzz_targets(void)
|
||||
{
|
||||
fuzz_add_qos_target(&(FuzzTarget){
|
||||
.name = "virtio-blk-fuzz",
|
||||
.description = "Fuzz the virtio-blk virtual queues, forking "
|
||||
"for each fuzz run",
|
||||
.pre_vm_init = &counter_shm_init,
|
||||
.pre_fuzz = &virtio_blk_pre_fuzz,
|
||||
.fuzz = virtio_blk_fork_fuzz,},
|
||||
"virtio-blk",
|
||||
&(QOSGraphTestOptions){.before = virtio_blk_test_setup}
|
||||
);
|
||||
|
||||
fuzz_add_qos_target(&(FuzzTarget){
|
||||
.name = "virtio-blk-flags-fuzz",
|
||||
.description = "Fuzz the virtio-blk virtual queues, forking "
|
||||
"for each fuzz run (also fuzzes the virtio flags)",
|
||||
.pre_vm_init = &counter_shm_init,
|
||||
.pre_fuzz = &virtio_blk_pre_fuzz,
|
||||
.fuzz = virtio_blk_with_flag_fuzz,},
|
||||
"virtio-blk",
|
||||
&(QOSGraphTestOptions){.before = virtio_blk_test_setup}
|
||||
);
|
||||
}
|
||||
|
||||
fuzz_target_init(register_virtio_blk_fuzz_targets);
|
|
@ -88,7 +88,7 @@ void qtest_quit(QTestState *s);
|
|||
* @fds: array of file descriptors
|
||||
* @fds_num: number of elements in @fds
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Sends a QMP message to QEMU with fds and returns the response.
|
||||
|
@ -101,7 +101,7 @@ QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num,
|
|||
* qtest_qmp:
|
||||
* @s: #QTestState instance to operate on.
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Sends a QMP message to QEMU and returns the response.
|
||||
|
@ -113,7 +113,7 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
|
|||
* qtest_qmp_send:
|
||||
* @s: #QTestState instance to operate on.
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Sends a QMP message to QEMU and leaves the response in the stream.
|
||||
|
@ -138,7 +138,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
|
|||
* @fds: array of file descriptors
|
||||
* @fds_num: number of elements in @fds
|
||||
* @fmt: QMP message to send to QEMU, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
* @ap: QMP message arguments
|
||||
*
|
||||
|
@ -152,7 +152,7 @@ QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num,
|
|||
* qtest_vqmp:
|
||||
* @s: #QTestState instance to operate on.
|
||||
* @fmt: QMP message to send to QEMU, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
* @ap: QMP message arguments
|
||||
*
|
||||
|
@ -167,7 +167,7 @@ QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap)
|
|||
* @fds: array of file descriptors
|
||||
* @fds_num: number of elements in @fds
|
||||
* @fmt: QMP message to send to QEMU, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
* @ap: QMP message arguments
|
||||
*
|
||||
|
@ -181,7 +181,7 @@ void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num,
|
|||
* qtest_qmp_vsend:
|
||||
* @s: #QTestState instance to operate on.
|
||||
* @fmt: QMP message to send to QEMU, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
* @ap: QMP message arguments
|
||||
*
|
||||
|
@ -636,7 +636,7 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data);
|
|||
* qtest_qmp_assert_success:
|
||||
* @qts: QTestState instance to operate on
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Sends a QMP message to QEMU and asserts that a 'return' key is present in
|
||||
|
@ -683,7 +683,7 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv,
|
|||
* @driver: Name of the device that should be added
|
||||
* @id: Identification string
|
||||
* @fmt: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Generic hot-plugging test via the device_add QMP command.
|
||||
|
|
|
@ -47,7 +47,7 @@ static inline void qtest_end(void)
|
|||
/**
|
||||
* qmp:
|
||||
* @fmt...: QMP message to send to qemu, formatted like
|
||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||
* qobject_from_jsonf_nofail(). See parse_interpolation() for what's
|
||||
* supported after '%'.
|
||||
*
|
||||
* Sends a QMP message to QEMU and returns the response.
|
||||
|
|
|
@ -70,10 +70,8 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx,
|
|||
qtest_end();
|
||||
tpm_util_swtpm_kill(swtpm_pid);
|
||||
|
||||
if (addr) {
|
||||
g_unlink(addr->u.q_unix.path);
|
||||
qapi_free_SocketAddress(addr);
|
||||
}
|
||||
g_unlink(addr->u.q_unix.path);
|
||||
qapi_free_SocketAddress(addr);
|
||||
}
|
||||
|
||||
void tpm_test_swtpm_migration_test(const char *src_tpm_path,
|
||||
|
|
|
@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM):
|
|||
name = "openbsd"
|
||||
arch = "x86_64"
|
||||
|
||||
link = "https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.iso"
|
||||
csum = "b22e63df56e6266de6bbeed8e9be0fbe9ee2291551c5bc03f3cc2e4ab9436ee3"
|
||||
link = "https://cdn.openbsd.org/pub/OpenBSD/6.8/amd64/install68.iso"
|
||||
csum = "47e291fcc2d0c1a8ae0b66329f040b33af755b6adbd21739e20bb5ad56f62b6c"
|
||||
size = "20G"
|
||||
pkgs = [
|
||||
# tools
|
||||
|
@ -37,10 +37,10 @@ class OpenBSDVM(basevm.BaseVM):
|
|||
"bash",
|
||||
"gmake",
|
||||
"gsed",
|
||||
"gettext",
|
||||
"gettext-tools",
|
||||
|
||||
# libs: usb
|
||||
"libusb1",
|
||||
"libusb1--",
|
||||
|
||||
# libs: crypto
|
||||
"gnutls",
|
||||
|
|
Loading…
Reference in New Issue