From 3466e94517715323832346a6a08370e84ff0fbcf Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Wed, 25 Jan 2023 01:01:09 +0000 Subject: [PATCH 1/6] Fix fs_setup for KASan in LKL. Pad '/init' string in fs_setup functio to make sure it's 8 bytes, otherwise KASan would emit an error. The kernel's strncpy implementation attempts to read 8 bytes at once and, thus, triggers KASan violation for the 6-byte string. Signed-off-by: Eugene Rodionov --- arch/lkl/kernel/setup.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/lkl/kernel/setup.c b/arch/lkl/kernel/setup.c index 1d40c32cf195c0..29afdd9c8e323a 100644 --- a/arch/lkl/kernel/setup.c +++ b/arch/lkl/kernel/setup.c @@ -200,7 +200,11 @@ static int __init fs_setup(void) { int fd; - fd = sys_open("/init", O_CREAT, 0700); + // Pad '/init' to make sure it's 8 bytes, otherwise KASan would + // emit an error. The kernel's strncpy implementation attempts to read + // 8 bytes at once and, thus, triggers KASan violation for the 6-byte + // string. + fd = sys_open("/init\0\0", O_CREAT, 0700); WARN_ON(fd < 0); sys_close(fd); From 03f81ba5d875bbf9435977e1fbaf1f6d75271b47 Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Wed, 25 Jan 2023 01:06:57 +0000 Subject: [PATCH 2/6] Add LKL kernel config options for fuzzing. This change adds two LKL kernel config options: * LKL_FUZZING which enables libFuzzer fuzzing instrumentation for the kernel code * LKL_LINE_COV which enables code coverage instrumentation for the fuzz targets. These kernel config options are enabled via environment variables which should be set up either in the environment or in the make files. To build LKL fuzzers run (currently there are no fuzzers checked in yet): make -C tools/lkl LKL_FUZZING=1 fuzzers Signed-off-by: Xuan Xing Signed-off-by: Eugene Rodionov --- arch/lkl/Kconfig | 20 ++++++++++++++++++++ arch/lkl/Makefile | 24 +++++++++++++++++++++++- tools/lkl/Makefile | 6 ++++++ tools/lkl/Makefile.autoconf | 11 +++++++++++ tools/lkl/Targets | 3 +++ 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/arch/lkl/Kconfig b/arch/lkl/Kconfig index 1ab00b45e1dd07..cb319652ff49fa 100644 --- a/arch/lkl/Kconfig +++ b/arch/lkl/Kconfig @@ -42,6 +42,26 @@ config LKL select GENERIC_STRNLEN_USER select HAVE_ARCH_KASAN +config LKL_FUZZING + bool "LLVM fuzzing instrumentation" + default n + help + This configuration option enables fuzzing instrumentation + for the Linux kernel source files to enable coverage-guided + fuzzing. At the moment LKL supports libFuzzer fuzzing + engine only. + +config LKL_LINE_COV + bool "Code coverage instrumentation for fuzzers" + depends on LKL_FUZZING && CC_IS_CLANG + default n + help + This configuration option enables line code coverage + instrumentation for the Linux kernel to generate fuzzing + code coverage reports. When this option is enabled the + kernel source files are built with LLVM SanitizerCoverage + instrumentation. + config OUTPUT_FORMAT string "Output format" default "" diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile index 2f31aa33eb2826..33919e41a0ad34 100644 --- a/arch/lkl/Makefile +++ b/arch/lkl/Makefile @@ -27,6 +27,28 @@ else # e.g., FreeBSD NPROC=$(shell sysctl -n hw.ncpu) endif +ifdef CONFIG_CC_IS_CLANG +# LKL fuzzing is based on libFuzzer which depends on clang compiler. +ifdef CONFIG_LKL_FUZZING +# This makes sure assembly code is compiled with clang's integerated assembler +# so the result object files are compatible with the fuzzing flag. +KBUILD_CFLAGS += -integrated-as + +# We want debug symbols for fuzzing (e.g. better stack trace in gdb). +KBUILD_CFLAGS += -g + +# Enabling libfuzzer instrumentation +KBUILD_CFLAGS += -fsanitize=fuzzer-no-link + +KEEP_EH_FRAMES := true +endif + +# This flag enables clang's extra instrumentation for visualizing line coverage. +ifdef CONFIG_LKL_LINE_COV +KBUILD_CFLAGS += -fprofile-instr-generate -fcoverage-mapping +endif +endif + LDFLAGS_vmlinux += -r LKL_ENTRY_POINTS := lkl_start_kernel lkl_sys_halt lkl_syscall lkl_trigger_irq \ lkl_get_free_irq lkl_put_irq lkl_is_running lkl_bug lkl_printf \ @@ -45,7 +67,7 @@ core-y += arch/lkl/drivers/ all: lkl.o arch/lkl/include/generated/uapi/asm/syscall_defs.h lkl.o: vmlinux - $(OBJCOPY) -R .eh_frame -R .syscall_defs $(foreach sym,$(LKL_ENTRY_POINTS),-G$(prefix)$(sym)) --prefix-symbols=$(prefix) vmlinux lkl.o + $(OBJCOPY) $(if $(KEEP_EH_FRAMES),,-R .eh_frame) -R .syscall_defs $(foreach sym,$(LKL_ENTRY_POINTS),-G$(prefix)$(sym)) --prefix-symbols=$(prefix) vmlinux lkl.o arch/lkl/include/generated/uapi/asm/syscall_defs.h: vmlinux $(OBJCOPY) -j .syscall_defs -O binary --set-section-flags .syscall_defs=alloc $< $@ diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 849912b63a4af8..a62b31b5b557f8 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -140,6 +140,12 @@ install: headers_install libraries_install programs_install run-tests: ./tests/run.py $(tests) +# Enable libFuzzer fuzzing instrumentation for the LKL fuzzers +$(OUTPUT)fuzzers/%$(EXESUF): LDFLAGS += -fsanitize=fuzzer --coverage + +FUZZ_TARGETS := $(fuzzers-y:%=$(OUTPUT)%$(EXESUF)) +fuzzers: $(FUZZ_TARGETS) + FORCE: ; .PHONY: all clean FORCE run-tests .PHONY: headers_install libraries_install programs_install install diff --git a/tools/lkl/Makefile.autoconf b/tools/lkl/Makefile.autoconf index 56edf8dda0540e..979e829ec5a315 100644 --- a/tools/lkl/Makefile.autoconf +++ b/tools/lkl/Makefile.autoconf @@ -169,7 +169,18 @@ define do_autoconf_llvm $(eval LD_FMT := $(call llvm_target_to_ld_fmt)) endef +define do_autoconf_fuzzing + export LLVM := 1 + export CROSS_COMPILE := x86_64-linux-gnu + $(eval LLVM := 1) + $(eval CROSS_COMPILE := x86_64-linux-gnu) + $(eval kasan := yes) + $(call set_kernel_config,LKL_FUZZING,y) + $(if $(LKL_LINE_COV),$(call set_kernel_config,LKL_LINE_COV,y)) +endef + define do_autoconf + $(if $(LKL_FUZZING),$(call do_autoconf_fuzzing)) $(if $(LLVM),$(call do_autoconf_llvm),$(call do_autoconf_gnu)) $(eval EXEC_FMT := $(shell echo $(LD_FMT) | cut -d "-" -f1)) $(call set_kernel_config,OUTPUT_FORMAT,\"$(LD_FMT)\") diff --git a/tools/lkl/Targets b/tools/lkl/Targets index 3e424699e1934c..d7eafcf3a714b5 100644 --- a/tools/lkl/Targets +++ b/tools/lkl/Targets @@ -26,3 +26,6 @@ progs-y += tests/disk progs-y += tests/disk-vfio-pci progs-y += tests/net-test progs-y += tests/config + +# LKL fuzzers +fuzzers-y += From ff76e8def397156a7fc545eec0edb36e11fb251f Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Wed, 25 Jan 2023 01:23:51 +0000 Subject: [PATCH 3/6] Implement a workaround for LKL incremental linking with group sections. Incremental linking in lld doesn't work well with the group sections (GRP_COMDAT) present in the input files -- in this case the linker semantics is unclear and not defined. This causes problems for building LKL fuzzers with libFuzzer instrumentation (-fsanitize=fuzzer) which generates object files with group sections due to the SanitizerCoverage instrumentation. This CL implements a workaround for this issue by introducing another post-link vmlinux pass. 1) First, vmlinux is linked with an empty linker script to avoid merging input sections 2) Then, .group sections (GRP_COMDAT) are stripped from the vmlinux image with `objcopy --remove-section=.group`. 3) Finally, we relink the vmlinux with the original linker scrip (using incremental linking). Signed-off-by: Eugene Rodionov --- arch/lkl/Makefile.postlink | 53 +++++++++++++++++++ arch/lkl/kernel/Makefile | 1 + .../lkl/kernel/vmlinux-fuzzing-postlink.lds.S | 3 ++ arch/lkl/kernel/vmlinux.lds.S | 10 ++++ 4 files changed, 67 insertions(+) create mode 100644 arch/lkl/Makefile.postlink create mode 100644 arch/lkl/kernel/vmlinux-fuzzing-postlink.lds.S diff --git a/arch/lkl/Makefile.postlink b/arch/lkl/Makefile.postlink new file mode 100644 index 00000000000000..4c7e9e42feb858 --- /dev/null +++ b/arch/lkl/Makefile.postlink @@ -0,0 +1,53 @@ +# The post-link vmlinux pass is only relevant when LKL is being built with +# libFuzzer instrumentation (-fsanitize=fuzzer). SanitizerCoverage +# emits GRP_COMDAT sections in the generated object files to group sections +# with instrumented code, coverage counters and relocations respectively. +# The linker semantics for incremental linking with section groups is unclear +# and results in the link-time errors. This extra post-link pass implements a +# workaround for this problem: +# +# 1) Vmlinux is incrementally linked with an empty linker script. Thus, the input +# sections aren't merged in the generated vmlinux image (ld and lld do still +# merge some common sections such as .text. .rela.text, .data and etc but it +# doesn't casue a problem). +# +# 2) Remove GRP_COMDAT group sections with name .group from the original vmlinux. +# +# 3) Relink the stripped vmlinux using the original LKL linker script. +# As there is no group sections in the input object, merging sections doesn't +# cause any issues. +# +# TODO: The above steps aren't solution to the problem but rather a short-term +# workaround. At the moment removing .group sections from the incrementally +# linked vmlinux doesn't seem to cause any issues as the only GRP_COMDAT sections +# present in the object files are the ones emited by SanitizerCoverage +# instrumentation. However, here is a couple of other things to take into +# consideration: +# +# * --gc-sections linker option could remove __sancov_pcs sections, thus, this +# option shouldn't be used when building fuzzers. +# +# * If the linker removes a function there might be 'dangling' __sancov_xxx +# sections. However, this isn't an issue for fuzzing. +# +# * __sancov_pcs and __sancov_cntrs sections need to be merged in the corresponding +# order (so that the value of the counters match to values of PC of their +# corresponding basic blocks). With this workaround this seem to be the case +# (while the linker might not guarantee this). However, even if these sections +# aren't merged in the order, technically, this won't be a problem for fuzzing. +ifneq ($(LKL_FUZZING),) +include scripts/Kbuild.include + +LKL_FUZZING_LDS := arch/lkl/kernel/vmlinux-fuzzing-postlink.lds + +.PHONY: vmlinux +vmlinux: $(LKL_FUZZING_LDS) + $(OBJCOPY) --remove-section=.group $@ $@.no_groups + $(LD) -r --build-id=sha1 -o $@ -T $< $@.no_groups + +clean: ; rm -rf $(LKL_FUZZING_LDS) vmlinux.no_groups +else # LKL_FUZZING +clean: ; +endif # LKL_FUZZING + +.PHONY: clean \ No newline at end of file diff --git a/arch/lkl/kernel/Makefile b/arch/lkl/kernel/Makefile index bacb9572256e56..80a557a92df78f 100644 --- a/arch/lkl/kernel/Makefile +++ b/arch/lkl/kernel/Makefile @@ -1,4 +1,5 @@ extra-y := vmlinux.lds +extra-y += vmlinux-fuzzing-postlink.lds KASAN_SANITIZE_init.o := n KASAN_SANITIZE_stacktrace.o := n diff --git a/arch/lkl/kernel/vmlinux-fuzzing-postlink.lds.S b/arch/lkl/kernel/vmlinux-fuzzing-postlink.lds.S new file mode 100644 index 00000000000000..d4e78b5b2e7ed9 --- /dev/null +++ b/arch/lkl/kernel/vmlinux-fuzzing-postlink.lds.S @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define LKL_FUZZING_POSTLINK +#include "vmlinux.lds.S" diff --git a/arch/lkl/kernel/vmlinux.lds.S b/arch/lkl/kernel/vmlinux.lds.S index cf93cc292ea21d..167a5fb5e5ccb7 100644 --- a/arch/lkl/kernel/vmlinux.lds.S +++ b/arch/lkl/kernel/vmlinux.lds.S @@ -1,3 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// When building LKL with libFuzzer instrumentation (-fsanitize=fuzzer) provide +// an empty linker script to avoid section merging. For additional information +// refer to arch/lkl/Makefile.postlink. +#if !defined(CONFIG_LKL_FUZZING) || defined(LKL_FUZZING_POSTLINK) #include #include #include @@ -67,5 +72,10 @@ SECTIONS STABS_DEBUG DWARF_DEBUG + // Don't include DISCARDS section to build the target with code coverage. +#if !defined(CONFIG_LKL_FUZZING) DISCARDS +#endif // !defined(CONFIG_LKL_FUZZING) } + +#endif // !defined(CONFIG_LKL_FUZZING) || defined(LKL_FUZZING_POSTLINK) From 8a8b824ab2c9b80fbfd4d50a21a017bbbed195aa Mon Sep 17 00:00:00 2001 From: Xuan Xing Date: Wed, 25 Jan 2023 01:30:52 +0000 Subject: [PATCH 4/6] Add custom LKL defconfig for fuzzing. As LKL fuzzers might be built with different kernel config options than the ones provided in the defconfig this change introduces an additional fuzzing_defconfig file. It is assumed that all LKL fuzzers are built using the same kernel config. Signed-off-by: Xuan Xing Signed-off-by: Eugene Rodionov --- arch/lkl/configs/fuzzing_defconfig | 193 +++++++++++++++++++++++++++++ tools/lkl/Makefile.autoconf | 1 + 2 files changed, 194 insertions(+) create mode 100644 arch/lkl/configs/fuzzing_defconfig diff --git a/arch/lkl/configs/fuzzing_defconfig b/arch/lkl/configs/fuzzing_defconfig new file mode 100644 index 00000000000000..8e2b348c4dd201 --- /dev/null +++ b/arch/lkl/configs/fuzzing_defconfig @@ -0,0 +1,193 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_NO_HZ_IDLE=y +# CONFIG_SYSFS_SYSCALL is not set +CONFIG_KALLSYMS_USE_DATA_SECTION=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_BASE_FULL is not set +# CONFIG_FUTEX is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_AIO is not set +# CONFIG_ADVISE_SYSCALLS is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_BLK_DEV_BSG is not set +CONFIG_NET=y +CONFIG_INET=y +# CONFIG_WIRELESS is not set +# CONFIG_UEVENT_HELPER is not set +# CONFIG_FW_LOADER is not set +CONFIG_VIRTIO_BLK=y +CONFIG_NETDEVICES=y +CONFIG_VIRTIO_NET=y +CONFIG_VHOST_VSOCK=y +CONFIG_VIRTIO_VSOCKETS=y +CONFIG_VSOCKETS=y +# CONFIG_ETHERNET is not set +# CONFIG_WLAN is not set +# CONFIG_VT is not set +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +# CONFIG_FILE_LOCKING is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +CONFIG_VFAT_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=y +CONFIG_NLS_CODEPAGE_775=y +CONFIG_NLS_CODEPAGE_850=y +CONFIG_NLS_CODEPAGE_852=y +CONFIG_NLS_CODEPAGE_855=y +CONFIG_NLS_CODEPAGE_857=y +CONFIG_NLS_CODEPAGE_860=y +CONFIG_NLS_CODEPAGE_861=y +CONFIG_NLS_CODEPAGE_862=y +CONFIG_NLS_CODEPAGE_863=y +CONFIG_NLS_CODEPAGE_864=y +CONFIG_NLS_CODEPAGE_865=y +CONFIG_NLS_CODEPAGE_866=y +CONFIG_NLS_CODEPAGE_869=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_CODEPAGE_950=y +CONFIG_NLS_CODEPAGE_932=y +CONFIG_NLS_CODEPAGE_949=y +CONFIG_NLS_CODEPAGE_874=y +CONFIG_NLS_ISO8859_8=y +CONFIG_NLS_CODEPAGE_1250=y +CONFIG_NLS_CODEPAGE_1251=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=y +CONFIG_NLS_ISO8859_3=y +CONFIG_NLS_ISO8859_4=y +CONFIG_NLS_ISO8859_5=y +CONFIG_NLS_ISO8859_6=y +CONFIG_NLS_ISO8859_7=y +CONFIG_NLS_ISO8859_9=y +CONFIG_NLS_ISO8859_13=y +CONFIG_NLS_ISO8859_14=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_KOI8_R=y +CONFIG_NLS_KOI8_U=y +CONFIG_NLS_MAC_ROMAN=y +CONFIG_NLS_MAC_CELTIC=y +CONFIG_NLS_MAC_CENTEURO=y +CONFIG_NLS_MAC_CROATIAN=y +CONFIG_NLS_MAC_CYRILLIC=y +CONFIG_NLS_MAC_GAELIC=y +CONFIG_NLS_MAC_GREEK=y +CONFIG_NLS_MAC_ICELAND=y +CONFIG_NLS_MAC_INUIT=y +CONFIG_NLS_MAC_ROMANIAN=y +CONFIG_NLS_MAC_TURKISH=y +CONFIG_NLS_UTF8=y +CONFIG_HZ_100=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_REDUCED=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set + +# +# HID support +# +CONFIG_HID=y +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_GENERIC=y +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_ASUS=y +CONFIG_HID_AUREAL=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_COUGAR=y +CONFIG_HID_MACALLY=y +CONFIG_HID_CMEDIA=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_GEMBIRD=y +CONFIG_HID_GFRM=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_WALTOP=y +CONFIG_HID_VIEWSONIC=y +CONFIG_HID_GYRATION=y +CONFIG_HID_ICADE=y +CONFIG_HID_ITE=y +CONFIG_HID_JABRA=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LED=y +CONFIG_HID_LENOVO=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_HIDPP=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MALTRON=y +CONFIG_HID_MAYFLASH=y +CONFIG_HID_REDRAGON=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTI=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LCD=y +CONFIG_HID_PICOLCD_LEDS=y +CONFIG_HID_PLANTRONICS=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_STEAM=y +CONFIG_HID_STEELSERIES=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_RMI=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THINGM=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_UDRAW_PS3=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_XINMO=y +CONFIG_HID_ZEROPLUS=y +CONFIG_ZEROPLUS_FF=y +CONFIG_HID_ZYDACRON=y +CONFIG_HID_SENSOR_HUB=y +CONFIG_HID_SENSOR_CUSTOM_SENSOR=y +CONFIG_HID_ALPS=y +# end of Special HID drivers +# end of HID support + +CONFIG_KASAN=y +CONFIG_KASAN_STACK_ENABLE=y +CONFIG_KASAN_GENERIC=y +CONFIG_KASAN_OUTLINE=y +CONFIG_FRAME_WARN=0 diff --git a/tools/lkl/Makefile.autoconf b/tools/lkl/Makefile.autoconf index 979e829ec5a315..3c0d07ac6835d6 100644 --- a/tools/lkl/Makefile.autoconf +++ b/tools/lkl/Makefile.autoconf @@ -170,6 +170,7 @@ define do_autoconf_llvm endef define do_autoconf_fuzzing + export KCONFIG := fuzzing_defconfig export LLVM := 1 export CROSS_COMPILE := x86_64-linux-gnu $(eval LLVM := 1) From 63fc53a34fcfab8da9b13afd7f499ce2eebbbb3c Mon Sep 17 00:00:00 2001 From: Xuan Xing Date: Wed, 25 Jan 2023 01:38:28 +0000 Subject: [PATCH 5/6] Add LKL fuzzer for HID. This fuzzer fuzzes Linux kernel HID subsystem via /dev/uhid device. To build the fuzzer: make -C tools/lkl LKL_FUZZING=1 fuzzers Signed-off-by: Xuan Xing --- arch/lkl/scripts/headers_install.py | 2 + tools/lkl/Targets | 3 +- tools/lkl/fuzzers/hid/Build | 2 + tools/lkl/fuzzers/hid/hid-fuzzer.c | 349 ++++++++++++++++++++++++++++ tools/lkl/fuzzers/hid/hid-fuzzer.h | 8 + tools/lkl/fuzzers/hid/seeds/1 | Bin 0 -> 127 bytes tools/lkl/fuzzers/hid/seeds/2 | Bin 0 -> 88 bytes tools/lkl/fuzzers/hid/seeds/3 | Bin 0 -> 50 bytes tools/lkl/fuzzers/hid/seeds/4 | Bin 0 -> 137 bytes tools/lkl/fuzzers/hid/seeds/5 | Bin 0 -> 38 bytes tools/lkl/fuzzers/hid/seeds/6 | Bin 0 -> 67 bytes 11 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 tools/lkl/fuzzers/hid/Build create mode 100644 tools/lkl/fuzzers/hid/hid-fuzzer.c create mode 100644 tools/lkl/fuzzers/hid/hid-fuzzer.h create mode 100644 tools/lkl/fuzzers/hid/seeds/1 create mode 100644 tools/lkl/fuzzers/hid/seeds/2 create mode 100644 tools/lkl/fuzzers/hid/seeds/3 create mode 100644 tools/lkl/fuzzers/hid/seeds/4 create mode 100644 tools/lkl/fuzzers/hid/seeds/5 create mode 100644 tools/lkl/fuzzers/hid/seeds/6 diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py index e22457d46a7924..36e965098a3a1c 100755 --- a/arch/lkl/scripts/headers_install.py +++ b/arch/lkl/scripts/headers_install.py @@ -128,6 +128,8 @@ def replace(h): find_headers("arch/lkl/include/uapi/asm/syscalls.h") headers.add("arch/lkl/include/uapi/asm/host_ops.h") +find_headers("include/uapi/linux/uhid.h") +find_headers("include/uapi/linux/input-event-codes.h") if 'LKL_INSTALL_ADDITIONAL_HEADERS' in os.environ: with open(os.environ['LKL_INSTALL_ADDITIONAL_HEADERS'], 'rU') as f: diff --git a/tools/lkl/Targets b/tools/lkl/Targets index d7eafcf3a714b5..7da425e774da91 100644 --- a/tools/lkl/Targets +++ b/tools/lkl/Targets @@ -28,4 +28,5 @@ progs-y += tests/net-test progs-y += tests/config # LKL fuzzers -fuzzers-y += +fuzzers-y += fuzzers/hid/hid-fuzzer + diff --git a/tools/lkl/fuzzers/hid/Build b/tools/lkl/fuzzers/hid/Build new file mode 100644 index 00000000000000..b9fb6a4e0d6ed5 --- /dev/null +++ b/tools/lkl/fuzzers/hid/Build @@ -0,0 +1,2 @@ +hid-fuzzer-y += hid-fuzzer.o +CFLAGS_hid-fuzzer.o += -O0 diff --git a/tools/lkl/fuzzers/hid/hid-fuzzer.c b/tools/lkl/fuzzers/hid/hid-fuzzer.c new file mode 100644 index 00000000000000..fa7669076f4cb8 --- /dev/null +++ b/tools/lkl/fuzzers/hid/hid-fuzzer.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hid-fuzzer.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX_EVENT_SIZE 128 + +#define LKL_CALL(op) lkl_sys_##op + +#define LOG(fmt, ...) \ + do { \ + if (g_log_enabled) {\ + printf(fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_BYTES(title, data, size) \ + do { \ + if (g_log_enabled) {\ + dump_bytes(title, data, size); \ + } \ + } while (0) + +bool g_log_enabled = true; + +void dump_bytes(const char *title, const void *data, size_t size) +{ + const int kBytesPerLine = 16; // 16 bytes per line + const int kCharPerByte = 3; // format string: " %02X" + const int kAddrWidth = 9; // format string: "%08X:" + + const uint8_t *p = (const uint8_t *)data; + char line[kAddrWidth + kCharPerByte * kBytesPerLine + kBytesPerLine + 2]; + + printf("%s:size=%zu\n", title, size); + for (size_t i = 0; i < size; i++) { + size_t col = i % kBytesPerLine; + + if (col == 0) { + memset(line, ' ', sizeof(line)); + snprintf(line, sizeof(line), "%08zX: ", i); + } + + // Hex code display + snprintf(&line[kAddrWidth + col * kCharPerByte], + sizeof(line) - (kAddrWidth + col * kCharPerByte), " %02X", *p); + + // Printable display + line[kAddrWidth + kCharPerByte * kBytesPerLine + col + 1] = isprint(*p) ? *p : '.'; + + // Line ending + if (col == (kBytesPerLine - 1) || i == (size - 1)) { + // This erases the '\0' added by snprintf right after hex code + line[kAddrWidth + (col + 1) * kCharPerByte] = ' '; + + // This adds the '\0' at the end of entire line + line[kAddrWidth + kCharPerByte * kBytesPerLine + kBytesPerLine + 1] = '\0'; + printf("%s\n", line); + } + + p++; + } + printf("\n"); +} + +static int uhid_write(int fd, const struct lkl_uhid_event *ev) +{ + int size = sizeof(*ev); + ssize_t ret = LKL_CALL(write)(fd, (const char *)ev, size); + + if (ret < 0) { + LOG("Cannot write to uhid: %d\n", errno); + return ret; + } else if (ret != sizeof(*ev)) { + LOG("Wrong size written to uhid: %ld != %lu\n", ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int uhid_create(int fd, uint16_t vid, uint16_t pid, + const void *data, size_t size) +{ + struct lkl_uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = LKL_UHID_CREATE; + strcpy((char *)ev.u.create.name, "test-uhid-device"); + ev.u.create.rd_data = (void *)data; + ev.u.create.rd_size = size; + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = vid; + ev.u.create.product = pid; + ev.u.create.version = 0; + ev.u.create.country = 0; + + return uhid_write(fd, &ev); +} + +static uint32_t fix_uhid_event_type(uint32_t type) +{ + static uint32_t event_map[] = { + LKL_UHID_INPUT, + LKL_UHID_INPUT2, + LKL_UHID_GET_REPORT_REPLY, + LKL_UHID_SET_REPORT_REPLY + }; + + int index = type % ARRAY_SIZE(event_map); + return event_map[index]; +} + +static const char *event_type_to_name(uint32_t type) +{ + if (type == LKL_UHID_INPUT) + return "UHID_INPUT"; + + if (type == LKL_UHID_INPUT2) + return "UHID_INPUT2"; + + if (type == LKL_UHID_GET_REPORT_REPLY) + return "UHID_GET_REPORT_REPLY"; + + if (type == LKL_UHID_SET_REPORT_REPLY) + return "UHID_SET_REPORT_REPLY"; + + return "UNKNOWN"; +} + +// return consumed data size +static int init_uhid_message(const uint8_t *data, size_t size, struct lkl_uhid_event *ev) +{ + memset(ev, 0, sizeof(*ev)); + + size_t ev_size = MIN(MAX_EVENT_SIZE, sizeof(*ev)); + + ev_size = MIN(ev_size, size); + + if (ev_size < sizeof(ev->type)) // no enough data + return 0; + + memcpy(ev, data, ev_size); + ev->type = fix_uhid_event_type(ev->type); + + return ev_size; +} + +static void uhid_destroy(int fd) +{ + struct lkl_uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +struct fuzz_data_t { + uint8_t rdesc_sz; + uint32_t msg_sz; + uint16_t vid; + uint16_t pid; + uint8_t payload[0]; +}; + +static int fuzz_data_fixup(struct fuzz_data_t *data, size_t data_sz) +{ + if (data_sz < sizeof(struct fuzz_data_t)) + return 0; + + if (data->rdesc_sz > 255) + data->rdesc_sz = 255; + + size_t sz = data_sz - sizeof(struct fuzz_data_t); + + if (data->rdesc_sz > sz) + data->rdesc_sz = sz; + + sz -= data->rdesc_sz; + + if (data->msg_sz > sz) + data->msg_sz = sz; + + return 1; +} + +static int uhid_fuzz(struct fuzz_data_t *data) +{ + if (data->rdesc_sz == 0) { + LOG("Empty fuzz dat\n"); + return 0; + } + + LOG("VID=%04X, PID=%04X, RDESC: %u bytes, msg: %u byetes\n", + data->vid, data->pid, data->rdesc_sz, data->msg_sz); + + LOG_BYTES("RDESC:", data->payload, data->rdesc_sz); + + int fd = LKL_CALL(open)("/dev/uhid", O_RDWR | O_CLOEXEC, 0); + + if (fd < 0) { + LOG("Cannot open /dev/uhid\n"); + return -1; + } + + int ret = uhid_create(fd, data->vid, data->pid, + data->payload, data->rdesc_sz); + + if (ret) { + close(fd); + LOG("Creating uhid device failed, %d\n", ret); + return -1; + } + + uint8_t *msg_data = data->payload + data->rdesc_sz; + uint32_t msg_data_size = data->msg_sz; + + while (msg_data_size > 0) { + struct lkl_uhid_event ev; + int consume = init_uhid_message(msg_data, msg_data_size, &ev); + + if (consume == 0) { // no more data + break; + } + + LOG("TYPE: %s ", event_type_to_name(ev.type)); + LOG_BYTES("DATA:", msg_data, consume); + msg_data += consume; + msg_data_size -= consume; + + uhid_write(fd, &ev); + } + + uhid_destroy(fd); + LKL_CALL(close)(fd); + return 0; +} + +static int initialize_lkl(void) +{ + if (!g_log_enabled) + lkl_host_ops.print = NULL; + + int ret = lkl_init(&lkl_host_ops); + + if (ret) { + LOG("lkl_init failed\n"); + return -1; + } + + ret = lkl_start_kernel("mem=50M kasan.fault=panic"); + if (ret) { + LOG("lkl_start_kernel failed\n"); + lkl_cleanup(); + return -1; + } + + lkl_mount_fs("sysfs"); + lkl_mount_fs("proc"); + lkl_mount_fs("dev"); + + // This is defined in miscdevice.h which is, however, not visible to + // userspace code. + #define UHID_MINOR 239 + + dev_t dev = makedev(MISC_MAJOR, UHID_MINOR); + int mknod_result = LKL_CALL(mknodat)(AT_FDCWD, "/dev/uhid", + S_IFCHR | 0600 /* S_IRUSR | S_IWUSR */, dev); + + if (mknod_result != 0) { + LOG("Create device file failed\n"); + return -1; + } + + return 0; +} + +void flush_coverage(void) +{ + LOG("Flushing coverage data...\n"); + __llvm_profile_write_file(); + LOG("Done...\n"); +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + for (int i = 0; i < *argc; i++) { + if (strcmp((*argv)[i], "-quiet=1") == 0) { + g_log_enabled = false; + break; + } + } + + initialize_lkl(); + + __llvm_profile_initialize_file(); + atexit(flush_coverage); + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + static int iter; + uint8_t data[sizeof(struct fuzz_data_t) + 128] = {0}; + + if (Size > sizeof(data)) + Size = sizeof(data); + + memcpy(data, Data, Size); + + struct fuzz_data_t *fuzz_data = (struct fuzz_data_t *)data; + int success = fuzz_data_fixup(fuzz_data, Size); + + if (success) { + uhid_fuzz(fuzz_data); + + iter++; + if (iter > 1000) { + flush_coverage(); + iter = 0; + } + } + + return 0; +} + diff --git a/tools/lkl/fuzzers/hid/hid-fuzzer.h b/tools/lkl/fuzzers/hid/hid-fuzzer.h new file mode 100644 index 00000000000000..1a61fa61c87d24 --- /dev/null +++ b/tools/lkl/fuzzers/hid/hid-fuzzer.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LKL_HID_FUZZER_H__ +#define __LKL_HID_FUZZER_H__ + +void __llvm_profile_initialize_file(void); +int __llvm_profile_write_file(void); + +#endif // __LKL_HID_FUZZER_H__ diff --git a/tools/lkl/fuzzers/hid/seeds/1 b/tools/lkl/fuzzers/hid/seeds/1 new file mode 100644 index 0000000000000000000000000000000000000000..d9897995467b6ab2d45d9a5b044b9e3b162cd0f0 GIT binary patch literal 127 zcmdOvVrArHTgb@DF8M(7xhR7wV=3cQFLc0gfPta@|Ns9>x+Kx0R8z}2rZP4%9Dp06 f`2c1N2T%tLG%`(PEahlqlw{CM1=^BY3eg1ss)s7P literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/2 b/tools/lkl/fuzzers/hid/seeds/2 new file mode 100644 index 0000000000000000000000000000000000000000..572b7839b6833ceaefa893f1ded370532078cbeb GIT binary patch literal 88 zcmdOvVrArHTgb@DF8M(7xhR7wV=3cQjz*@bjHMinjFJqRsX(#RQjV#NjSL4^8M)aO LGN3EX!KM@dLEsa% literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/3 b/tools/lkl/fuzzers/hid/seeds/3 new file mode 100644 index 0000000000000000000000000000000000000000..6deff109f3f11189d806765fcda65f8fb64c3bd8 GIT binary patch literal 50 xcmdNEYC6Em$jP>l@jxyMh+=)9Darm^lz~yTlyNGDwp0^LoL%yP=JQ6TsQ^9-4N?FA literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/4 b/tools/lkl/fuzzers/hid/seeds/4 new file mode 100644 index 0000000000000000000000000000000000000000..f0e7c8d763d6fc31f02b8eb3cbc4ec0aa312ef55 GIT binary patch literal 137 zcmdPqVrArHTgb@DF8M(7xhR7wqqbDjfm{~2ST2ipDMurtB!gxuP&Sp7eJW!k!vR*H zGN48V)qd7e#;F{QOjG~;2ZP_LjI0o|8kriIBT&FpR#p#5Mopj{U=^CFr5qqbIN2Dd HvM~VwU4|on literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/5 b/tools/lkl/fuzzers/hid/seeds/5 new file mode 100644 index 0000000000000000000000000000000000000000..1ba1fcb6034d207a48944afd9aee2d476f23665e GIT binary patch literal 38 mcmZ?d=qhCt<(SIY$Z&v_k&|s9BkN(w2bv6Ua6D1#KNkR@90)o9 literal 0 HcmV?d00001 diff --git a/tools/lkl/fuzzers/hid/seeds/6 b/tools/lkl/fuzzers/hid/seeds/6 new file mode 100644 index 0000000000000000000000000000000000000000..da78f8a83821b547924b8038227d39f9d564b6f3 GIT binary patch literal 67 zcmcC!U|`5)(LTz`$jP>laVkeCqbP$aBP+Y)1I_1+OpVM_Sy?$G88ulaGEHSHWuC}5 Wm93ORltJx315gQr)_;aZh64c0TMoeh literal 0 HcmV?d00001 From cc563a4552b8f2b4431b157adec9a1d59bb95d82 Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Fri, 10 Feb 2023 22:51:01 +0000 Subject: [PATCH 6/6] github CI: add target for LKL fuzzers. Add a new target to build LKL-based fuzzers using clang/lld toolchain. Signed-off-by: Eugene Rodionov --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b38f8473c5f4f..7aa827f7f0d2d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,11 @@ jobs: runs_on: ubuntu-22.04 shell: bash build_options: "LLVM=1 CROSS_COMPILE=x86_64-linux-gnu" + - displayTargetName: lkl-fuzzers + os: unix + runs_on: ubuntu-22.04 + shell: bash + build_options: "LKL_FUZZING=1 fuzzers" timeout-minutes: 100 env: CCACHE_DIR: ${{ github.workspace }}/.ccache