[compiler-rt] r312029 - Minimal runtime for UBSan.

Evgeniy Stepanov via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 29 13:03:51 PDT 2017


Author: eugenis
Date: Tue Aug 29 13:03:51 2017
New Revision: 312029

URL: http://llvm.org/viewvc/llvm-project?rev=312029&view=rev
Log:
Minimal runtime for UBSan.

Summary:
An implementation of ubsan runtime library suitable for use in production.

Minimal attack surface.
* No stack traces.
* Definitely no C++ demangling.
* No UBSAN_OPTIONS=log_file=/path (very suid-unfriendly). And no UBSAN_OPTIONS in general.
* as simple as possible

Minimal CPU and RAM overhead.
* Source locations unnecessary in the presence of (split) debug info.
* Values and types (as in A+B overflows T) can be reconstructed from register/stack dumps, once you know what type of error you are looking at.
* above two items save 3% binary size.

When UBSan is used with -ftrap-function=abort, sometimes it is hard to reason about failures. This library replaces abort with a slightly more informative message without much extra overhead. Since ubsan interface in not stable, this code must reside in compiler-rt.

Reviewers: pcc, kcc

Subscribers: srhines, mgorny, aprantl, krytarowski, llvm-commits

Differential Revision: https://reviews.llvm.org/D36810

Added:
    compiler-rt/trunk/lib/ubsan_minimal/
    compiler-rt/trunk/lib/ubsan_minimal/CMakeLists.txt
    compiler-rt/trunk/lib/ubsan_minimal/ubsan.syms.extra
    compiler-rt/trunk/lib/ubsan_minimal/ubsan_minimal_handlers.cc
    compiler-rt/trunk/test/ubsan_minimal/
    compiler-rt/trunk/test/ubsan_minimal/CMakeLists.txt
    compiler-rt/trunk/test/ubsan_minimal/TestCases/
    compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup-limit.cpp
    compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup.cpp
    compiler-rt/trunk/test/ubsan_minimal/TestCases/uadd-overflow.cpp
    compiler-rt/trunk/test/ubsan_minimal/lit.common.cfg
    compiler-rt/trunk/test/ubsan_minimal/lit.site.cfg.in
Modified:
    compiler-rt/trunk/cmake/config-ix.cmake

Modified: compiler-rt/trunk/cmake/config-ix.cmake
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/cmake/config-ix.cmake?rev=312029&r1=312028&r2=312029&view=diff
==============================================================================
--- compiler-rt/trunk/cmake/config-ix.cmake (original)
+++ compiler-rt/trunk/cmake/config-ix.cmake Tue Aug 29 13:03:51 2017
@@ -470,7 +470,7 @@ else()
   set(OS_NAME "${CMAKE_SYSTEM_NAME}")
 endif()
 
-set(ALL_SANITIZERS asan;dfsan;msan;tsan;safestack;cfi;esan;scudo)
+set(ALL_SANITIZERS asan;dfsan;msan;tsan;safestack;cfi;esan;scudo;ubsan_minimal)
 set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING
     "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
 list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
@@ -545,6 +545,13 @@ else()
   set(COMPILER_RT_HAS_UBSAN FALSE)
 endif()
 
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND
+    OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD|Android")
+  set(COMPILER_RT_HAS_UBSAN_MINIMAL TRUE)
+else()
+  set(COMPILER_RT_HAS_UBSAN_MINIMAL FALSE)
+endif()
+
 if (COMPILER_RT_HAS_SANITIZER_COMMON AND SAFESTACK_SUPPORTED_ARCH AND
     OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD")
   set(COMPILER_RT_HAS_SAFESTACK TRUE)

Added: compiler-rt/trunk/lib/ubsan_minimal/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan_minimal/CMakeLists.txt?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan_minimal/CMakeLists.txt (added)
+++ compiler-rt/trunk/lib/ubsan_minimal/CMakeLists.txt Tue Aug 29 13:03:51 2017
@@ -0,0 +1,50 @@
+# Build for the undefined behavior sanitizer runtime support library.
+
+set(UBSAN_MINIMAL_SOURCES
+  ubsan_minimal_handlers.cc
+  )
+
+include_directories(..)
+
+set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+append_rtti_flag(OFF UBSAN_CFLAGS)
+
+set(UBSAN_STANDALONE_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+append_rtti_flag(OFF UBSAN_STANDALONE_CFLAGS)
+
+add_compiler_rt_component(ubsan-minimal)
+
+# Common parts of UBSan runtime.
+add_compiler_rt_object_libraries(RTUbsan_minimal
+  ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH}
+  SOURCES ${UBSAN_MINIMAL_SOURCES} CFLAGS ${UBSAN_CFLAGS})
+
+
+if(COMPILER_RT_HAS_UBSAN)
+  # Initializer of standalone UBSan runtime.
+
+  # Standalone UBSan runtimes.
+  add_compiler_rt_runtime(clang_rt.ubsan_minimal
+    STATIC
+    ARCHS ${UBSAN_SUPPORTED_ARCH}
+    OBJECT_LIBS RTUbsan_minimal
+    CFLAGS ${UBSAN_CFLAGS}
+    PARENT_TARGET ubsan-minimal)
+
+  add_compiler_rt_runtime(clang_rt.ubsan_minimal
+    SHARED
+    ARCHS ${UBSAN_SUPPORTED_ARCH}
+    OBJECT_LIBS RTUbsan_minimal
+    CFLAGS ${UBSAN_CFLAGS}
+    LINK_LIBS ${UBSAN_DYNAMIC_LIBS}
+    PARENT_TARGET ubsan-minimal)
+
+  if (UNIX)
+    set(ARCHS_FOR_SYMBOLS ${UBSAN_SUPPORTED_ARCH})
+    list(REMOVE_ITEM ARCHS_FOR_SYMBOLS i386 i686)
+    add_sanitizer_rt_symbols(clang_rt.ubsan_minimal
+      ARCHS ${ARCHS_FOR_SYMBOLS}
+      PARENT_TARGET ubsan-minimal
+      EXTRA ubsan.syms.extra)
+  endif()
+endif()

Added: compiler-rt/trunk/lib/ubsan_minimal/ubsan.syms.extra
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan_minimal/ubsan.syms.extra?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan_minimal/ubsan.syms.extra (added)
+++ compiler-rt/trunk/lib/ubsan_minimal/ubsan.syms.extra Tue Aug 29 13:03:51 2017
@@ -0,0 +1 @@
+__ubsan_*

Added: compiler-rt/trunk/lib/ubsan_minimal/ubsan_minimal_handlers.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan_minimal/ubsan_minimal_handlers.cc?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/lib/ubsan_minimal/ubsan_minimal_handlers.cc (added)
+++ compiler-rt/trunk/lib/ubsan_minimal/ubsan_minimal_handlers.cc Tue Aug 29 13:03:51 2017
@@ -0,0 +1,91 @@
+#include <atomic>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void message(const char *msg) {
+  write(2, msg, strlen(msg));
+}
+
+static const int kMaxCallerPcs = 20;
+static std::atomic<void *> caller_pcs[kMaxCallerPcs];
+// Number of elements in caller_pcs. A special value of kMaxCallerPcs + 1 means
+// that "too many errors" has already been reported.
+static std::atomic<int> caller_pcs_sz;
+
+__attribute__((noinline))
+static bool report_this_error(void *caller) {
+  if (caller == nullptr) return false;
+  while (true) {
+    int sz = caller_pcs_sz.load(std::memory_order_relaxed);
+    if (sz > kMaxCallerPcs) return false; // early exit
+    // when sz==kMaxCallerPcs print "too many errors", but only when cmpxchg
+    // succeeds in order to not print it multiple times.
+    if (sz > 0 && sz < kMaxCallerPcs) {
+      void *p;
+      for (int i = 0; i < sz; ++i) {
+        p = caller_pcs[i].load(std::memory_order_relaxed);
+        if (p == nullptr) break; // Concurrent update.
+        if (p == caller) return false;
+      }
+      if (p == nullptr) continue; // FIXME: yield?
+    }
+
+    if (!caller_pcs_sz.compare_exchange_strong(sz, sz + 1))
+      continue; // Concurrent update! Try again from the start.
+
+    if (sz == kMaxCallerPcs) {
+      message("ubsan: too many errors\n");
+      return false;
+    }
+    caller_pcs[sz].store(caller, std::memory_order_relaxed);
+    return true;
+  }
+}
+
+#if defined(__ANDROID__)
+extern "C" void android_set_abort_message(const char *msg);
+static void abort_with_message(const char *msg) {
+#if __ANDROID_API__ >= 21
+  android_set_abort_message(msg);
+#endif
+  abort();
+}
+#else
+static void abort_with_message(const char *) { abort(); }
+#endif
+
+#define INTERFACE extern "C" __attribute__((visibility("default")))
+
+// FIXME: add caller pc to the error message (possibly as "ubsan: error-type
+// @1234ABCD").
+#define HANDLER(name, msg)                                       \
+  INTERFACE void __ubsan_handle_##name##_minimal() {             \
+    if (!report_this_error(__builtin_return_address(0))) return; \
+    message("ubsan: " msg "\n");                                 \
+  }                                                              \
+                                                                 \
+  INTERFACE void __ubsan_handle_##name##_minimal_abort() {       \
+    message("ubsan: " msg "\n");                                 \
+    abort_with_message("ubsan: " msg);                           \
+  }
+
+HANDLER(type_mismatch, "type-mismatch")
+HANDLER(add_overflow, "add-overflow")
+HANDLER(sub_overflow, "sub-overflow")
+HANDLER(mul_overflow, "mul-overflow")
+HANDLER(negate_overflow, "negate-overflow")
+HANDLER(divrem_overflow, "divrem-overflow")
+HANDLER(shift_out_of_bounds, "shift-out-of-bounds")
+HANDLER(out_of_bounds, "out-of-bounds")
+HANDLER(builtin_unreachable, "builtin-unreachable")
+HANDLER(missing_return, "missing-return")
+HANDLER(vla_bound_not_positive, "vla-bound-not-positive")
+HANDLER(float_cast_overflow, "float-cast-overflow")
+HANDLER(load_invalid_value, "load-invalid-value")
+HANDLER(invalid_builtin, "invalid-builtin")
+HANDLER(function_type_mismatch, "function-type-mismatch")
+HANDLER(nonnull_arg, "nonnull-arg")
+HANDLER(nullability_arg, "nullability-arg")
+HANDLER(pointer_overflow, "pointer-overflow")
+HANDLER(cfi_check_fail, "cfi-check-fail")

Added: compiler-rt/trunk/test/ubsan_minimal/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan_minimal/CMakeLists.txt?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan_minimal/CMakeLists.txt (added)
+++ compiler-rt/trunk/test/ubsan_minimal/CMakeLists.txt Tue Aug 29 13:03:51 2017
@@ -0,0 +1,23 @@
+set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+set(UBSAN_TEST_ARCH ${UBSAN_SUPPORTED_ARCH})
+
+set(UBSAN_TESTSUITES)
+set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
+if(NOT COMPILER_RT_STANDALONE_BUILD)
+  list(APPEND UBSAN_TEST_DEPS ubsan-minimal)
+endif()
+
+foreach(arch ${UBSAN_TEST_ARCH})
+  get_test_cc_for_arch(${arch} UBSAN_TEST_TARGET_CC UBSAN_TEST_TARGET_CFLAGS)
+  set(CONFIG_NAME ${arch})
+  configure_lit_site_cfg(
+    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
+  list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+endforeach()
+
+add_lit_testsuite(check-ubsan-minimal "Running UndefinedBehaviorSanitizerMinimal tests"
+  ${UBSAN_TESTSUITES}
+  DEPENDS ${UBSAN_TEST_DEPS})
+set_target_properties(check-ubsan-minimal PROPERTIES FOLDER "Compiler-RT Misc")

Added: compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup-limit.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup-limit.cpp?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup-limit.cpp (added)
+++ compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup-limit.cpp Tue Aug 29 13:03:51 2017
@@ -0,0 +1,41 @@
+// RUN: %clangxx -fsanitize=signed-integer-overflow -fsanitize-recover=all %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+#define OVERFLOW  \
+  x = 0x7FFFFFFE; \
+  x += __LINE__
+
+int main() {
+  int32_t x;
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+  OVERFLOW;  // CHECK: add-overflow
+
+  // CHECK-NOT: add-overflow
+  OVERFLOW;  // CHECK: too many errors
+  // CHECK-NOT: add-overflow
+  OVERFLOW;
+  OVERFLOW;
+  OVERFLOW;
+}

Added: compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup.cpp?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup.cpp (added)
+++ compiler-rt/trunk/test/ubsan_minimal/TestCases/recover-dedup.cpp Tue Aug 29 13:03:51 2017
@@ -0,0 +1,25 @@
+// RUN: %clangxx -fsanitize=signed-integer-overflow -fsanitize-recover=all %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+__attribute__((noinline))
+int f(int x, int y) {
+  // CHECK: mul-overflow
+  return x * y;
+}
+
+__attribute__((noinline))
+int g(int x, int y) {
+  // CHECK: mul-overflow
+  return x * (y + 1);
+}
+
+int main() {
+  int x = 2;
+  for (int i = 0; i < 10; ++i)
+    x = f(x, x);
+  x = 2;
+  for (int i = 0; i < 10; ++i)
+    x = g(x, x);
+  // CHECK-NOT: mul-overflow
+}

Added: compiler-rt/trunk/test/ubsan_minimal/TestCases/uadd-overflow.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan_minimal/TestCases/uadd-overflow.cpp?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan_minimal/TestCases/uadd-overflow.cpp (added)
+++ compiler-rt/trunk/test/ubsan_minimal/TestCases/uadd-overflow.cpp Tue Aug 29 13:03:51 2017
@@ -0,0 +1,10 @@
+// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=all %s -o %t && not --crash %run %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+int main() {
+  uint32_t k = 0x87654321;
+  k += 0xedcba987;
+  // CHECK: add-overflow
+}

Added: compiler-rt/trunk/test/ubsan_minimal/lit.common.cfg
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan_minimal/lit.common.cfg?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan_minimal/lit.common.cfg (added)
+++ compiler-rt/trunk/test/ubsan_minimal/lit.common.cfg Tue Aug 29 13:03:51 2017
@@ -0,0 +1,35 @@
+# -*- Python -*-
+
+import os
+
+def get_required_attr(config, attr_name):
+  attr_value = getattr(config, attr_name, None)
+  if attr_value == None:
+    lit_config.fatal(
+      "No attribute %r in test configuration! You may need to run "
+      "tests from your build directory or add this attribute "
+      "to lit.site.cfg " % attr_name)
+  return attr_value
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+def build_invocation(compile_flags):
+  return " " + " ".join([config.clang] + compile_flags) + " "
+
+target_cflags = [get_required_attr(config, "target_cflags")]
+clang_ubsan_cflags = ["-fsanitize-minimal-runtime"] + target_cflags
+clang_ubsan_cxxflags = config.cxx_mode_flags + clang_ubsan_cflags
+
+# Define %clang and %clangxx substitutions to use in test RUN lines.
+config.substitutions.append( ("%clang ", build_invocation(clang_ubsan_cflags)) )
+config.substitutions.append( ("%clangxx ", build_invocation(clang_ubsan_cxxflags)) )
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# Check that the host supports UndefinedBehaviorSanitizerMinimal tests
+if config.host_os not in ['Darwin', 'Linux', 'FreeBSD', 'NetBSD']: # TODO: Windows
+  config.unsupported = True
+
+config.available_features.add('arch=' + config.target_arch)

Added: compiler-rt/trunk/test/ubsan_minimal/lit.site.cfg.in
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan_minimal/lit.site.cfg.in?rev=312029&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan_minimal/lit.site.cfg.in (added)
+++ compiler-rt/trunk/test/ubsan_minimal/lit.site.cfg.in Tue Aug 29 13:03:51 2017
@@ -0,0 +1,11 @@
+ at LIT_SITE_CFG_IN_HEADER@
+
+# Tool-specific config options.
+config.target_cflags = "@UBSAN_TEST_TARGET_CFLAGS@"
+config.target_arch = "@UBSAN_TEST_TARGET_ARCH@"
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/lit.common.cfg")




More information about the llvm-commits mailing list