dokyungs updated this revision to Diff 277462. dokyungs added a comment. Add interceptors for all the functions libFuzzer has a weak interceptor for, and duplicate existing interceptor test cases with new compiler flags (-fno-sanitize=address).
Builtin libfunc optimizations may transform memcmp and strcmp-like functions. To disable such optimizations, -fno-builtin= flag was additionally added in compiling new test cases. FWIW, the original test cases didn't require such flags since other sanitizers including ASan disables those optimizations in their LLVM pass by dropping libfunc attribute in the call instructions. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D83494/new/ https://reviews.llvm.org/D83494 Files: clang/include/clang/Driver/SanitizerArgs.h clang/lib/Driver/SanitizerArgs.cpp clang/lib/Driver/ToolChains/CommonArgs.cpp compiler-rt/lib/fuzzer/CMakeLists.txt compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp compiler-rt/test/fuzzer/no-asan-memcmp.test compiler-rt/test/fuzzer/no-asan-strcmp.test compiler-rt/test/fuzzer/no-asan-strncmp.test compiler-rt/test/fuzzer/no-asan-strstr.test
Index: compiler-rt/test/fuzzer/no-asan-strstr.test =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/no-asan-strstr.test @@ -0,0 +1,5 @@ +UNSUPPORTED: freebsd +RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-strstr %S/StrstrTest.cpp -o %t-StrstrTest +RUN: not %run %t-StrstrTest -seed=1 -runs=2000000 2>&1 | FileCheck %s +CHECK: BINGO + Index: compiler-rt/test/fuzzer/no-asan-strncmp.test =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/no-asan-strncmp.test @@ -0,0 +1,4 @@ +UNSUPPORTED: freebsd +RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-strncmp %S/StrncmpTest.cpp -o %t-StrncmpTest +RUN: not %run %t-StrncmpTest -seed=2 -runs=10000000 2>&1 | FileCheck %s +CHECK: BINGO Index: compiler-rt/test/fuzzer/no-asan-strcmp.test =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/no-asan-strcmp.test @@ -0,0 +1,5 @@ +UNSUPPORTED: freebsd +RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-strcmp %S/StrcmpTest.cpp -o %t-StrcmpTest +RUN: not %run %t-StrcmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s +CHECK: BINGO + Index: compiler-rt/test/fuzzer/no-asan-memcmp.test =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/no-asan-memcmp.test @@ -0,0 +1,4 @@ +UNSUPPORTED: freebsd +RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-memcmp %S/MemcmpTest.cpp -o %t-MemcmpTest +RUN: not %run %t-MemcmpTest -seed=1 -runs=10000000 2>&1 | FileCheck %s +CHECK: BINGO Index: compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp @@ -0,0 +1,157 @@ +//===-- FuzzerInterceptors.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Intercept certain libc functions to aid fuzzing. +// Linked only when other RTs that define their own interceptors are not linked. +//===----------------------------------------------------------------------===// + +#include "FuzzerBuiltins.h" + +#if LIBFUZZER_LINUX + +typedef unsigned long uptr; + +#include <dlfcn.h> // for dlsym() + +static void *GetFuncAddr(const char *name, uptr wrapper_addr) { + void *addr = dlsym(RTLD_NEXT, name); + if (!addr) { + // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is + // later in the library search order than the DSO that we are trying to + // intercept, which means that we cannot intercept this function. We still + // want the address of the real definition, though, so look it up using + // RTLD_DEFAULT. + addr = dlsym(RTLD_DEFAULT, name); + + // In case `name' is not loaded, dlsym ends up finding the actual wrapper. + // We don't want to intercept the wrapper and have it point to itself. + if ((uptr)addr == wrapper_addr) + addr = nullptr; + } + return addr; +} + +extern "C" { + +void __sanitizer_weak_hook_memcmp(void *, const void *, const void *, size_t, + int); +void __sanitizer_weak_hook_strncmp(void *, const char *, const char *, size_t, + int); +void __sanitizer_weak_hook_strcmp(void *, const char *, const char *, int); +void __sanitizer_weak_hook_strncasecmp(void *, const char *, const char *, + size_t, int); +void __sanitizer_weak_hook_strcasecmp(void *, const char *, const char *, int); +void __sanitizer_weak_hook_strstr(void *, const char *, const char *, char *); +void __sanitizer_weak_hook_strcasestr(void *, const char *, const char *, + char *); +void __sanitizer_weak_hook_memmem(void *, const void *, size_t, const void *, + size_t, void *); + +typedef int (*memcmp_t)(const void *, const void *, size_t); +typedef int (*strncmp_t)(const char *, const char *, size_t); +typedef int (*strcmp_t)(const char *, const char *); +typedef int (*strncasecmp_t)(const char *, const char *, size_t); +typedef int (*strcasecmp_t)(const char *, const char *); +typedef char *(*strstr_t)(char *, const char *); +typedef char *(*strcasestr_t)(char *, const char *); +typedef void *(*memmem_t)(const void *, size_t, const void *, size_t); + +static memcmp_t real_memcmp; +static strncmp_t real_strncmp; +static strcmp_t real_strcmp; +static strncasecmp_t real_strncasecmp; +static strcasecmp_t real_strcasecmp; +static strstr_t real_strstr; +static strcasestr_t real_strcasestr; +static memmem_t real_memmem; + +ATTRIBUTE_INTERFACE int memcmp(const void *ptr1, const void *ptr2, size_t n) { + int result = real_memcmp(ptr1, ptr2, n); + __sanitizer_weak_hook_memcmp(GET_CALLER_PC(), ptr1, ptr2, n, result); + + return result; +} + +ATTRIBUTE_INTERFACE int strncmp(const char *str1, const char *str2, size_t n) { + int result = real_strncmp(str1, str2, n); + __sanitizer_weak_hook_strncmp(GET_CALLER_PC(), str1, str2, n, result); + + return result; +} + +ATTRIBUTE_INTERFACE int strcmp(const char *str1, const char *str2) { + int result = real_strcmp(str1, str2); + __sanitizer_weak_hook_strcmp(GET_CALLER_PC(), str1, str2, result); + + return result; +} + +ATTRIBUTE_INTERFACE int strncasecmp(const char *str1, const char *str2, + size_t n) { + int result = real_strncasecmp(str1, str2, n); + __sanitizer_weak_hook_strncasecmp(GET_CALLER_PC(), str1, str2, n, result); + + return result; +} + +ATTRIBUTE_INTERFACE +int strcasecmp(const char *str1, const char *str2) { + int result = real_strcasecmp(str1, str2); + __sanitizer_weak_hook_strcasecmp(GET_CALLER_PC(), str1, str2, result); + + return result; +} + +extern "C++" ATTRIBUTE_INTERFACE char *strstr(char *str1, const char *str2) { + char *result = real_strstr(str1, str2); + __sanitizer_weak_hook_strstr(GET_CALLER_PC(), str1, str2, result); + + return result; +} + +extern "C++" ATTRIBUTE_INTERFACE char *strcasestr(char *str1, + const char *str2) { + char *result = real_strcasestr(str1, str2); + __sanitizer_weak_hook_strcasestr(GET_CALLER_PC(), str1, str2, result); + + return result; +} + +ATTRIBUTE_INTERFACE +void *memmem(const void *haystack, size_t haystacklen, const void *needle, + size_t needlelen) { + void *result = real_memmem(haystack, haystacklen, needle, needlelen); + __sanitizer_weak_hook_memmem(GET_CALLER_PC(), haystack, haystacklen, needle, + needlelen, result); + + return result; +} + +static void __fuzzer_init() { + real_memcmp = + reinterpret_cast<memcmp_t>(GetFuncAddr("memcmp", (uptr)&memcmp)); + real_strncmp = + reinterpret_cast<strncmp_t>(GetFuncAddr("strncmp", (uptr)&strncmp)); + real_strcmp = + reinterpret_cast<strcmp_t>(GetFuncAddr("strcmp", (uptr)&strcmp)); + real_strncasecmp = reinterpret_cast<strncasecmp_t>( + GetFuncAddr("strncasecmp", (uptr)&strncasecmp)); + real_strcasecmp = reinterpret_cast<strcasecmp_t>( + GetFuncAddr("strcasecmp", (uptr)&strcasecmp)); + real_strstr = reinterpret_cast<strstr_t>( + GetFuncAddr("strstr", (uptr)(strstr_t)&strstr)); + real_strcasestr = reinterpret_cast<strcasestr_t>( + GetFuncAddr("strcasestr", (uptr)(strcasestr_t)&strcasestr)); + real_memmem = + reinterpret_cast<memmem_t>(GetFuncAddr("memmem", (uptr)&memmem)); +} + +__attribute__((section(".preinit_array"), used)) static void ( + *__local_fuzzer_preinit)(void) = __fuzzer_init; +} + +#endif Index: compiler-rt/lib/fuzzer/CMakeLists.txt =================================================================== --- compiler-rt/lib/fuzzer/CMakeLists.txt +++ compiler-rt/lib/fuzzer/CMakeLists.txt @@ -99,6 +99,13 @@ CFLAGS ${LIBFUZZER_CFLAGS} DEPS ${LIBFUZZER_DEPS}) +add_compiler_rt_object_libraries(RTfuzzer_interceptors + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + SOURCES FuzzerInterceptors.cpp + CFLAGS ${LIBFUZZER_CFLAGS} + DEPS ${LIBFUZZER_DEPS}) + add_compiler_rt_runtime(clang_rt.fuzzer STATIC OS ${FUZZER_SUPPORTED_OS} @@ -115,6 +122,14 @@ CFLAGS ${LIBFUZZER_CFLAGS} PARENT_TARGET fuzzer) +add_compiler_rt_runtime(clang_rt.fuzzer_interceptors + STATIC + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + OBJECT_LIBS RTfuzzer_interceptors + CFLAGS ${LIBFUZZER_CFLAGS} + PARENT_TARGET fuzzer) + if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH AND COMPILER_RT_LIBCXXABI_PATH) @@ -148,7 +163,10 @@ add_dependencies(RTfuzzer.${arch} libcxx_fuzzer_${arch}-build) target_compile_options(RTfuzzer_main.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1) add_dependencies(RTfuzzer_main.${arch} libcxx_fuzzer_${arch}-build) + target_compile_options(RTfuzzer_interceptors.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1) + add_dependencies(RTfuzzer_interceptors.${arch} libcxx_fuzzer_${arch}-build) partially_link_libcxx(fuzzer_no_main ${LIBCXX_${arch}_PREFIX} ${arch}) + partially_link_libcxx(fuzzer_interceptors ${LIBCXX_${arch}_PREFIX} ${arch}) partially_link_libcxx(fuzzer ${LIBCXX_${arch}_PREFIX} ${arch}) endforeach() endif() Index: clang/lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.cpp +++ clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -779,6 +779,9 @@ !Args.hasArg(options::OPT_shared)) { addSanitizerRuntime(TC, Args, CmdArgs, "fuzzer", false, true); + if (SanArgs.needsFuzzerInterceptors()) + addSanitizerRuntime(TC, Args, CmdArgs, "fuzzer_interceptors", false, + true); if (!Args.hasArg(clang::driver::options::OPT_nostdlibxx)) TC.AddCXXStdlibLibArgs(Args, CmdArgs); } Index: clang/lib/Driver/SanitizerArgs.cpp =================================================================== --- clang/lib/Driver/SanitizerArgs.cpp +++ clang/lib/Driver/SanitizerArgs.cpp @@ -238,6 +238,11 @@ return TrappingKinds; } +bool SanitizerArgs::needsFuzzerInterceptors() const { + return needsFuzzer() && !needsAsanRt() && !needsHwasanRt() && + !needsTsanRt() && !needsMsanRt(); +} + bool SanitizerArgs::needsUbsanRt() const { // All of these include ubsan. if (needsAsanRt() || needsMsanRt() || needsHwasanRt() || needsTsanRt() || Index: clang/include/clang/Driver/SanitizerArgs.h =================================================================== --- clang/include/clang/Driver/SanitizerArgs.h +++ clang/include/clang/Driver/SanitizerArgs.h @@ -74,6 +74,7 @@ !Sanitizers.has(SanitizerKind::Address) && !Sanitizers.has(SanitizerKind::HWAddress); } + bool needsFuzzerInterceptors() const; bool needsUbsanRt() const; bool requiresMinimalRuntime() const { return MinimalRuntime; } bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits