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

Reply via email to