This revision was automatically updated to reflect the committed changes.
dokyungs marked an inline comment as done.
Closed by commit rG831ae45e3dc6: Recommit "[libFuzzer] Link 
libFuzzer's own interceptors when other compiler… (authored by dokyungs).

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/CustomAllocator.cpp
  compiler-rt/test/fuzzer/custom-allocator.test
  compiler-rt/test/fuzzer/memcmp.test
  compiler-rt/test/fuzzer/memcmp64.test
  compiler-rt/test/fuzzer/strcmp.test
  compiler-rt/test/fuzzer/strncmp.test
  compiler-rt/test/fuzzer/strstr.test

Index: compiler-rt/test/fuzzer/strstr.test
===================================================================
--- compiler-rt/test/fuzzer/strstr.test
+++ compiler-rt/test/fuzzer/strstr.test
@@ -1,5 +1,11 @@
 UNSUPPORTED: freebsd
 RUN: %cpp_compiler %S/StrstrTest.cpp -o %t-StrstrTest
 RUN: not %run %t-StrstrTest               -seed=1 -runs=2000000   2>&1 | FileCheck %s
-CHECK: BINGO
 
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-strstr %S/StrstrTest.cpp -o %t-NoAsanStrstrTest
+RUN: not %run %t-NoAsanStrstrTest         -seed=1 -runs=2000000   2>&1 | FileCheck %s
+
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-calloc -fno-builtin-strstr %S/CustomAllocator.cpp %S/StrstrTest.cpp -o %t-NoAsanCustomAllocatorStrstrTest
+RUN: not %run %t-NoAsanCustomAllocatorStrstrTest -seed=1 -runs=2000000   2>&1 | FileCheck %s
+
+CHECK: BINGO
Index: compiler-rt/test/fuzzer/strncmp.test
===================================================================
--- compiler-rt/test/fuzzer/strncmp.test
+++ compiler-rt/test/fuzzer/strncmp.test
@@ -1,5 +1,11 @@
 UNSUPPORTED: freebsd
 RUN: %cpp_compiler %S/StrncmpTest.cpp -o %t-StrncmpTest
 RUN: not %run %t-StrncmpTest              -seed=2 -runs=10000000   2>&1 | FileCheck %s
-CHECK: BINGO
 
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-strncmp %S/StrncmpTest.cpp -o %t-NoAsanStrncmpTest
+RUN: not %run %t-NoAsanStrncmpTest        -seed=2 -runs=10000000   2>&1 | FileCheck %s
+
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-calloc -fno-builtin-strncmp %S/CustomAllocator.cpp %S/StrncmpTest.cpp -o %t-NoAsanCustomAllocatorStrncmpTest
+RUN: not %run %t-NoAsanCustomAllocatorStrncmpTest -seed=2 -runs=10000000   2>&1 | FileCheck %s
+
+CHECK: BINGO
Index: compiler-rt/test/fuzzer/strcmp.test
===================================================================
--- compiler-rt/test/fuzzer/strcmp.test
+++ compiler-rt/test/fuzzer/strcmp.test
@@ -1,5 +1,11 @@
 UNSUPPORTED: freebsd
 RUN: %cpp_compiler %S/StrcmpTest.cpp -o %t-StrcmpTest
 RUN: not %run %t-StrcmpTest               -seed=1 -runs=2000000   2>&1 | FileCheck %s
-CHECK: BINGO
 
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-strcmp %S/StrcmpTest.cpp -o %t-NoAsanStrcmpTest
+RUN: not %run %t-NoAsanStrcmpTest         -seed=1 -runs=2000000   2>&1 | FileCheck %s
+
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-calloc -fno-builtin-strcmp %S/CustomAllocator.cpp %S/StrcmpTest.cpp -o %t-NoAsanCustomAllocatorStrcmpTest
+RUN: not %run %t-NoAsanCustomAllocatorStrcmpTest -seed=1 -runs=2000000   2>&1 | FileCheck %s
+
+CHECK: BINGO
Index: compiler-rt/test/fuzzer/memcmp64.test
===================================================================
--- compiler-rt/test/fuzzer/memcmp64.test
+++ compiler-rt/test/fuzzer/memcmp64.test
@@ -1,4 +1,8 @@
 UNSUPPORTED: freebsd
 RUN: %cpp_compiler %S/Memcmp64BytesTest.cpp -o %t-Memcmp64BytesTest
 RUN: not %run %t-Memcmp64BytesTest        -seed=1 -runs=1000000   2>&1 | FileCheck %s
+
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-memcmp %S/Memcmp64BytesTest.cpp -o %t-NoAsanMemcmp64BytesTest
+RUN: not %run %t-NoAsanMemcmp64BytesTest  -seed=1 -runs=1000000   2>&1 | FileCheck %s
+
 CHECK: BINGO
Index: compiler-rt/test/fuzzer/memcmp.test
===================================================================
--- compiler-rt/test/fuzzer/memcmp.test
+++ compiler-rt/test/fuzzer/memcmp.test
@@ -1,4 +1,11 @@
 UNSUPPORTED: freebsd
 RUN: %cpp_compiler %S/MemcmpTest.cpp -o %t-MemcmpTest
 RUN: not %run %t-MemcmpTest               -seed=1 -runs=10000000   2>&1 | FileCheck %s
+
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-memcmp %S/MemcmpTest.cpp -o %t-NoAsanMemcmpTest
+RUN: not %run %t-NoAsanMemcmpTest         -seed=1 -runs=10000000   2>&1 | FileCheck %s
+
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin-calloc -fno-builtin-memcmp %S/CustomAllocator.cpp %S/MemcmpTest.cpp -o %t-NoAsanCustomAllocatorMemcmpTest
+RUN: not %run %t-NoAsanCustomAllocatorMemcmpTest -seed=1 -runs=10000000   2>&1 | FileCheck %s
+
 CHECK: BINGO
Index: compiler-rt/test/fuzzer/custom-allocator.test
===================================================================
--- /dev/null
+++ compiler-rt/test/fuzzer/custom-allocator.test
@@ -0,0 +1,9 @@
+UNSUPPORTED: freebsd
+RUN: %cpp_compiler -fno-sanitize=address -fno-builtin %S/CustomAllocator.cpp %S/EmptyTest.cpp -o %t-NoAsanCustomAllocatorTest
+
+RUN: %run %t-NoAsanCustomAllocatorTest -runs=1 2>&1 | FileCheck %s
+
+CHECK: CALLOC-MEMCMP
+CHECK-NEXT: CALLOC-STRNCMP
+CHECK-NEXT: CALLOC-STRCMP
+CHECK-NEXT: CALLOC-STRSTR
Index: compiler-rt/test/fuzzer/CustomAllocator.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/fuzzer/CustomAllocator.cpp
@@ -0,0 +1,46 @@
+// 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
+
+// Test whether calling certain libFuzzer's interceptors inside allocators
+// does not cause an assertion failure.
+#include <assert.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <malloc.h>
+
+static const char *buf1 = "aaaa";
+static const char *buf2 = "bbbb";
+
+static void callFuzzerInterceptors(const char *prefix) {
+  int memcmp_result = memcmp(buf1, buf2, 4);
+  if (memcmp_result != 0) {
+    fprintf(stderr, "%s-MEMCMP\n", prefix);
+  }
+  int strncmp_result = strncmp(buf1, buf2, 4);
+  if (strncmp_result != 0) {
+    fprintf(stderr, "%s-STRNCMP\n", prefix);
+  }
+  int strcmp_result = strcmp(buf1, buf2);
+  if (strcmp_result != 0) {
+    fprintf(stderr, "%s-STRCMP\n", prefix);
+  }
+  const char *strstr_result = strstr(buf1, buf2);
+  if (strstr_result == nullptr) {
+    fprintf(stderr, "%s-STRSTR\n", prefix);
+  }
+}
+
+extern "C" void *__libc_calloc(size_t, size_t);
+
+extern "C" void *calloc(size_t n, size_t elem_size) {
+  static bool CalledOnce = false;
+  if (!CalledOnce) {
+    callFuzzerInterceptors("CALLOC");
+    CalledOnce = true;
+  }
+  return __libc_calloc(n, elem_size);
+}
Index: compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp
===================================================================
--- /dev/null
+++ compiler-rt/lib/fuzzer/FuzzerInterceptors.cpp
@@ -0,0 +1,224 @@
+//===-- 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 "FuzzerPlatform.h"
+
+#if LIBFUZZER_LINUX
+
+#define GET_CALLER_PC() __builtin_return_address(0)
+
+#define PTR_TO_REAL(x) real_##x
+#define REAL(x) __interception::PTR_TO_REAL(x)
+#define FUNC_TYPE(x) x##_type
+#define DEFINE_REAL(ret_type, func, ...)                                       \
+  typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__);                            \
+  namespace __interception {                                                   \
+  FUNC_TYPE(func) PTR_TO_REAL(func);                                           \
+  }
+
+#include <cassert>
+#include <cstdint>
+#include <dlfcn.h> // for dlsym()
+#include <sanitizer/common_interface_defs.h>
+
+static void *getFuncAddr(const char *name, uintptr_t 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 (reinterpret_cast<uintptr_t>(addr) == wrapper_addr)
+      addr = nullptr;
+  }
+  return addr;
+}
+
+static int FuzzerInited = 0;
+static bool FuzzerInitIsRunning;
+
+static void fuzzerInit();
+
+static void ensureFuzzerInited() {
+  assert(!FuzzerInitIsRunning);
+  if (!FuzzerInited) {
+    fuzzerInit();
+  }
+}
+
+static int internal_strcmp_strncmp(const char *s1, const char *s2, bool strncmp,
+                                   size_t n) {
+  size_t i = 0;
+  while (true) {
+    if (strncmp) {
+      if (i == n)
+        break;
+      i++;
+    }
+    unsigned c1 = *s1;
+    unsigned c2 = *s2;
+    if (c1 != c2)
+      return (c1 < c2) ? -1 : 1;
+    if (c1 == 0)
+      break;
+    s1++;
+    s2++;
+  }
+  return 0;
+}
+
+static int internal_strncmp(const char *s1, const char *s2, size_t n) {
+  return internal_strcmp_strncmp(s1, s2, true, n);
+}
+
+static int internal_strcmp(const char *s1, const char *s2) {
+  return internal_strcmp_strncmp(s1, s2, false, 0);
+}
+
+static int internal_memcmp(const void *s1, const void *s2, size_t n) {
+  const uint8_t *t1 = static_cast<const uint8_t *>(s1);
+  const uint8_t *t2 = static_cast<const uint8_t *>(s2);
+  for (size_t i = 0; i < n; ++i, ++t1, ++t2)
+    if (*t1 != *t2)
+      return *t1 < *t2 ? -1 : 1;
+  return 0;
+}
+
+static size_t internal_strlen(const char *s) {
+  size_t i = 0;
+  while (s[i])
+    i++;
+  return i;
+}
+
+static char *internal_strstr(const char *haystack, const char *needle) {
+  // This is O(N^2), but we are not using it in hot places.
+  size_t len1 = internal_strlen(haystack);
+  size_t len2 = internal_strlen(needle);
+  if (len1 < len2)
+    return nullptr;
+  for (size_t pos = 0; pos <= len1 - len2; pos++) {
+    if (internal_memcmp(haystack + pos, needle, len2) == 0)
+      return const_cast<char *>(haystack) + pos;
+  }
+  return nullptr;
+}
+
+extern "C" {
+
+DEFINE_REAL(int, memcmp, const void *, const void *, size_t)
+DEFINE_REAL(int, strncmp, const char *, const char *, size_t)
+DEFINE_REAL(int, strcmp, const char *, const char *)
+DEFINE_REAL(int, strncasecmp, const char *, const char *, size_t)
+DEFINE_REAL(int, strcasecmp, const char *, const char *)
+DEFINE_REAL(char *, strstr, const char *, const char *)
+DEFINE_REAL(char *, strcasestr, const char *, const char *)
+DEFINE_REAL(void *, memmem, const void *, size_t, const void *, size_t)
+
+ATTRIBUTE_INTERFACE int memcmp(const void *s1, const void *s2, size_t n) {
+  if (!FuzzerInited)
+    return internal_memcmp(s1, s2, n);
+  int result = REAL(memcmp)(s1, s2, n);
+  __sanitizer_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE int strncmp(const char *s1, const char *s2, size_t n) {
+  if (!FuzzerInited)
+    return internal_strncmp(s1, s2, n);
+  int result = REAL(strncmp)(s1, s2, n);
+  __sanitizer_weak_hook_strncmp(GET_CALLER_PC(), s1, s2, n, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE int strcmp(const char *s1, const char *s2) {
+  if (!FuzzerInited)
+    return internal_strcmp(s1, s2);
+  int result = REAL(strcmp)(s1, s2);
+  __sanitizer_weak_hook_strcmp(GET_CALLER_PC(), s1, s2, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE int strncasecmp(const char *s1, const char *s2, size_t n) {
+  ensureFuzzerInited();
+  int result = REAL(strncasecmp)(s1, s2, n);
+  __sanitizer_weak_hook_strncasecmp(GET_CALLER_PC(), s1, s2, n, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE int strcasecmp(const char *s1, const char *s2) {
+  ensureFuzzerInited();
+  int result = REAL(strcasecmp)(s1, s2);
+  __sanitizer_weak_hook_strcasecmp(GET_CALLER_PC(), s1, s2, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE char *strstr(const char *s1, const char *s2) {
+  if (!FuzzerInited)
+    return internal_strstr(s1, s2);
+  char *result = REAL(strstr)(s1, s2);
+  __sanitizer_weak_hook_strstr(GET_CALLER_PC(), s1, s2, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE char *strcasestr(const char *s1, const char *s2) {
+  ensureFuzzerInited();
+  char *result = REAL(strcasestr)(s1, s2);
+  __sanitizer_weak_hook_strcasestr(GET_CALLER_PC(), s1, s2, result);
+  return result;
+}
+
+ATTRIBUTE_INTERFACE
+void *memmem(const void *s1, size_t len1, const void *s2, size_t len2) {
+  ensureFuzzerInited();
+  void *result = REAL(memmem)(s1, len1, s2, len2);
+  __sanitizer_weak_hook_memmem(GET_CALLER_PC(), s1, len1, s2, len2, result);
+  return result;
+}
+
+__attribute__((section(".preinit_array"),
+               used)) static void (*__local_fuzzer_preinit)(void) = fuzzerInit;
+
+} // extern "C"
+
+static void fuzzerInit() {
+  assert(!FuzzerInitIsRunning);
+  if (FuzzerInited)
+    return;
+  FuzzerInitIsRunning = true;
+
+  REAL(memcmp) = reinterpret_cast<memcmp_type>(
+      getFuncAddr("memcmp", reinterpret_cast<uintptr_t>(&memcmp)));
+  REAL(strncmp) = reinterpret_cast<strncmp_type>(
+      getFuncAddr("strncmp", reinterpret_cast<uintptr_t>(&strncmp)));
+  REAL(strcmp) = reinterpret_cast<strcmp_type>(
+      getFuncAddr("strcmp", reinterpret_cast<uintptr_t>(&strcmp)));
+  REAL(strncasecmp) = reinterpret_cast<strncasecmp_type>(
+      getFuncAddr("strncasecmp", reinterpret_cast<uintptr_t>(&strncasecmp)));
+  REAL(strcasecmp) = reinterpret_cast<strcasecmp_type>(
+      getFuncAddr("strcasecmp", reinterpret_cast<uintptr_t>(&strcasecmp)));
+  REAL(strstr) = reinterpret_cast<strstr_type>(
+      getFuncAddr("strstr", reinterpret_cast<uintptr_t>(&strstr)));
+  REAL(strcasestr) = reinterpret_cast<strcasestr_type>(
+      getFuncAddr("strcasestr", reinterpret_cast<uintptr_t>(&strcasestr)));
+  REAL(memmem) = reinterpret_cast<memmem_type>(
+      getFuncAddr("memmem", reinterpret_cast<uintptr_t>(&memmem)));
+
+  FuzzerInitIsRunning = false;
+  FuzzerInited = 1;
+}
+
+#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
@@ -784,6 +784,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
@@ -240,6 +240,10 @@
   return TrappingKinds;
 }
 
+bool SanitizerArgs::needsFuzzerInterceptors() const {
+  return needsFuzzer() && !needsAsanRt() && !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

Reply via email to