1#!/bin/bash
2set -e
3
4print_help()
5{
6    cat <<EOFHELP
7# Simple wrapper around a libfuzzer test run, as much for
8# documentation as direct use.  The idea here is really simple: build
9# for the Zephyr "native_posix" board (which is a just a x86
10# executable for the build host, not an emulated device) and run the
11# resulting zephyr.exe file.  This specifies a "fuzz_corpus" directory
12# to save the seeds that produce useful coverage output for use in
13# repeated runs (these are not particularly large, we might consider
14# curating and commiting such a seed directory to the tree).
15#
16# The tool will run until it finds a failure condition.  You will see
17# MANY errors on stdout from all the randomized input.  Don't try to
18# capture this, either let it output to a terminal or arrange to keep
19# only the last XXX lines after the tool exits.
20#
21# The only prerequisite to install is a clang compiler on the host.
22# Versions 12+ have all been observed to work.
23#
24# You will need the kconfigs specified below for correct operation,
25# but can add more at the end of this script's command line to
26# duplicate configurations as needed.  Alternatively you can pass
27# overlay files in kconfig syntax via:
28#    fuzz.sh  -t 300 -- -DOVERLAY_CONFIG=..., etc...
29EOFHELP
30}
31
32
33# To test this test, make the following change locally:
34: <<EOF_TEST_PATCH
35--- a/src/ipc/ipc3/handler.c
36+++ b/src/ipc/ipc3/handler.c
37@@ -1609,6 +1609,8 @@ void ipc_boot_complete_msg(struct ipc_cmd_hdr *header, uint32_t data)
38
39 void ipc_cmd(struct ipc_cmd_hdr *_hdr)
40 {
41+       __ASSERT(false, "test the fuzzer test");
42+
43        struct sof_ipc_cmd_hdr *hdr = ipc_from_hdr(_hdr);
44EOF_TEST_PATCH
45
46
47main()
48{
49  setup
50
51  # Parse "$@". getopts stops after '--'
52  while getopts "ho:t:" opt; do
53      case "$opt" in
54          h) print_help; exit 0;;
55          o) FUZZER_STDOUT="$OPTARG";;
56          t) TEST_DURATION="$OPTARG";;
57          *) print_help; exit 1;;
58      esac
59  done
60
61  # Pass all "$@" arguments remaining after '--' to cmake.
62  # This also drops _leading_ '--'.
63  shift $((OPTIND-1))
64
65  # Default values
66  : "${TEST_DURATION:=3}"
67  : "${FUZZER_STDOUT:=/dev/stdout}" # bashism
68
69  west build -d build-fuzz -b native_posix "$SOF_TOP"/app/ -- \
70    -DCONFIG_ASSERT=y \
71    -DCONFIG_SYS_HEAP_BIG_ONLY=y \
72    -DCONFIG_ZEPHYR_NATIVE_DRIVERS=y \
73    -DCONFIG_ARCH_POSIX_LIBFUZZER=y \
74    -DCONFIG_ARCH_POSIX_FUZZ_TICKS=100 \
75    -DCONFIG_ASAN=y "$@"
76
77  mkdir -p ./fuzz_corpus
78
79  timeoutret=0
80  date
81  timeout -k 10 "$TEST_DURATION" >"$FUZZER_STDOUT" build-fuzz/zephyr/zephyr.exe ./fuzz_corpus ||
82      timeoutret=$?
83  if [ $timeoutret -eq 124 ]; then
84      # The current ASAN seems overly sensitive: it frequently (but not always) complains
85      # loudly that it has been aborted with a so-called DEADLYSIGNAL when the timeout
86      # command above actually delivers just a gentle TERM by default. Counteracts the
87      # wrong impression this unpredictable whining can give with a proud "successfully"
88      # message.
89      printf '\n\nSuccessfully aborted fuzzer after %d seconds test duration with DEADLYSIGNAL\n' \
90             "$TEST_DURATION"
91  else
92      die 'zephyr.exe fuzzer stopped before timing out: %d\n' $timeoutret
93  fi
94  date
95}
96
97
98setup()
99{
100    SOF_TOP=$(cd "$(dirname "$0")/.." && pwd)
101    export SOF_TOP
102
103    export ZEPHYR_TOOLCHAIN_VARIANT=llvm
104
105    # ZEPHYR_BASE. Does not seem required.
106    local WS_TOP
107    WS_TOP=$(cd "$SOF_TOP"; west topdir)
108    # The manifest itself is not a west project
109    ZEP_DIR=$(2>/dev/null west list -f '{path}' zephyr || echo 'zephyr')
110    export ZEPHYR_BASE="$WS_TOP/$ZEP_DIR"
111
112}
113
114die()
115{
116        >&2 printf '%s ERROR: ' "$0"
117        # We want die() to be usable exactly like printf
118        # shellcheck disable=SC2059
119        >&2 printf "$@"
120        >&2 printf '\n'
121        exit 1
122}
123
124main "$@"
125