| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| @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") |