Commit 050565e5 authored by Jim Harris's avatar Jim Harris Committed by Changpeng Liu
Browse files

test/nvmf: fuzz nvmf target using LLVM's libFuzzer



LLVM provides libFuzzer which does coverage-guided
fuzzing of a library or application under test.  For
SPDK, we can use this as a new and better way to
generate random commands to the SPDK nvmf target.

By default, libFuzzer provides the main() and your
source file just provides the function called by
LLVM for each iteration of random data.  But this
doesn't really work for SPDK since we need to start
the app framework and the nvmf target.  So we
specify -fsanitizer=fuzzer-no-link, explicitly
specify the location of the fuzzer_no_main library
and then call LLVMFuzzerRunDriver to start the
fuzzing process once we are ready.

Since this is all coverage-guided, we invoke the
fuzzer inside the nvmf target application.  So this
patch creates a new target application called
'llvm_nvme_fuzz'. One core is needed to run the
nvmf target, then we spawn a pthread to run the
fuzzer against it.

Currently there are two fuzzers defined.  Fuzzer 0
does random testing of admin commands.  Fuzzer 1
is focused solely on GET_LOG_PAGE and fuzzes a
smaller subset of the bytes in the spdk_nvme_cmd.

Additional fuzzers can be added in the future for
other commands, testing I/O queues, data payloads,
etc.

You do need to specify CC and CXX when running
configure, as well as specify the location of the
special clang_rt.fuzz_no_main library. The path of
that library is dependent on your clang version and
architecture. If using clang-12 on x86_64 platform,
it will look like:

CC=clang-12 CXX=clang++-12 ./configure --with-fuzzer= \
  /usr/lib/llvm-12/lib/clang/12.0.0/lib/linux/libclang_rt.fuzzer_no_main-x86_64.a

Then just do the following to demonstrate the fuzzer
tool.

make
test/nvmf/target/llvm_nvme_fuzz.sh --time=60 --fuzzer=0

Signed-off-by: default avatarJim Harris <james.r.harris@intel.com>
Change-Id: Iee0997501893ac284a3947a1db7a155c5ceb7849
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/10038


Reviewed-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent 723adbaf
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@ CONFIG_ASAN=n
# Build with Undefined Behavior Sanitizer enabled
CONFIG_UBSAN=n

# Build with LLVM fuzzing enabled
CONFIG_FUZZER=n
CONFIG_FUZZER_LIB=

# Build with Thread Sanitizer enabled
CONFIG_TSAN=n

+22 −0
Original line number Diff line number Diff line
@@ -101,6 +101,10 @@ function usage() {
	echo " --without-wpdk            The argument must be a directory containing lib and include."
	echo " --with-usdt               Build with userspace DTrace probes enabled."
	echo " --without-usdt            No path required."
	echo " --with-fuzzer             Build with LLVM fuzzing enabled."
	echo "                           Path to clang_rt.fuzzer_no_main library required."
	echo "                           Requires setting CC and CXX to clang."
	echo "                           (Typically /usr/lib/llvm-VER/lib/clang/VER/lib/linux/libclang_rt.fuzzer_no_main-ARCH.a)"
	echo ""
	echo "Environment variables:"
	echo ""
@@ -482,6 +486,19 @@ for i in "$@"; do
		--without-usdt)
			CONFIG[USDT]=n
			;;
		--with-fuzzer)
			echo "Must specify fuzzer library path with --with-fuzzer"
			usage
			exit 1
			;;
		--with-fuzzer=*)
			CONFIG[FUZZER]=y
			CONFIG[FUZZER_LIB]=$(readlink -f ${i#*=})
			;;
		--without-fuzzer)
			CONFIG[FUZZER]=n
			CONFIG[FUZZER_LIB]=
			;;
		--)
			break
			;;
@@ -888,6 +905,11 @@ if [ "${CONFIG[CET]}" = "y" ]; then
	fi
fi

if [[ "${CONFIG[FUZZER]}" = "y" && "$CC_TYPE" != "clang" ]]; then
	echo "--with-fuzzer requires setting CC and CXX to clang."
	exit 1
fi

if [[ "${CONFIG[ISAL]}" = "y" ]]; then
	if [ ! -f "$rootdir"/isa-l/autogen.sh ]; then
		echo "ISA-L was not found; To install ISA-L run:"
+6 −0
Original line number Diff line number Diff line
@@ -279,6 +279,12 @@ COMMON_CFLAGS += -fsanitize=thread
LDFLAGS += -fsanitize=thread
endif

ifeq ($(CONFIG_FUZZER),y)
COMMON_CFLAGS += -fsanitize=fuzzer-no-link
LDFLAGS += -fsanitize=fuzzer-no-link
SYS_LIBS += $(CONFIG_FUZZER_LIB)
endif

SPDK_GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null)
ifneq (, $(SPDK_GIT_COMMIT))
COMMON_CFLAGS += -DSPDK_GIT_COMMIT=$(SPDK_GIT_COMMIT)
+4 −0
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@ include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y += nvme_fuzz
DIRS-y += iscsi_fuzz

ifeq ($(CONFIG_FUZZER),y)
DIRS-y += llvm_nvme_fuzz
endif

ifeq ($(OS),Linux)
DIRS-$(CONFIG_VIRTIO) += vhost_fuzz
endif
+1 −0
Original line number Diff line number Diff line
llvm_nvme_fuzz
Loading