diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/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); } diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/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() || diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/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); } diff --git a/compiler-rt/lib/fuzzer/CMakeLists.txt b/compiler-rt/lib/fuzzer/CMakeLists.txt --- a/compiler-rt/lib/fuzzer/CMakeLists.txt +++ b/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() diff --git a/compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp b/compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp new file mode 100644 --- /dev/null +++ b/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 // 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(GetFuncAddr("memcmp", (uptr)&memcmp)); + real_strncmp = + reinterpret_cast(GetFuncAddr("strncmp", (uptr)&strncmp)); + real_strcmp = + reinterpret_cast(GetFuncAddr("strcmp", (uptr)&strcmp)); + real_strncasecmp = reinterpret_cast( + GetFuncAddr("strncasecmp", (uptr)&strncasecmp)); + real_strcasecmp = reinterpret_cast( + GetFuncAddr("strcasecmp", (uptr)&strcasecmp)); + real_strstr = reinterpret_cast( + GetFuncAddr("strstr", (uptr)(strstr_t)&strstr)); + real_strcasestr = reinterpret_cast( + GetFuncAddr("strcasestr", (uptr)(strcasestr_t)&strcasestr)); + real_memmem = + reinterpret_cast(GetFuncAddr("memmem", (uptr)&memmem)); +} + +__attribute__((section(".preinit_array"), used)) static void ( + *__local_fuzzer_preinit)(void) = __fuzzer_init; +} + +#endif diff --git a/compiler-rt/test/fuzzer/no-asan-memcmp.test b/compiler-rt/test/fuzzer/no-asan-memcmp.test new file mode 100644 --- /dev/null +++ b/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 diff --git a/compiler-rt/test/fuzzer/no-asan-strcmp.test b/compiler-rt/test/fuzzer/no-asan-strcmp.test new file mode 100644 --- /dev/null +++ b/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 + diff --git a/compiler-rt/test/fuzzer/no-asan-strncmp.test b/compiler-rt/test/fuzzer/no-asan-strncmp.test new file mode 100644 --- /dev/null +++ b/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 diff --git a/compiler-rt/test/fuzzer/no-asan-strstr.test b/compiler-rt/test/fuzzer/no-asan-strstr.test new file mode 100644 --- /dev/null +++ b/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 +