From b12183499fadb509458561e6f7c38efdc0ef0ca2 Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Sat, 18 Jan 2025 23:42:23 -0800 Subject: [PATCH 1/4] Fix enum install_headers.py script. Current implementation of install_headers.py doesn't handle enums which contain complex expressions such as macro invocations. For example, in include/uapi/linux/android/binder.h the following enums are incorrectly parsed due to commas inside macro _IOW('c', 0, ...) in: ``` enum binder_driver_command_protocol { BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), BC_REPLY = _IOW('c', 1, struct binder_transaction_data), ... } ``` This CL implements a workaround which detect presence of macros with brackets and ignores commas in it. As a limitation the proposed solution isn't generic engough to handle nested macro invocations (i.e. MACRO1(MACRO2(...))) with unlimited depth of brackets. However it offers an improvement over existing approach. Signed-off-by: Zi Fan Tan Signed-off-by: Eugene Rodionov --- arch/lkl/scripts/headers_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py index 258a4f5006dce9..f7dacdc73ed598 100755 --- a/arch/lkl/scripts/headers_install.py +++ b/arch/lkl/scripts/headers_install.py @@ -181,7 +181,7 @@ def replace(h): p = re.compile(r"static\s+__always_inline(\s+\w+)+\s+(\w+)\([^)]*\)\s") find_symbols(p, defines) p = re.compile(r"enum\s+(\w*)\s*{([^}]*)}", re.M|re.S) -q = re.compile(r"(\w+)\s*(,|=[^,]*|$)", re.M|re.S) +q = re.compile(r"(\w+)\s*(,|=\s*\w+\s*\([^()]*\)|=[^,]*|$)", re.M|re.S) find_enums(p, q, defines) # needed for i386 From 67836d710e9b9e5e0ca2350100ddc401e2353a44 Mon Sep 17 00:00:00 2001 From: Zi Fan Tan Date: Sat, 18 Jan 2025 23:43:22 -0800 Subject: [PATCH 2/4] Implement Android Binder libprotobuf-mutator-based fuzzer. The instructions on how to build the fuzzer are provided in tools/lkl/fuzzers/binder/README.md Signed-off-by: Zi Fan Tan Signed-off-by: Eugene Rodionov --- arch/lkl/configs/fuzzing_defconfig | 2 + arch/lkl/scripts/headers_install.py | 1 + tools/lkl/Makefile | 53 +- tools/lkl/Makefile.autoconf | 79 ++ tools/lkl/Targets | 3 + tools/lkl/fuzzers/binder/Build | 8 + tools/lkl/fuzzers/binder/README.md | 326 ++++++++ tools/lkl/fuzzers/binder/binder-fuzzer.cpp | 365 +++++++++ tools/lkl/fuzzers/binder/binder.c | 730 ++++++++++++++++++ tools/lkl/fuzzers/binder/binder.h | 133 ++++ tools/lkl/fuzzers/binder/binder.proto | 111 +++ tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 | 83 ++ .../lkl/scripts/libprotobuf-mutator-build.sh | 21 + 13 files changed, 1908 insertions(+), 7 deletions(-) create mode 100644 tools/lkl/fuzzers/binder/Build create mode 100644 tools/lkl/fuzzers/binder/README.md create mode 100644 tools/lkl/fuzzers/binder/binder-fuzzer.cpp create mode 100644 tools/lkl/fuzzers/binder/binder.c create mode 100644 tools/lkl/fuzzers/binder/binder.h create mode 100644 tools/lkl/fuzzers/binder/binder.proto create mode 100644 tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 create mode 100755 tools/lkl/scripts/libprotobuf-mutator-build.sh diff --git a/arch/lkl/configs/fuzzing_defconfig b/arch/lkl/configs/fuzzing_defconfig index 35482c9aefb7f4..b86b411bfb8b7f 100644 --- a/arch/lkl/configs/fuzzing_defconfig +++ b/arch/lkl/configs/fuzzing_defconfig @@ -191,3 +191,5 @@ CONFIG_KASAN_STACK_ENABLE=y CONFIG_KASAN_GENERIC=y CONFIG_KASAN_OUTLINE=y CONFIG_FRAME_WARN=0 + +CONFIG_DEVTMPFS=y diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py index f7dacdc73ed598..6498f5cc3c91b5 100755 --- a/arch/lkl/scripts/headers_install.py +++ b/arch/lkl/scripts/headers_install.py @@ -132,6 +132,7 @@ 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/android/binder.h") find_headers("include/uapi/linux/uhid.h") find_headers("include/uapi/linux/mman.h") find_headers("include/uapi/linux/input-event-codes.h") diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 355441f6d5b60e..6650b4b8dab788 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -114,13 +114,58 @@ $(OUTPUT)%-in.o: $(OUTPUT)lib/lkl.o FORCE $(OUTPUT)cpfromfs$(EXESUF): cptofs$(EXESUF) $(Q)if ! [ -e $@ ]; then ln -s $< $@; fi +# START: fuzzing-related build-rules +FUZZ_TARGETS := $(fuzzers-y:%=$(OUTPUT)%$(EXESUF)) +fuzzers: $(FUZZ_TARGETS) + +# Enable libFuzzer fuzzing instrumentation for the LKL fuzzers +$(OUTPUT)fuzzers/%$(EXESUF): LDFLAGS += -fsanitize=fuzzer --coverage + +# Binder fuzzer is special: built with C++ toolchain and linked with libprotobuf-mutator +$(OUTPUT)fuzzers/binder%$(EXESUF): $(OUTPUT)fuzzers/binder%-in.o $(OUTPUT)liblkl.a + $(QUIET_LINK)$(CXX) $(LDFLAGS) $(LDFLAGS_$*-y) -o $@ $^ $(LDLIBS) $(LDLIBS_$*-y) + +# Track all the protoc generated files for 'clean' target +PROTOC_GENERATED_FILES := + +read_proto_deps = \ + $(strip $(shell grep PROTOBUF_MUTATOR_PROTO $(1) | sed 's/^.*= //g')) + +define gen_protobuf_deps = + $(eval FUZZER_SRC_DIR := $(dir $(srctree)/tools/lkl/$(1))) + $(eval FUZZER_PROTO_FILES := $(call read_proto_deps,$(FUZZER_SRC_DIR)Build)) + $(eval FUZZER_PROTO_FILES := $(FUZZER_PROTO_FILES:.proto=.pb.cpp) \ + $(FUZZER_PROTO_FILES:.proto=.pb.h)) + $(eval FUZZER_PROTO_FILES := \ + $(addprefix $(FUZZER_SRC_DIR),$(FUZZER_PROTO_FILES))) + PROTOC_GENERATED_FILES += $(FUZZER_PROTO_FILES) + $(OUTPUT)$(1)-in.o: $(FUZZER_PROTO_FILES) +endef + +# Add generated .pb.cc and pb.h files for libprotobuf-mutator as prerequisites +# for the corresponding fuzz targets to have them made using the rule below. +$(foreach fuzzer,$(fuzzers-y),$(eval $(call gen_protobuf_deps,$(fuzzer)))) + +# TODO: Cannot depend on the corresponding $(srctree)/tools/lkl/%.proto file +# due to the pattern rule +# '$(OUTPUT)%$(EXESUF): $(OUTPUT)%-in.o $(OUTPUT)liblkl.a' above. For POSIX +# target $(EXESUF) is an empty string, thus, target $(OUTPUT)%$(EXESUF) would +# match any source file and will try to make it. +# Update once https://github.com/lkl/linux/issues/573 is fixed. +$(srctree)/tools/lkl/%.pb.cpp $(srctree)/tools/lkl/%.pb.h&: FORCE + $(PROTOC_PATH) --cpp_out=$(@D) --proto_path=$(@D) $(@F:pb.cpp=proto) + mv $(@:.cpp=.cc) $@ + +# END: fuzzing-related build-rules + clean: $(call QUIET_CLEAN, vmlinux)$(MAKE) -C ../.. ARCH=lkl $(KOPT) clean $(call QUIET_CLEAN, objects)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd'\ -delete -o -name '\.*.d' -delete $(call QUIET_CLEAN, headers)$(RM) -r $(OUTPUT)/include/lkl/ $(call QUIET_CLEAN, liblkl.a)$(RM) $(OUTPUT)/liblkl.a - $(call QUIET_CLEAN, targets)$(RM) $(TARGETS) bin/stat + $(call QUIET_CLEAN, targets)$(RM) $(TARGETS) $(FUZZ_TARGETS) bin/stat + $(call QUIET_CLEAN, gen_protos)$(RM) $(PROTOC_GENERATED_FILES) mrproper: clean $(call QUIET_CLEAN, vmlinux)$(MAKE) -C ../.. ARCH=lkl $(KOPT) mrproper @@ -153,12 +198,6 @@ 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 clean-conf mrproper FORCE run-tests .PHONY: headers_install libraries_install programs_install install diff --git a/tools/lkl/Makefile.autoconf b/tools/lkl/Makefile.autoconf index e884f364521912..1ebb23f96e274c 100644 --- a/tools/lkl/Makefile.autoconf +++ b/tools/lkl/Makefile.autoconf @@ -169,6 +169,7 @@ define do_autoconf_llvm $(eval LLVM_SUFFIX := $(if $(filter -%,$(LLVM)),$(LLVM))) export CLANG_TARGET_FLAGS_lkl := $(CROSS_COMPILE) export CC := $(LLVM_PREFIX)clang$(LLVM_SUFFIX) + export CXX := $(LLVM_PREFIX)clang++$(LLVM_SUFFIX) export LD := $(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX) export AR := $(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX) $(eval LD := $(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX)) @@ -176,6 +177,81 @@ define do_autoconf_llvm $(eval LD_FMT := $(call llvm_target_to_ld_fmt)) endef +define define_libprotobuf_mutator + $(eval PROTOC_PATH := $(PROTOBUF_MUTATOR_DIR)/build/external.protobuf/bin/protoc) + export PROTOC_PATH := $(PROTOC_PATH) + + # Tell compiler where to find libprotobuf-mutator-related headers + export LIBPROTOBUF_MUTATOR_INCLUDES_DIR := -I$(PROTOBUF_MUTATOR_DIR) \ + -I$(PROTOBUF_MUTATOR_DIR)/build/external.protobuf/include + + # Tell linker where to find libprotobuf-mutator-related static libs + export LIBPROTOBUF_MUTATOR_LIBS_DIR := -L$(PROTOBUF_MUTATOR_DIR)/build/src \ + -L$(PROTOBUF_MUTATOR_DIR)/build/src/libfuzzer \ + -L$(PROTOBUF_MUTATOR_DIR)/build/external.protobuf/lib + + # The same list of absl dependencies as in libprotobuf-mutator cmake config: + # https://github.com/google/libprotobuf-mutator/blob/master/cmake/external/protobuf.cmake + $(eval LIBPROTOBUF_LIBS := protobufd \ + absl_bad_any_cast_impl absl_bad_optional_access absl_bad_variant_access \ + absl_base absl_city absl_civil_time absl_cord absl_cord_internal \ + absl_cordz_functions absl_cordz_handle absl_cordz_info \ + absl_cordz_sample_token absl_crc_cord_state absl_crc_cpu_detect \ + absl_crc_internal absl_crc32c absl_debugging_internal \ + absl_demangle_internal absl_die_if_null absl_examine_stack \ + absl_exponential_biased absl_failure_signal_handler \ + absl_flags_commandlineflag absl_flags_commandlineflag_internal \ + absl_flags_config absl_flags_internal absl_flags_marshalling \ + absl_flags_parse absl_flags_private_handle_accessor \ + absl_flags_program_name absl_flags_reflection absl_flags_usage \ + absl_flags_usage_internal absl_graphcycles_internal absl_hash \ + absl_hashtablez_sampler absl_int128 absl_kernel_timeout_internal \ + absl_leak_check absl_log_entry absl_log_flags absl_log_globals \ + absl_log_initialize absl_log_internal_check_op \ + absl_log_internal_conditions absl_log_internal_format \ + absl_log_internal_globals absl_log_internal_log_sink_set \ + absl_log_internal_message absl_log_internal_nullguard \ + absl_log_internal_proto absl_log_severity \ + absl_log_sink absl_low_level_hash absl_malloc_internal \ + absl_periodic_sampler absl_random_distributions \ + absl_random_internal_distribution_test_util absl_random_internal_platform \ + absl_random_internal_pool_urbg absl_random_internal_randen \ + absl_random_internal_randen_hwaes absl_random_internal_randen_hwaes_impl \ + absl_random_internal_randen_slow absl_random_internal_seed_material \ + absl_random_seed_gen_exception absl_random_seed_sequences \ + absl_raw_hash_set absl_raw_logging_internal absl_scoped_set_env \ + absl_spinlock_wait absl_stacktrace absl_status \ + absl_statusor absl_str_format_internal absl_strerror \ + absl_string_view absl_strings absl_strings_internal \ + absl_symbolize absl_synchronization absl_throw_delegate \ + absl_time absl_time_zone utf8_validity) + + export LIBPROTOBUF_MUTATOR_LIBS := \ + -lprotobuf-mutator-libfuzzer -lprotobuf-mutator \ + $(addprefix -l,$(LIBPROTOBUF_LIBS)) + + # Libprotobuf-mutator build safety checks + $(if $(wildcard $(PROTOC_PATH)),,\ + $(error Cannot find protoc binary at $(PROTOC_PATH). \ + Refer to documentation at tools/lkl/fuzzers/binder/README.md)) + + $(if $(wildcard $(PROTOBUF_MUTATOR_DIR)/build/src/libprotobuf-mutator.a),,\ + $(error Cannot find libprotobuf-mutator.a in \ + $(abspath $(PROTOBUF_MUTATOR_DIR)/build/src/). \ + Refer to documentation at tools/lkl/fuzzers/binder/README.md)) + + $(if $(wildcard $(PROTOBUF_MUTATOR_DIR)/build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a),,\ + $(error Cannot find libprotobuf-mutator-libfuzzer.a in \ + $(abspath $(PROTOBUF_MUTATOR_DIR)/build/src/libfuzzer/). \ + Refer to documentation at tools/lkl/fuzzers/binder/README.md)) + + $(foreach protobuf_lib,$(LIBPROTOBUF_LIBS),\ + $(if $(wildcard $(PROTOBUF_MUTATOR_DIR)/build/external.protobuf/lib/lib$(protobuf_lib).a),,\ + $(error Cannot find lib$(protobuf_lib).a in \ + $(PROTOBUF_MUTATOR_DIR)/build/external.protobuf/lib/. \ + Refer to documentation at tools/lkl/fuzzers/binder/README.md))) +endef + define do_autoconf_fuzzing export KCONFIG := fuzzing_defconfig export LLVM := 1 @@ -185,6 +261,9 @@ define do_autoconf_fuzzing $(eval kasan := yes) $(call set_kernel_config,LKL_FUZZING,y) $(if $(LKL_LINE_COV),$(call set_kernel_config,LKL_LINE_COV,y)) + $(if $(MMU),$(call set_kernel_config,ANDROID_BINDER_IPC,y)) + $(if $(PROTOBUF_MUTATOR_DIR),$(call define_libprotobuf_mutator)) + LDFLAGS += -fuse-ld=lld endef define mmu_test_enable diff --git a/tools/lkl/Targets b/tools/lkl/Targets index eaf9d892d57a84..3d30bd8be3c840 100644 --- a/tools/lkl/Targets +++ b/tools/lkl/Targets @@ -42,4 +42,7 @@ endif # LKL fuzzers fuzzers-y += fuzzers/hid/hid-fuzzer +fuzzers-$(LKL_HOST_CONFIG_MMU) += fuzzers/binder/binder-fuzzer +LDFLAGS_/binder-fuzzer-$(LKL_HOST_CONFIG_MMU) += $(LIBPROTOBUF_MUTATOR_LIBS_DIR) +LDLIBS_/binder-fuzzer-$(LKL_HOST_CONFIG_MMU) += $(LIBPROTOBUF_MUTATOR_LIBS) diff --git a/tools/lkl/fuzzers/binder/Build b/tools/lkl/fuzzers/binder/Build new file mode 100644 index 00000000000000..89b38275958c37 --- /dev/null +++ b/tools/lkl/fuzzers/binder/Build @@ -0,0 +1,8 @@ +CXXFLAGS += $(CFLAGS) $(LIBPROTOBUF_MUTATOR_INCLUDES_DIR) + +CXXFLAGS_binder.pb.o += -DNDEBUG + +PROTOBUF_MUTATOR_PROTO := binder.proto + +binder-fuzzer-y += binder-fuzzer.o binder.o binder.pb.o + diff --git a/tools/lkl/fuzzers/binder/README.md b/tools/lkl/fuzzers/binder/README.md new file mode 100644 index 00000000000000..cabf2c4a7ad471 --- /dev/null +++ b/tools/lkl/fuzzers/binder/README.md @@ -0,0 +1,326 @@ +# Android Binder fuzzer + +This folder contains implementation of libprotobuf-mutator-based Android Binder +fuzzer. + +# Build instructions + +Android Binder fuzzer is based on `libprotobuf-mutator` +[project](https://github.com/google/libprotobuf-mutator). Thus, +to build the fuzzer you would need to checkout and build `libprotobuf-mutator`. + +`libprotobuf-mutator` in turn depends on `libprotobuf` library. You can build +`libprotobuf-mutator` either using system-installed `libprotobuf` or +automatically downloaded the latest version of `libprotobuf` during build +process. On some systems the system version of library is too old and might +cause build issues for the fuzzers. Thus, it is advised to instruct `cmake` +which is used for building `libprotobuf-mutator` to automatically download and +build a working version of protobuf library. The instructions provided below +are based on this approach. + +Assuming that all the necessary dependencies per `libprobotub-mutator` +[documentation](https://github.com/google/libprotobuf-mutator/blob/master/README.md#quick-start-on-debianubuntu): + +1. Run `tools/lkl/scripts/libprotobuf-mutator-build.sh` passing path to the +folder which `libprotobuf-mutator` should be downloaded to via +`PROTOBUF_MUTATOR_DIR` variable, for example: + +``` +PROTOBUF_MUTATOR_DIR=/tmp/libprotobuf-mutator \ + tools/lkl/scripts/libprotobuf-mutator-build.sh +``` + +This script will checkout `libprotobuf-mutator` into `/tmp/libprotobuf-mutator` +folder with its dependencies including the working version of `libprotobuf` +and build the project. `libprotobuf-mutator-build.sh` script uses +`libprotobuf-mutator` +[version 1.4](https://github.com/google/libprotobuf-mutator/releases/tag/v1.4) +(which has been confirmed working with the current implementation of LKL build +system). + +Upon successful invocation of the commands above we should get the following +layout: + +* `/tmp/libprotobuf-mutator/build/external.protobuf` -- downloaded and built +working version of protobuf library + +* `/tmp/libprotobuf-mutator/build/external.protobuf/bin/protoc` -- built +protobuf compiler which generates .pb.cpp and .pb.h files from the +corresponding .proto file + +* `/tmp/libprotobuf-mutator/build/external.protobuf/include` -- header files +necessary for building the fuzzer harness + +* `/tmp/libprotobuf-mutator/build/external.protobuf/lib` -- built static +libraries needed for building the fuzzer harness + +* `/tmp/libprotobuf-mutator/build/src` -- contains compiled +`libprotobuf-mutator.a` static library + +* `/tmp/libprotobuf-mutator/build/src/libfuzzer` -- contains compiled +`libprotobuf-mutator-libfuzzer.a` static library for integration with +`libfuzzer` engine + +At this point we have all the necessary `libprotobuf-mutator`-related +dependencies and can move to building the actual fuzzer. + +2. Build `binder-fuzzer` + +``` +make -C tools/lkl LKL_FUZZING=1 MMU=1 \ + PROTOBUF_MUTATOR_DIR=/tmp/libprotobuf-mutator \ + clean-conf fuzzers -jX +``` + +where `X` is the number of parallel jobs to speed up building the fuzzer, +`LKL_FUZZING` enables fuzzing instrumentation (such as code coverage, KASan), +`MMU` enables `CONFIG_MMU` config which binder driver depends on, +`PROTOBUF_MUTATOE_DIR` provides path to the `libprotobuf-mutator` directory +with the header files and static lib dependencies. + +Upon successful completion of the `make` command above the fuzzer binary +can be located at `tools/lkl/fuzzers/binder/binder-fuzzer`. + +# Reproducing CVE-2023-20938 + +Here are the instructions on how to reproduce the UAF vulnerability with the +fuzzer: + +1. Roll back +[patch](https://github.com/torvalds/linux/commit/bdc1c5fac982845a58d28690cdb56db8c88a530d) +for CVE-2023-21255 using the following command: + +``` +$ cat > /tmp/CVE-2023-21255.rollback << EOF +--- a/drivers/android/binder.c ++++ b/drivers/android/binder.c +@@ -1941,22 +1941,24 @@ static void binder_deferred_fd_close(int fd) + static void binder_transaction_buffer_release(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_buffer *buffer, +- binder_size_t off_end_offset, ++ binder_size_t failed_at, + bool is_failure) + { + int debug_id = buffer->debug_id; +- binder_size_t off_start_offset, buffer_offset; ++ binder_size_t off_start_offset, buffer_offset, off_end_offset; + + binder_debug(BINDER_DEBUG_TRANSACTION, + "%d buffer release %d, size %zd-%zd, failed at %llx\n", + proc->pid, buffer->debug_id, + buffer->data_size, buffer->offsets_size, +- (unsigned long long)off_end_offset); ++ (unsigned long long)failed_at); + + if (buffer->target_node) + binder_dec_node(buffer->target_node, 1, 0); + + off_start_offset = ALIGN(buffer->data_size, sizeof(void *)); ++ off_end_offset = is_failure && failed_at ? failed_at : ++ off_start_offset + buffer->offsets_size; + + for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; + buffer_offset += sizeof(binder_size_t)) { +@@ -2117,21 +2119,6 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, + } + } + +-/* Clean up all the objects in the buffer */ +-static inline void binder_release_entire_buffer(struct binder_proc *proc, +- struct binder_thread *thread, +- struct binder_buffer *buffer, +- bool is_failure) +-{ +- binder_size_t off_end_offset; +- +- off_end_offset = ALIGN(buffer->data_size, sizeof(void *)); +- off_end_offset += buffer->offsets_size; +- +- binder_transaction_buffer_release(proc, thread, buffer, +- off_end_offset, is_failure); +-} +- + static int binder_translate_binder(struct flat_binder_object *fp, + struct binder_transaction *t, + struct binder_thread *thread) +@@ -2827,7 +2814,7 @@ static int binder_proc_transaction(struct binder_transaction *t, + t_outdated->buffer = NULL; + buffer->transaction = NULL; + trace_binder_transaction_update_buffer_release(buffer); +- binder_release_entire_buffer(proc, NULL, buffer, false); ++ binder_transaction_buffer_release(proc, NULL, buffer, 0, 0); + binder_alloc_free_buf(&proc->alloc, buffer); + kfree(t_outdated); + binder_stats_deleted(BINDER_STAT_TRANSACTION); +@@ -3800,7 +3787,7 @@ binder_free_buf(struct binder_proc *proc, + binder_node_inner_unlock(buf_node); + } + trace_binder_transaction_buffer_release(buffer); +- binder_release_entire_buffer(proc, thread, buffer, is_failure); ++ binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure); + binder_alloc_free_buf(&proc->alloc, buffer); + } + +EOF + +$ git apply /tmp/CVE-2023-21255.rollback +``` + +2. Roll back +[patch](https://github.com/torvalds/linux/commit/6d98eb95b450a75adb4516a1d33652dc78d2b20c) +for CVE-2023-20938 using the following command: + +``` +$ cat > /tmp/CVE-2023-20938.rollback << EOF +--- a/drivers/android/binder.c ++++ b/drivers/android/binder.c +@@ -3260,6 +3260,20 @@ static void binder_transaction(struct binder_proc *proc, + t->buffer->clear_on_free = !!(t->flags & TF_CLEAR_BUF); + trace_binder_transaction_alloc_buf(t->buffer); + ++ if (binder_alloc_copy_user_to_buffer( ++ &target_proc->alloc, ++ t->buffer, 0, ++ (const void __user *) ++ (uintptr_t)tr->data.ptr.buffer, ++ tr->data_size)) { ++ binder_user_error("%d:%d got transaction with invalid data ptr\n", ++ proc->pid, thread->pid); ++ return_error = BR_FAILED_REPLY; ++ return_error_param = -EFAULT; ++ return_error_line = __LINE__; ++ goto err_copy_data_failed; ++ } ++ + if (binder_alloc_copy_user_to_buffer( + &target_proc->alloc, + t->buffer, +@@ -3318,27 +3332,9 @@ static void binder_transaction(struct binder_proc *proc, + return_error_line = __LINE__; + goto err_bad_offset; + } ++ object_size = binder_get_object(target_proc, NULL, t->buffer, ++ object_offset, &object); + +- /* +- * Copy the source user buffer up to the next object +- * that will be processed. +- */ +- copy_size = object_offset - user_offset; +- if (copy_size && (user_offset > object_offset || +- binder_alloc_copy_user_to_buffer( +- &target_proc->alloc, +- t->buffer, user_offset, +- user_buffer + user_offset, +- copy_size))) { +- binder_user_error("%d:%d got transaction with invalid data ptr\n", +- proc->pid, thread->pid); +- return_error = BR_FAILED_REPLY; +- return_error_param = -EFAULT; +- return_error_line = __LINE__; +- goto err_copy_data_failed; +- } +- object_size = binder_get_object(target_proc, user_buffer, +- t->buffer, object_offset, &object); + if (object_size == 0 || object_offset < off_min) { + binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n", + proc->pid, thread->pid, +@@ -3556,19 +3552,6 @@ static void binder_transaction(struct binder_proc *proc, + goto err_bad_object_type; + } + } +- /* Done processing objects, copy the rest of the buffer */ +- if (binder_alloc_copy_user_to_buffer( +- &target_proc->alloc, +- t->buffer, user_offset, +- user_buffer + user_offset, +- tr->data_size - user_offset)) { +- binder_user_error("%d:%d got transaction with invalid data ptr\n", +- proc->pid, thread->pid); +- return_error = BR_FAILED_REPLY; +- return_error_param = -EFAULT; +- return_error_line = __LINE__; +- goto err_copy_data_failed; +- } + + ret = binder_do_deferred_txn_copies(&target_proc->alloc, t->buffer, + &sgc_head, &pf_head); + +EOF + +$ git apply /tmp/CVE-2023-20938.rollback +``` + +3. Build the binder fuzzer (replace `X` with the number of parallel make jobs): + +``` +make -C tools/lkl LKL_FUZZING=1 MMU=1 \ + PROTOBUF_MUTATOR_DIR=/tmp/libprotobuf-mutator \ + clean-conf fuzzers -jX +``` + +4. Run the reproducer from `tools/lkl/fuzzers/binder/seeds/CVE-2023-20938`: + +``` +$ tools/lkl/fuzzers/binder/binder-fuzzer \ + tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 + +... +Running: tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 +... +[ 0.624939] ================================================================== +[ 0.624977] BUG: KASAN: slab-use-after-free in __unnamed_1+0x2a6c1a/0x1f6c380 +[ 0.625064] Read of size 16 at addr 0000000050bed658 by task host4/29 +[ 0.625094] +[ 0.625113] CPU: 0 PID: 29 Comm: host4 Not tainted 6.6.0+ #1 +[ 0.625149] Call Trace: +[ 0.625166] #00 [<0x000055fa9266541b>] psmouse_protocols+0x1b/0x60 +[ 0.625227] #01 [<0x000055fa92879b4e>] trace_event_fields_global_dirty_state+0x2e/0x180 +[ 0.625273] #02 [<0x000055fa9287a392>] trace_event_fields_balance_dirty_pages+0xf2/0x320 +[ 0.625316] #03 [<0x000055fa9287c069>] __unnamed_1+0x5e9/0x2ec0 +[ 0.625356] #04 [<0x000055fa92c468fa>] __unnamed_1+0x2a6c1a/0x1f6c380 +[ 0.625394] #05 [<0x000055fa92c3ed04>] __unnamed_1+0x29f024/0x1f6c380 +[ 0.625432] #06 [<0x000055fa928beb3e>] print_fmt_io_uring_req_failed+0x25e/0x260 +[ 0.625474] #07 [<0x000055fa92682eed>] c2u_E6+0x16d/0x280 +[ 0.625518] #08 [<0x000055fa9261770a>] 0x55fa9261770a +[ 0.625548] #09 [<0x000055fa926162ad>] 0x55fa926162ad +[ 0.625578] #10 [<0x0000000000000000>] 0x0 +[ 0.625606] #11 [<0x000055fa9261ab40>] 0x55fa9261ab40 +[ 0.625636] #12 [<0xbde8087b8d480974>] 0xbde8087b8d480974 +[ 0.625667] +[ 0.625685] The buggy address belongs to the object at 0000000050bed600 +[ 0.625685] which belongs to the cache kmalloc-128 of size 128 +[ 0.625714] The buggy address is located 88 bytes inside of +[ 0.625714] freed 128-byte region [0000000050bed600, 0000000050bed680) +[ 0.625747] +[ 0.625764] The buggy address belongs to the physical page: +[ 0.625784] page:(____ptrval____) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x50bed +[ 0.625818] flags: 0x800(slab|zone=0) +[ 0.625846] page_type: 0xffffffff() +[ 0.625877] raw: 0000000000000800 0000000050001700 0000000000000100 0000000000000122 +[ 0.625908] raw: 0000000000000000 0000000000100010 00000001ffffffff +[ 0.625931] page dumped because: kasan: bad access detected +[ 0.625952] +[ 0.625968] Memory state around the buggy address: +[ 0.625990] 0000000050bed500: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 0.626017] 0000000050bed580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 0.626051] >0000000050bed600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 0.626074] ^ +[ 0.626099] 0000000050bed680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 0.626126] 0000000050bed700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc fc +[ 0.626149] ================================================================== +[ 0.626170] Disabling lock debugging due to kernel taint +[ 0.626243] binder: 29:29 ioctl c0306201 7f3ef9dff400 returned -11 +[ 0.626588] binder: undelivered TRANSACTION_COMPLETE +[ 0.626632] binder: undelivered TRANSACTION_ERROR: 29201 +[ 0.626668] binder: undelivered transaction 2, process died. +Executed tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 in 17 ms +``` + +# Resources + +"How to Fuzz Your Way to Android Universal Root" +[presentation](https://www.youtube.com/watch?v=U-xSM159YLI) +([slides](https://androidoffsec.withgoogle.com/posts/attacking-android-binder-analysis-and-exploitation-of-cve-2023-20938/offensivecon_24_binder.pdf)) provides information on fuzzer design and vulnerabilities caught using the fuzzer. + diff --git a/tools/lkl/fuzzers/binder/binder-fuzzer.cpp b/tools/lkl/fuzzers/binder/binder-fuzzer.cpp new file mode 100644 index 00000000000000..9e5fda31b9969b --- /dev/null +++ b/tools/lkl/fuzzers/binder/binder-fuzzer.cpp @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include "binder.h" +} + +#include "binder.pb.h" + +#include "src/libfuzzer/libfuzzer_macro.h" + +#define NUM_CLIENT 3 +#define MAX_HANDLE 10 +#define MAX_READ_BUF_SIZE 1024 +#define MAX_EXTRA_BUF_SIZE 256 +#define MAX_BINDER_BUF_OBJ_SIZE 32 + +static void bwr_buf_clear_txnouts(std::vector *txnouts) +{ + txnout_t *txnout; + while (!txnouts->empty()) { + txnout = txnouts->back(); + txnouts->pop_back(); + if (txnout->fd != -1) { + lkl_close_fd(txnout->fd); + } + free(txnout); + } +} + +static void binder_bwr_transaction_binder_object(txnout_t *txnout, + const BinderObject &fuzz_bo) +{ + switch (fuzz_bo.type_case()) { + case BinderObject::kBinder: + binder_transaction_put_object_binder( + txnout, fuzz_bo.binder().ptr() % MAX_HANDLE); + break; + case BinderObject::kWeakBinder: + binder_transaction_put_object_weak_binder( + txnout, fuzz_bo.weak_binder().ptr() % MAX_HANDLE); + break; + case BinderObject::kHandle: + binder_transaction_put_object_handle( + txnout, fuzz_bo.handle() % MAX_HANDLE); + break; + case BinderObject::kWeakHandle: + binder_transaction_put_object_weak_handle( + txnout, fuzz_bo.weak_handle() % MAX_HANDLE); + break; + case BinderObject::kFd: { + binder_transaction_put_object_fd(txnout); + } break; + case BinderObject::kFda: { + binder_transaction_put_object_fda( + txnout, fuzz_bo.fda().num_fds(), fuzz_bo.fda().parent(), + fuzz_bo.fda().parent_offset()); + } break; + case BinderObject::kPtr: { + binder_transaction_put_object_ptr( + txnout, fuzz_bo.ptr().has_parent_flag(), + (void *)fuzz_bo.ptr().buffer().data(), + fuzz_bo.ptr().buffer_size() % MAX_BINDER_BUF_OBJ_SIZE, + fuzz_bo.ptr().parent(), fuzz_bo.ptr().parent_offset()); + } break; + case BinderObject::TYPE_NOT_SET: + break; + } +} + +static void *binder_bwr_transaction(bwr_buf_t *bb, + std::vector *txnouts, + bool reply, const Transaction &fuzz_tr) +{ + txnout_t *txnout; + + txnout = (txnout_t *)malloc(sizeof(*txnout)); + if (txnout == NULL) + return NULL; + + txnout_init(txnout); + txnouts->push_back(txnout); + + // Generate random binder objects + for (const BinderObject &fuzz_bo : fuzz_tr.binder_objects()) { + binder_bwr_transaction_binder_object(txnout, fuzz_bo); + } + + unsigned int target_handle = fuzz_tr.target_handle() % MAX_HANDLE; + unsigned int flags = 0; + for (const auto &flag : fuzz_tr.flags()) { + flags |= flag; + } + size_t extra_data_size = fuzz_tr.extra_data_size() % RANDOM_SIZE_RANGE; + size_t extra_offsets_size = + fuzz_tr.extra_offsets_size() % RANDOM_SIZE_RANGE; + + return bwr_buf_alloc_transaction(bb, reply, txnout, target_handle, + flags, extra_data_size, + extra_offsets_size); +} + +static void *binder_bwr_transaction_sg(bwr_buf_t *bb, + std::vector *txnouts, + bool reply, + const TransactionSg &fuzz_tr_sg) +{ + txnout_t *txnout; + + txnout = (txnout_t *)malloc(sizeof(*txnout)); + if (txnout == NULL) + return NULL; + + txnout_init(txnout); + txnouts->push_back(txnout); + + // Generate random binder objects + for (const BinderObject &fuzz_bo : + fuzz_tr_sg.transaction().binder_objects()) { + binder_bwr_transaction_binder_object(txnout, fuzz_bo); + } + + unsigned int target_handle = + fuzz_tr_sg.transaction().target_handle() % MAX_HANDLE; + unsigned int flags = 0; + for (const auto &flag : fuzz_tr_sg.transaction().flags()) { + flags |= flag; + } + size_t extra_data_size = + fuzz_tr_sg.transaction().extra_data_size() % RANDOM_SIZE_RANGE; + size_t extra_offsets_size = + fuzz_tr_sg.transaction().extra_offsets_size() % + RANDOM_SIZE_RANGE; + + size_t extra_buffers_size = + fuzz_tr_sg.extra_buffers_size() % MAX_EXTRA_BUF_SIZE; + + return bwr_buf_alloc_transaction_sg(bb, reply, txnout, target_handle, + flags, extra_data_size, + extra_offsets_size, + extra_buffers_size); +} + +static void binder_bwr(binder_ctx *ctx, bwr_buf_t *bb, + std::vector *txnouts, + const BinderWrite &fuzz_binder_write) +{ + for (const BinderCommand &command : + fuzz_binder_write.binder_commands()) { + switch (command.bc_case()) { + case BinderCommand::kAcquire: + binder_bwr_acquire(bb, command.acquire() % MAX_HANDLE); + break; + case BinderCommand::kIncrefs: + binder_bwr_increfs(bb, command.increfs() % MAX_HANDLE); + break; + case BinderCommand::kRelease: + binder_bwr_release(bb, command.release() % MAX_HANDLE); + break; + case BinderCommand::kDecrefs: + binder_bwr_decrefs(bb, command.decrefs() % MAX_HANDLE); + break; + case BinderCommand::kIncrefsDone: + binder_bwr_increfs_done( + bb, command.increfs_done().ptr() % MAX_HANDLE); + break; + case BinderCommand::kAcquireDone: + binder_bwr_acquire_done( + bb, command.acquire_done().ptr() % MAX_HANDLE); + break; + case BinderCommand::kTransaction: { + binder_bwr_transaction(bb, txnouts, false, + command.transaction()); + } break; + case BinderCommand::kReply: { + binder_bwr_transaction(bb, txnouts, true, + command.transaction()); + } break; + case BinderCommand::kTransactionSg: { + binder_bwr_transaction_sg(bb, txnouts, false, + command.transaction_sg()); + } break; + case BinderCommand::kReplySg: { + binder_bwr_transaction_sg(bb, txnouts, true, + command.transaction_sg()); + } break; + case BinderCommand::kFreeBuffer: { + if (size_binder_buffers_queue(&ctx->buffers)) { + binder_bwr_free_buffer( + bb, front_binder_buffers_queue( + &ctx->buffers)); + pop_binder_buffers_queue(&ctx->buffers); + } else { + binder_bwr_free_buffer(bb, 0); + } + } break; + case BinderCommand::kRequestDeathNotification: + binder_bwr_request_death_notification( + bb, command.request_death_notification().ptr() % + MAX_HANDLE); + break; + case BinderCommand::kClearDeathNotification: + binder_bwr_clear_death_notification( + bb, command.clear_death_notification().ptr() % + MAX_HANDLE); + break; + case BinderCommand::kDeadBinderDone: + binder_bwr_dead_binder_done(bb, + command.dead_binder_done()); + break; + case BinderCommand::kRegisterLooper: + binder_bwr_register_looper(bb); + break; + case BinderCommand::kEnterLooper: + binder_bwr_enter_looper(bb); + break; + case BinderCommand::kExitLooper: + binder_bwr_exit_looper(bb); + break; + case BinderCommand::BC_NOT_SET: + break; + } + } +} + +static void perform_ioctl(binder_ctx *ctx, const Ioctl *ioctl) +{ + switch (ioctl->ioctl_case()) { + case Ioctl::kBinderWrite: { + bwr_buf_t bb; + uint8_t bwr_buffer[256]; + bwr_buf_init(&bb, bwr_buffer, sizeof(bwr_buffer)); + std::vector txnouts; + binder_bwr(ctx, &bb, &txnouts, ioctl->binder_write()); + binder_send(ctx, &bb); + bwr_buf_clear_txnouts(&txnouts); + } break; + case Ioctl::kBinderRead: { + binder_recv(ctx, MAX_READ_BUF_SIZE); + } break; + case Ioctl::kBinderThreadExit: + binder_ioctl_thread_exit(ctx); + break; + case Ioctl::kBinderVersion: + binder_ioctl_check_version(ctx); + break; + case Ioctl::kBinderGetNodeDebugInfo: + binder_ioctl_get_node_debug_info( + ctx, + ioctl->binder_get_node_debug_info().ptr() % MAX_HANDLE); + break; + case Ioctl::kBinderGetNodeInfoForRef: + binder_ioctl_get_node_info_for_ref( + ctx, ioctl->binder_get_node_info_for_ref().handle() % + MAX_HANDLE); + break; + case Ioctl::kBinderEnableOnewaySpamDetection: + binder_ioctl_enable_oneway_spam_detection( + ctx, ioctl->binder_enable_oneway_spam_detection()); + break; + case Ioctl::IOCTL_NOT_SET: + break; + } +} + +extern "C" void __llvm_profile_initialize_file(void); +extern "C" int __llvm_profile_write_file(void); + +void flush_coverage() +{ + __llvm_profile_write_file(); +} + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + initialize_lkl(); + + __llvm_profile_initialize_file(); + atexit(flush_coverage); + return 0; +} + +struct binder_thread_work { + sem_t wait_ioctl_sem; + sem_t done_ioctl_sem; + const Ioctl *ioctl; + bool done; + int id; + pthread_t tid; +}; + +void *ioctl_thread(void *arg) +{ + struct binder_thread_work *work = (binder_thread_work *)arg; + + if (work->id != 0) + run_in_new_process(); + + struct binder_ctx *ctx = binder_open(); + if (!ctx) + return NULL; + + if (work->id == 0) + assert(binder_ioctl_set_context_manager(ctx) == 0); + + while (!work->done) { + assert(sem_wait(&work->wait_ioctl_sem) == 0); + if (!work->done) + perform_ioctl(ctx, work->ioctl); + assert(sem_post(&work->done_ioctl_sem) == 0); + } + + binder_close(ctx); + + return NULL; +} + +DEFINE_PROTO_FUZZER(const Session &session) +{ + static int iter = 0; + struct binder_thread_work binder_work[NUM_CLIENT]; + + for (int i = 0; i < NUM_CLIENT; i++) { + assert(sem_init(&binder_work[i].wait_ioctl_sem, 0, 0) == 0); + assert(sem_init(&binder_work[i].done_ioctl_sem, 0, 0) == 0); + binder_work[i].done = false; + binder_work[i].id = i; + + assert(pthread_create(&binder_work[i].tid, NULL, ioctl_thread, + (void *)&binder_work[i]) == 0); + } + + for (const Ioctl &ioctl : session.ioctls()) { + // Pick the worker thread + assert(ioctl.binder_client() < NUM_CLIENT); + struct binder_thread_work *current_work = + &binder_work[ioctl.binder_client()]; + + // Send the ioctl for execution + current_work->ioctl = &ioctl; + assert(sem_post(¤t_work->wait_ioctl_sem) == 0); + + // Wait once the ioctl is handled + assert(sem_wait(¤t_work->done_ioctl_sem) == 0); + } + + for (int i = 0; i < NUM_CLIENT; i++) { + binder_work[i].done = true; + assert(sem_post(&binder_work[i].wait_ioctl_sem) == 0); + pthread_join(binder_work[i].tid, NULL); + } + + iter++; + if (iter > 1000) { + flush_coverage(); + iter = 0; + } +} diff --git a/tools/lkl/fuzzers/binder/binder.c b/tools/lkl/fuzzers/binder/binder.c new file mode 100644 index 00000000000000..6b0fb5d2ee2f85 --- /dev/null +++ b/tools/lkl/fuzzers/binder/binder.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "binder.h" + +#ifndef LKL_MAP_FAILED +#define LKL_MAP_FAILED ((void *)-1) +#endif + +#define ERR(fmt, ...) printf("ERROR: " fmt "\n", ##__VA_ARGS__) + +#define BINDER_DEVICE "/dev/binder" +#define BINDER_VM_SIZE (1 * 1024 * 1024) + +#define PAD_SIZE_UNSAFE(s) (((s) + 3) & ~3UL) + +// Copied from drivers/android/binder_internal.h +struct binder_object { + union { + struct lkl_binder_object_header hdr; + struct lkl_flat_binder_object fbo; + struct lkl_binder_fd_object fdo; + struct lkl_binder_buffer_object bbo; + struct lkl_binder_fd_array_object fdao; + }; +}; + +void bwr_buf_init(struct bwr_buf_t *bb, uint8_t *ptr, size_t size) +{ + bb->data0 = bb->data = ptr; + bb->data_avail = size; +} + +void *bwr_buf_alloc(struct bwr_buf_t *bb, size_t size) +{ + void *ptr; + + if (size > bb->data_avail) + return NULL; + + ptr = bb->data; + bb->data += size; + bb->data_avail -= size; + + return ptr; +} + +static void bwr_buf_put_uint32(struct bwr_buf_t *bb, uint32_t value) +{ + uint32_t *ptr = (uint32_t *)bwr_buf_alloc(bb, sizeof(value)); + + if (ptr) + *ptr = value; +} + +static void bwr_buf_put_uintptr(struct bwr_buf_t *bb, uintptr_t value) +{ + uintptr_t *ptr = (uintptr_t *)bwr_buf_alloc(bb, sizeof(value)); + + if (ptr) + *ptr = value; +} + +static void bwr_buf_put_bc(struct bwr_buf_t *bb, uint32_t bc) +{ + bwr_buf_put_uint32(bb, bc); +} + +static void bwr_buf_put_binder_req(struct bwr_buf_t *bb, uintptr_t binder_ptr, + uintptr_t cookie, uint32_t bc) +{ + bwr_buf_put_uint32(bb, bc); + bwr_buf_put_uintptr(bb, binder_ptr); + bwr_buf_put_uintptr(bb, cookie); +} + +static void bwr_buf_put_handle_req(struct bwr_buf_t *bb, uint32_t handle, uint32_t bc) +{ + bwr_buf_put_uint32(bb, bc); + bwr_buf_put_uint32(bb, handle); +} + +static void bwr_buf_put_death_notif_req(struct bwr_buf_t *bb, uintptr_t binder_ptr, + uintptr_t cookie, uint32_t bc) +{ + bwr_buf_put_uint32(bb, bc); + bwr_buf_put_uint32(bb, binder_ptr); + bwr_buf_put_uintptr(bb, cookie); +} + +static void *bwr_buf_pop(struct bwr_buf_t *bb, size_t size) +{ + void *ptr; + + if (size > bb->data_avail) + return NULL; + + ptr = bb->data; + bb->data += size; + bb->data_avail -= size; + + return ptr; +} + +static uint32_t bwr_buf_pop_uint32(struct bwr_buf_t *bb) +{ + uint32_t *value; + + value = (uint32_t *)bwr_buf_pop(bb, sizeof(uint32_t)); + if (!value) + return 0; + + return *value; +} + +void txnout_init(struct txnout_t *txnout) +{ + txnout->data = txnout->data0; + txnout->data_avail = sizeof(txnout->data0) - RANDOM_SIZE_RANGE; + txnout->offs = txnout->offs0; + txnout->offs_avail = (sizeof(txnout->offs0) - 8) / sizeof(size_t); + txnout->extra_data = txnout->extra_data0; + txnout->extra_data_avail = + sizeof(txnout->extra_data0) - RANDOM_SIZE_RANGE; + txnout->fd = -1; +} + +static void *txnout_alloc(struct txnout_t *txnout, size_t size) +{ + void *ptr; + + if (size > txnout->data_avail || !txnout->offs_avail) + return NULL; + + ptr = txnout->data; + txnout->data += size; + txnout->data_avail -= size; + txnout->offs_avail--; + *txnout->offs++ = ((uint8_t *)ptr) - ((uint8_t *)txnout->data0); + + return ptr; +} + +static void *txnout_extra_buf_alloc(struct txnout_t *txnout, size_t size) +{ + void *ptr; + + if (size > txnout->extra_data_avail) + return NULL; + + ptr = txnout->extra_data; + txnout->extra_data += size; + txnout->extra_data_avail -= size; + + return ptr; +} + +struct txnin_t { + uint8_t *data0; + uint8_t *data; + size_t data_avail; +}; + +static void txnin_init(struct txnin_t *txnin, void *tr) +{ + struct lkl_binder_transaction_data *tr_data = + (struct lkl_binder_transaction_data *)tr; + + txnin->data0 = txnin->data = (uint8_t *)tr_data->data.ptr.buffer; + txnin->data_avail = tr_data->data_size; +} + +struct binder_ctx *binder_open(void) +{ + struct binder_ctx *ctx; + + ctx = (struct binder_ctx *)malloc(sizeof(struct binder_ctx)); + if (!ctx) + return NULL; + + ctx->fd = lkl_sys_open(BINDER_DEVICE, + LKL_O_RDWR | LKL_O_CLOEXEC | LKL_O_NONBLOCK, 0); + if (ctx->fd == -1) { + ERR("Failed to open binder device"); + goto err_open; + } + + ctx->epoll_fd = -1; + ctx->map_size = BINDER_VM_SIZE; + ctx->map_ptr = lkl_sys_mmap(NULL, BINDER_VM_SIZE, LKL_PROT_READ, + LKL_MAP_PRIVATE, ctx->fd, 0); + if (ctx->map_ptr == LKL_MAP_FAILED) { + ERR("Failed to mmap binder device"); + goto err_mmap; + } + + init_binder_buffers_queue(&ctx->buffers); + + return ctx; +err_mmap: + lkl_sys_close(ctx->fd); +err_open: + free(ctx); + return NULL; +} + +void binder_close(struct binder_ctx *ctx) +{ + if (ctx) { + lkl_sys_munmap((unsigned long)ctx->map_ptr, ctx->map_size); + lkl_sys_close(ctx->fd); + if (ctx->epoll_fd != -1) + lkl_sys_close(ctx->epoll_fd); + free(ctx); + } +} + +int binder_ioctl_set_context_manager(struct binder_ctx *ctx) +{ + int ret = lkl_sys_ioctl(ctx->fd, LKL_BINDER_SET_CONTEXT_MGR, 0); + + if (ret < 0) + ERR("Failed to set context manager"); + + return ret; +} + +int binder_ioctl_write(struct binder_ctx *ctx, void *buffer, size_t size) +{ + struct lkl_binder_write_read bwr = { + .write_size = size, + .write_consumed = 0, + .write_buffer = (lkl_binder_uintptr_t)buffer + }; + + int ret = lkl_sys_ioctl(ctx->fd, LKL_BINDER_WRITE_READ, + (unsigned long)&bwr); + + if (ret < 0) + return ret; + + return bwr.write_consumed; +} + +int binder_ioctl_read(struct binder_ctx *ctx, void *buffer, size_t size, + size_t *read_consumed) +{ + struct lkl_binder_write_read bwr = { + .read_size = size, + .read_consumed = 0, + .read_buffer = (lkl_binder_uintptr_t)buffer + }; + + int ret = lkl_sys_ioctl(ctx->fd, LKL_BINDER_WRITE_READ, + (unsigned long)&bwr); + + if (ret == 0) + *read_consumed = bwr.read_consumed; + + return ret; +} + +int binder_ioctl_thread_exit(struct binder_ctx *ctx) +{ + int ret = lkl_sys_ioctl(ctx->fd, LKL_BINDER_THREAD_EXIT, 0); + + if (ret < 0) + ERR("Failed to perform thread exit"); + + return ret; +} + +int binder_ioctl_check_version(struct binder_ctx *ctx) +{ + int ret; + struct lkl_binder_version version = { 0 }; + + ret = lkl_sys_ioctl(ctx->fd, LKL_BINDER_VERSION, + (unsigned long)&version); + if (ret < 0) { + return ret; + } else if (version.protocol_version != + LKL_BINDER_CURRENT_PROTOCOL_VERSION) { + ERR("Binder version does not match: %u", + version.protocol_version); + return -1; + } + + return 0; +} + +int binder_ioctl_get_node_debug_info(struct binder_ctx *ctx, uintptr_t ptr) +{ + struct lkl_binder_node_debug_info info = { .ptr = ptr }; + + return lkl_sys_ioctl(ctx->fd, LKL_BINDER_GET_NODE_DEBUG_INFO, + (unsigned long)&info); +} + +int binder_ioctl_get_node_info_for_ref(struct binder_ctx *ctx, uint32_t handle) +{ + struct lkl_binder_node_info_for_ref info = { .handle = handle }; + + return lkl_sys_ioctl(ctx->fd, LKL_BINDER_GET_NODE_INFO_FOR_REF, + (unsigned long)&info); +} + +int binder_ioctl_enable_oneway_spam_detection(struct binder_ctx *ctx, uint32_t e) +{ + uint32_t enable = e; + + return lkl_sys_ioctl(ctx->fd, LKL_BINDER_ENABLE_ONEWAY_SPAM_DETECTION, + (unsigned long)&enable); +} + +static void binder_execute_cmds(struct binder_ctx *ctx, struct bwr_buf_t *bb) +{ + struct txnin_t txnin; + uint32_t cmd; + void *cmd_data; + + while (bb->data_avail > 0) { + cmd = bwr_buf_pop_uint32(bb); + cmd_data = bwr_buf_pop(bb, _LKL_IOC_SIZE(cmd)); + switch (cmd) { + case LKL_BR_ACQUIRE: + case LKL_BR_INCREFS: { + uint8_t bwr_buffer[256]; + struct bwr_buf_t bb_out; + struct lkl_binder_ptr_cookie *bpc = + (struct lkl_binder_ptr_cookie *)cmd_data; + bwr_buf_init(&bb_out, bwr_buffer, sizeof(bwr_buffer)); + bwr_buf_put_uint32(&bb_out, + cmd == LKL_BR_ACQUIRE ? + LKL_BC_ACQUIRE_DONE : + LKL_BC_INCREFS_DONE); + bwr_buf_put_uintptr(&bb_out, bpc->ptr); + bwr_buf_put_uintptr(&bb_out, bpc->cookie); + binder_send(ctx, &bb_out); + } break; + case LKL_BR_DEAD_BINDER: { + struct bwr_buf_t bb_out; + uint8_t bwr_buffer[256]; + lkl_binder_uintptr_t cookie = + *(lkl_binder_uintptr_t *)cmd_data; + bwr_buf_init(&bb_out, bwr_buffer, sizeof(bwr_buffer)); + bwr_buf_put_uint32(&bb_out, LKL_BC_DEAD_BINDER_DONE); + bwr_buf_put_uintptr(&bb_out, cookie); + binder_send(ctx, &bb_out); + } break; + case LKL_BR_TRANSACTION: + case LKL_BR_REPLY: { + txnin_init(&txnin, cmd_data); + push_binder_buffers_queue(&ctx->buffers, + (uintptr_t)txnin.data0); + } break; + default: + break; + } + } +} + +int binder_recv(struct binder_ctx *ctx, size_t size) +{ + int ret; + struct bwr_buf_t bb; + uint8_t read_buf[size]; + size_t read_consumed; + + ret = binder_ioctl_read(ctx, read_buf, sizeof(read_buf), + &read_consumed); + if (ret) + return ret; + bwr_buf_init(&bb, read_buf, read_consumed); + binder_execute_cmds(ctx, &bb); + + return 0; +} + +int binder_send(struct binder_ctx *ctx, struct bwr_buf_t *bb) +{ + return binder_ioctl_write(ctx, bb->data0, + PAD_SIZE_UNSAFE(bb->data - bb->data0)); +} + +void binder_bwr_acquire(struct bwr_buf_t *bb, uint32_t bc) +{ + bwr_buf_put_handle_req(bb, bc, LKL_BC_ACQUIRE); +} + +void binder_bwr_increfs(struct bwr_buf_t *bb, uint32_t bc) +{ + bwr_buf_put_handle_req(bb, bc, LKL_BC_INCREFS); +} + +void binder_bwr_release(struct bwr_buf_t *bb, uint32_t bc) +{ + bwr_buf_put_handle_req(bb, bc, LKL_BC_RELEASE); +} + +void binder_bwr_decrefs(struct bwr_buf_t *bb, uint32_t bc) +{ + bwr_buf_put_handle_req(bb, bc, LKL_BC_DECREFS); +} + +void binder_bwr_increfs_done(struct bwr_buf_t *bb, uintptr_t binder) +{ + bwr_buf_put_binder_req(bb, binder, 0, LKL_BC_INCREFS_DONE); +} + +void binder_bwr_acquire_done(struct bwr_buf_t *bb, uintptr_t binder) +{ + bwr_buf_put_binder_req(bb, binder, 0, LKL_BC_ACQUIRE_DONE); +} + +void binder_bwr_request_death_notification(struct bwr_buf_t *bb, uintptr_t binder) +{ + bwr_buf_put_death_notif_req(bb, binder, 0, + LKL_BC_REQUEST_DEATH_NOTIFICATION); +} + +void binder_bwr_clear_death_notification(struct bwr_buf_t *bb, uintptr_t binder) +{ + bwr_buf_put_death_notif_req(bb, binder, 0, + LKL_BC_CLEAR_DEATH_NOTIFICATION); +} + +void binder_bwr_register_looper(struct bwr_buf_t *bb) +{ + bwr_buf_put_bc(bb, LKL_BC_REGISTER_LOOPER); +} + +void binder_bwr_enter_looper(struct bwr_buf_t *bb) +{ + bwr_buf_put_bc(bb, LKL_BC_ENTER_LOOPER); +} + +void binder_bwr_exit_looper(struct bwr_buf_t *bb) +{ + bwr_buf_put_bc(bb, LKL_BC_EXIT_LOOPER); +} + +void binder_bwr_dead_binder_done(struct bwr_buf_t *bb, uintptr_t cookie) +{ + bwr_buf_put_uint32(bb, LKL_BC_DEAD_BINDER_DONE); + bwr_buf_put_uintptr(bb, cookie); +} + +void binder_bwr_free_buffer(struct bwr_buf_t *bb, uintptr_t buffer_addr) +{ + bwr_buf_put_uint32(bb, LKL_BC_FREE_BUFFER); + bwr_buf_put_uintptr(bb, buffer_addr); +} + +void binder_transaction_put_object_binder(struct txnout_t *txnout, uintptr_t binder) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + if (!bo) + return; + + struct lkl_flat_binder_object fbo = { + .hdr = { .type = LKL_BINDER_TYPE_BINDER }, + .binder = binder, + .cookie = 0, + }; + + bo->fbo = fbo; +} + +void binder_transaction_put_object_weak_binder(struct txnout_t *txnout, + uintptr_t weak_binder) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + if (!bo) + return; + + struct lkl_flat_binder_object fbo = { + .hdr = { .type = LKL_BINDER_TYPE_WEAK_BINDER }, + .binder = weak_binder, + .cookie = 0, + }; + + bo->fbo = fbo; +} + +void binder_transaction_put_object_handle(struct txnout_t *txnout, unsigned int handle) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + if (!bo) + return; + + struct lkl_flat_binder_object fbo = { + .hdr = { .type = LKL_BINDER_TYPE_HANDLE }, + .handle = handle, + }; + + bo->fbo = fbo; +} + +void binder_transaction_put_object_weak_handle(struct txnout_t *txnout, + unsigned int weak_handle) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + if (!bo) + return; + + struct lkl_flat_binder_object fbo = { + .hdr = { .type = LKL_BINDER_TYPE_WEAK_HANDLE }, + .handle = weak_handle, + }; + + bo->fbo = fbo; +} + +void binder_transaction_put_object_fd(struct txnout_t *txnout) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + if (!bo) + return; + + if (txnout->fd == -1) { + txnout->fd = + lkl_sys_open(".", LKL_O_RDONLY | LKL_O_DIRECTORY, 0); + } + + struct lkl_binder_fd_object fdo = { + .hdr = { .type = LKL_BINDER_TYPE_FD }, + .fd = (uint32_t)txnout->fd, + .cookie = 0, + }; + + bo->fdo = fdo; +} + +void binder_transaction_put_object_fda(struct txnout_t *txnout, size_t num_fds, + size_t parent, size_t parent_offset) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + if (!bo) + return; + + // TODO(zifantan): Open files and add them to an array buffer + struct lkl_binder_fd_array_object fdao = { + .hdr = { .type = LKL_BINDER_TYPE_FDA }, + .num_fds = num_fds, + .parent = parent, + .parent_offset = parent_offset, + }; + + bo->fdao = fdao; +} + +void binder_transaction_put_object_ptr(struct txnout_t *txnout, + unsigned int has_parent_flag, void *data, + size_t buffer_size, size_t parent, + size_t parent_offset) +{ + struct binder_object *bo = + (struct binder_object *)txnout_alloc(txnout, sizeof(*bo)); + + if (!bo) + return; + + void *bbo_buf = txnout_extra_buf_alloc(txnout, buffer_size); + + if (bbo_buf) + memcpy(bbo_buf, data, buffer_size); + + struct lkl_binder_buffer_object bbo = { + .hdr = { .type = LKL_BINDER_TYPE_PTR }, + .flags = (uint32_t)(has_parent_flag ? + LKL_BINDER_BUFFER_FLAG_HAS_PARENT : + 0), + .buffer = (lkl_binder_size_t)bbo_buf, + .length = (lkl_binder_size_t)buffer_size, + .parent = parent, + .parent_offset = parent_offset, + }; + + bo->bbo = bbo; +} + +void *bwr_buf_alloc_transaction(struct bwr_buf_t *bb, int reply, struct txnout_t *txnout, + unsigned int target_handle, unsigned int flags, + size_t extra_data_size, + size_t extra_offsets_size) +{ + bwr_buf_put_uint32(bb, reply ? LKL_BC_REPLY : LKL_BC_TRANSACTION); + + struct lkl_binder_transaction_data *tr_data = + (struct lkl_binder_transaction_data *)bwr_buf_alloc( + bb, sizeof(struct lkl_binder_transaction_data)); + + if (tr_data != NULL) { + tr_data->target.handle = target_handle; + tr_data->flags = flags; + + tr_data->data_size = txnout->data - txnout->data0; + tr_data->offsets_size = + ((uint8_t *)txnout->offs) - ((uint8_t *)txnout->offs0); + tr_data->data.ptr.buffer = (lkl_binder_uintptr_t)txnout->data0; + tr_data->data.ptr.offsets = (lkl_binder_uintptr_t)txnout->offs0; + + // Introduce unaligned data and offsets size + tr_data->data_size += extra_data_size; + tr_data->offsets_size += extra_offsets_size; + } + return tr_data; +} + +void *bwr_buf_alloc_transaction_sg(struct bwr_buf_t *bb, int reply, struct txnout_t *txnout, + unsigned int target_handle, + unsigned int flags, size_t extra_data_size, + size_t extra_offsets_size, + size_t extra_buffers_size) +{ + bwr_buf_put_uint32(bb, reply ? LKL_BC_REPLY_SG : LKL_BC_TRANSACTION_SG); + + struct lkl_binder_transaction_data_sg *tr_data_sg = + (struct lkl_binder_transaction_data_sg *)bwr_buf_alloc( + bb, sizeof(struct lkl_binder_transaction_data_sg)); + + if (tr_data_sg != NULL) { + tr_data_sg->transaction_data.target.handle = target_handle; + tr_data_sg->transaction_data.flags = flags; + + tr_data_sg->transaction_data.data_size = + txnout->data - txnout->data0; + tr_data_sg->transaction_data.offsets_size = + ((uint8_t *)txnout->offs) - ((uint8_t *)txnout->offs0); + tr_data_sg->transaction_data.data.ptr.buffer = + (lkl_binder_uintptr_t)txnout->data0; + tr_data_sg->transaction_data.data.ptr.offsets = + (lkl_binder_uintptr_t)txnout->offs0; + + // Introduce unaligned data and offsets size + tr_data_sg->transaction_data.data_size += extra_data_size; + tr_data_sg->transaction_data.offsets_size += extra_offsets_size; + + tr_data_sg->buffers_size = extra_buffers_size; + } + return tr_data_sg; +} + +void init_binder_buffers_queue(struct binder_buffers_queue *q) +{ + q->size = q->front = 0; +} + +int size_binder_buffers_queue(struct binder_buffers_queue *q) +{ + return q->size - q->front; +} + +uintptr_t front_binder_buffers_queue(struct binder_buffers_queue *q) +{ + return q->_queue[q->front]; +} + +void pop_binder_buffers_queue(struct binder_buffers_queue *q) +{ + if (q->front < q->size) + q->front++; +} + +void push_binder_buffers_queue(struct binder_buffers_queue *q, uintptr_t val) +{ + assert(q->size < BUFFERS_QUEUE_SIZE); + if (q->size < BUFFERS_QUEUE_SIZE) + q->_queue[q->size++] = val; +} + +static int check_binder_version(void) +{ + int ret; + struct binder_ctx *ctx; + + ctx = binder_open(); + if (ctx == NULL) + return -1; + + ret = binder_ioctl_check_version(ctx); + + binder_close(ctx); + return ret; +} + +void lkl_close_fd(int fd) +{ + lkl_sys_close(fd); +} + +void run_in_new_process(void) +{ + assert(lkl_sys_new_thread_group_leader() == 0); +} + +void initialize_lkl(void) +{ + int ret; + + assert(lkl_init(&lkl_host_ops) == 0); + assert(lkl_start_kernel("mem=50M loglevel=8") == 0); + + assert(lkl_mount_fs("sysfs") == 0); + assert(lkl_mount_fs("proc") == 0); + + ret = lkl_sys_mkdir("/dev", 0770); + assert(ret == 0 || ret == -LKL_EEXIST); + + assert(lkl_sys_mount("devtmpfs", "/dev", "devtmpfs", 0, NULL) == 0); + + assert(check_binder_version() == 0); +} diff --git a/tools/lkl/fuzzers/binder/binder.h b/tools/lkl/fuzzers/binder/binder.h new file mode 100644 index 00000000000000..ee41a1134117c2 --- /dev/null +++ b/tools/lkl/fuzzers/binder/binder.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef BINDER_H_ +#define BINDER_H_ + +#include + +#define RANDOM_SIZE_RANGE 8 + +#define BUFFERS_QUEUE_SIZE 256 + +struct bwr_buf_t { + uint8_t *data0; + uint8_t *data; + size_t data_avail; +}; + +void bwr_buf_init(struct bwr_buf_t *bb, uint8_t *ptr, size_t size); + +struct txnout_t { + uint8_t data0[256]; + uint8_t *data; + size_t data_avail; + size_t offs0[256]; + size_t *offs; + size_t offs_avail; + uint8_t extra_data0[256]; + uint8_t *extra_data; + size_t extra_data_avail; + int fd; +}; + +void txnout_init(struct txnout_t *txnout); + +struct binder_buffers_queue { + uintptr_t _queue[BUFFERS_QUEUE_SIZE]; + int size; + int front; +}; + +void init_binder_buffers_queue(struct binder_buffers_queue *q); +int size_binder_buffers_queue(struct binder_buffers_queue *q); +uintptr_t front_binder_buffers_queue(struct binder_buffers_queue *q); +void pop_binder_buffers_queue(struct binder_buffers_queue *q); +void push_binder_buffers_queue(struct binder_buffers_queue *q, uintptr_t val); + +/* + * Binder context + * @fd - file description to a binder device + * @epoll_fd - epoll for the binder fd + * @map_ptr - pointer to mmaped memory + * @map_size - mmapped memory size + * @task - a pointer to the task_struct that opened the binder device + * @buffers - buffers of incoming transactions + */ +struct binder_ctx { + int fd; + int epoll_fd; + void *map_ptr; + size_t map_size; + void *task; + struct binder_buffers_queue buffers; +}; + +/* + * Binder open, close + */ +struct binder_ctx *binder_open(void); +void binder_close(struct binder_ctx *ctx); + +/* + * IOCTL + */ +int binder_ioctl_set_context_manager(struct binder_ctx *ctx); +int binder_ioctl_write(struct binder_ctx *ctx, void *buffer, size_t size); +int binder_ioctl_read(struct binder_ctx *ctx, void *buffer, size_t size, + size_t *read_consumed); +int binder_ioctl_thread_exit(struct binder_ctx *ctx); +int binder_ioctl_check_version(struct binder_ctx *ctx); +int binder_ioctl_get_node_debug_info(struct binder_ctx *ctx, uintptr_t ptr); +int binder_ioctl_get_node_info_for_ref(struct binder_ctx *ctx, uint32_t handle); +int binder_ioctl_enable_oneway_spam_detection(struct binder_ctx *ctx, uint32_t e); + +void *bwr_buf_alloc_transaction(struct bwr_buf_t *bb, int reply, struct txnout_t *txnout, + unsigned int target_handle, unsigned int flags, + size_t extra_data_size, + size_t extra_offsets_size); +void *bwr_buf_alloc_transaction_sg(struct bwr_buf_t *bb, int reply, struct txnout_t *txnout, + unsigned int target_handle, + unsigned int flags, size_t extra_data_size, + size_t extra_offsets_size, + size_t extra_buffers_size); + +void binder_bwr_free_buffer(struct bwr_buf_t *bb, uintptr_t buffer_addr); + +void binder_bwr_dead_binder_done(struct bwr_buf_t *bb, uintptr_t cookie); + +int binder_recv(struct binder_ctx *ctx, size_t size); +int binder_send(struct binder_ctx *ctx, struct bwr_buf_t *bb); + +void binder_transaction_put_object_binder(struct txnout_t *txnout, uintptr_t binder); +void binder_transaction_put_object_weak_binder(struct txnout_t *txnout, + uintptr_t weak_binder); +void binder_transaction_put_object_handle(struct txnout_t *txnout, + unsigned int handle); +void binder_transaction_put_object_weak_handle(struct txnout_t *txnout, + unsigned int weak_handle); +void binder_transaction_put_object_fd(struct txnout_t *txnout); +void binder_transaction_put_object_fda(struct txnout_t *txnout, size_t num_fds, + size_t parent, size_t parent_offset); +void binder_transaction_put_object_ptr(struct txnout_t *txnout, + unsigned int has_parent_flag, + void *bbo_buf, size_t buffer_size, + size_t parent, size_t parent_offset); + +void binder_bwr_acquire(struct bwr_buf_t *bb, uint32_t bc); +void binder_bwr_increfs(struct bwr_buf_t *bb, uint32_t bc); +void binder_bwr_release(struct bwr_buf_t *bb, uint32_t bc); +void binder_bwr_decrefs(struct bwr_buf_t *bb, uint32_t bc); +void binder_bwr_increfs_done(struct bwr_buf_t *bb, uintptr_t binder); +void binder_bwr_acquire_done(struct bwr_buf_t *bb, uintptr_t binder); +void binder_bwr_request_death_notification(struct bwr_buf_t *bb, uintptr_t binder); +void binder_bwr_clear_death_notification(struct bwr_buf_t *bb, uintptr_t binder); +void binder_bwr_register_looper(struct bwr_buf_t *bb); +void binder_bwr_enter_looper(struct bwr_buf_t *bb); +void binder_bwr_exit_looper(struct bwr_buf_t *bb); + +void initialize_lkl(void); + +void lkl_close_fd(int fd); + +void run_in_new_process(void); + +#endif // BINDER_H_ diff --git a/tools/lkl/fuzzers/binder/binder.proto b/tools/lkl/fuzzers/binder/binder.proto new file mode 100644 index 00000000000000..82a4b9299e67aa --- /dev/null +++ b/tools/lkl/fuzzers/binder/binder.proto @@ -0,0 +1,111 @@ +syntax = "proto2"; + +message Session { + repeated Ioctl ioctls = 1; +} + +message Ioctl { + enum BinderClientId { + CLIENT_1 = 0; + CLIENT_2 = 1; + CLIENT_3 = 2; + } + oneof ioctl { + BinderWrite binder_write = 1; + BinderRead binder_read = 2; + bool binder_thread_exit = 3; + bool binder_version = 4; + BinderGetNodeDebugInfo binder_get_node_debug_info = 5; + BinderGetNodeInfoForRef binder_get_node_info_for_ref = 6; + bool binder_enable_oneway_spam_detection = 7; + } + required BinderClientId binder_client = 10; +} + +message BinderWrite { + repeated BinderCommand binder_commands = 1; +} + +message BinderRead { + required uint32 read_size = 1; +} + +message BinderCommand { + oneof bc { + uint32 acquire = 1; + uint32 increfs = 2; + uint32 release = 3; + uint32 decrefs = 4; + Binder increfs_done = 5; + Binder acquire_done = 6; + Transaction transaction = 7; + Transaction reply = 8; + TransactionSg transaction_sg = 9; + TransactionSg reply_sg = 10; + bool free_buffer = 11; + Binder request_death_notification = 12; + Binder clear_death_notification = 13; + bool dead_binder_done = 14; + bool register_looper = 15; + bool enter_looper = 16; + bool exit_looper = 17; + } +} + +message Transaction { + enum TransactionFlag { + TF_ONE_WAY = 0x1; + TF_ROOT_OBJECT = 0x04; + TF_STATUS_CODE = 0x08; + TF_ACCEPT_FDS = 0x10; + TF_CLEAR_BUF = 0x20; + TF_UPDATE_TXN = 0x40; + } + repeated BinderObject binder_objects = 1; + required uint32 target_handle = 2; + repeated TransactionFlag flags = 3; + required uint32 extra_data_size = 4; + required uint32 extra_offsets_size = 5; +} + +message BinderObject { + oneof type { + Binder binder = 1; + Binder weak_binder = 2; + uint32 handle = 3; + uint32 weak_handle = 4; + bool fd = 5; + FdArray fda = 6; + BufferObject ptr = 7; + } +} + +message Binder { + required uint32 ptr = 1; +} + +message FdArray { + required uint32 num_fds = 1; + required uint32 parent = 2; + required uint32 parent_offset = 3; +} + +message BufferObject { + required bool has_parent_flag = 1; + repeated bytes buffer = 2; + required uint32 parent = 3; + required uint32 parent_offset = 4; +} + +message TransactionSg { + required Transaction transaction = 1; + required uint32 extra_buffers_size = 2; +} + +message BinderGetNodeDebugInfo { + required uint32 ptr = 1; +} + +message BinderGetNodeInfoForRef { + required uint32 handle = 1; +} diff --git a/tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 b/tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 new file mode 100644 index 00000000000000..97f898157432fd --- /dev/null +++ b/tools/lkl/fuzzers/binder/seeds/CVE-2023-20938 @@ -0,0 +1,83 @@ +ioctls { + binder_write { + binder_commands { + transaction { + binder_objects { + binder { + ptr: 7 + } + } + binder_objects { + binder { + ptr: 9 + } + } + target_handle: 0 + flags: TF_ONE_WAY + extra_data_size: 0 + extra_offsets_size: 0 + } + } + } + binder_client: CLIENT_3 +} +ioctls { + binder_write { + binder_commands { + transaction { + binder_objects { + binder { + ptr: 4 + } + } + target_handle: 2 + flags: TF_ONE_WAY + extra_data_size: 0 + extra_offsets_size: 0 + } + } + binder_commands { + release: 2 + } + binder_commands { + transaction { + binder_objects { + binder { + ptr: 9 + } + } + target_handle: 1 + flags: TF_ONE_WAY + extra_data_size: 0 + extra_offsets_size: 4 + } + } + } + binder_client: CLIENT_1 +} +ioctls { + binder_write { + binder_commands { + enter_looper: true + } + } + binder_client: CLIENT_3 +} +ioctls { + binder_read { + read_size: 1024 + } + binder_client: CLIENT_3 +} +ioctls { + binder_read { + read_size: 1024 + } + binder_client: CLIENT_3 +} +ioctls { + binder_read { + read_size: 1024 + } + binder_client: CLIENT_3 +} diff --git a/tools/lkl/scripts/libprotobuf-mutator-build.sh b/tools/lkl/scripts/libprotobuf-mutator-build.sh new file mode 100755 index 00000000000000..b324f664641c0a --- /dev/null +++ b/tools/lkl/scripts/libprotobuf-mutator-build.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +set -e + +if [ -z ${PROTOBUF_MUTATOR_DIR+x} ]; then + echo PROTOBUF_MUTATOR_DIR is not defined + exit 1 +fi + +rm -rf "${PROTOBUF_MUTATOR_DIR}" + +git clone https://github.com/google/libprotobuf-mutator.git \ + "${PROTOBUF_MUTATOR_DIR}" +cd "${PROTOBUF_MUTATOR_DIR}" +git checkout tags/v1.4 + +mkdir -p build && cd build +cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_BUILD_TYPE=Debug -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=on +ninja From 299753ca976a7a9aedbfec54ccf2a6c420426814 Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Wed, 26 Feb 2025 20:43:23 +0000 Subject: [PATCH 3/4] Force including Makefile.conf in tools/lkl/Makefile Currently Makefile.conf is included in tools/lkl/Makefile using `-include` construct which won't generate an error if Makefile.conf is not present which might happen due to an error while executing Makefile.autoconf. As Makefile.conf contains important LKL configuration options `make` should not proceed with building targets. Do the same for inclusion of Target and ../scripts/Makefile.include files. Signed-off-by: Eugene Rodionov --- tools/lkl/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 6650b4b8dab788..ee76a1089099fd 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -24,7 +24,7 @@ ifeq (,$(srctree)) endif export srctree --include ../scripts/Makefile.include +include ../scripts/Makefile.include # OUTPUT fixup should be *after* include ../scripts/Makefile.include ifneq ($(OUTPUT),) @@ -42,13 +42,13 @@ conf: $(OUTPUT)Makefile.conf $(OUTPUT)Makefile.conf $(OUTPUT)include/kernel_config.h $(OUTPUT)/kernel.config: Makefile.autoconf $(call QUIET_AUTOCONF, headers)$(MAKE) -f Makefile.autoconf -s --include $(OUTPUT)Makefile.conf +include $(OUTPUT)Makefile.conf export CFLAGS += -I$(OUTPUT)/include -Iinclude -Wall -g -O2 -Wextra \ -Wno-unused-parameter \ -Wno-missing-field-initializers -fno-strict-aliasing --include Targets +include Targets # Expand targets to output location and suffix but preserve special # targets (e.g. .WAIT) From 07b74e7afd6d8bdfa3c2e5fd81117484d7a2fa30 Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Thu, 27 Feb 2025 05:31:44 +0000 Subject: [PATCH 4/4] Add LKL fuzzers tests to github CI. Move lkl-fuzzing test from tests group into a dedicated entry. Building fuzzers target generates fuzzer bineries and produces no test binaries (e.g. boot, disk, etc). Enable MMU and libprotobuf-mutator for testing LKL fuzzers in CI. Signed-off-by: Eugene Rodionov --- .github/workflows/ci.yml | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c41f3e84db57f9..b571674900005f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,11 +37,6 @@ 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" - displayTargetName: zpoline # maybe integrate with default Linux build once the function becomes stable os: unix @@ -178,3 +173,27 @@ jobs: run: sudo pip install ply GitPython - name: Check coding style run: tools/lkl/scripts/checkpatch.sh + + fuzzers: + runs-on: ubuntu-22.04 + name: fuzzers + env: + PROTOBUF_MUTATOR_DIR: /tmp/libprotobuf-mutator + steps: + - name: Checkout + with: + fetch-depth: 0 + uses: actions/checkout@v4 + - name: Install clang toolchain + run: | + sudo apt update -y + sudo apt install -y clang lld llvm + - name: Install libprotobuf-mutator prerequisites + run: | + sudo apt update -y + sudo apt install -y binutils cmake ninja-build liblzma-dev libz-dev \ + pkg-config autoconf libtool + - name: Build libprotobuf-mutator + run: tools/lkl/scripts/libprotobuf-mutator-build.sh + - name: Build fuzzers + run: make -j4 -C tools/lkl LKL_FUZZING=1 MMU=1 fuzzers