diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt --- a/compiler-rt/test/CMakeLists.txt +++ b/compiler-rt/test/CMakeLists.txt @@ -86,6 +86,9 @@ compiler_rt_test_runtime(${sanitizer}) endif() endforeach() + + # These tests don't need an additional runtime but use asan runtime. + add_subdirectory(metadata) endif() if(COMPILER_RT_BUILD_PROFILE AND COMPILER_RT_HAS_PROFILE) compiler_rt_test_runtime(profile) @@ -105,8 +108,6 @@ # ShadowCallStack does not yet provide a runtime with compiler-rt, the tests # include their own minimal runtime add_subdirectory(shadowcallstack) - # These tests are self-contained and don't need an additional runtime. - add_subdirectory(metadata) endif() if(COMPILER_RT_STANDALONE_BUILD) diff --git a/compiler-rt/test/metadata/CMakeLists.txt b/compiler-rt/test/metadata/CMakeLists.txt --- a/compiler-rt/test/metadata/CMakeLists.txt +++ b/compiler-rt/test/metadata/CMakeLists.txt @@ -4,6 +4,11 @@ set(METADATA_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(METADATA_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + set(METADATA_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) + if (NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND METADATA_TEST_DEPS asan ubsan) + endif() + set(SANITIZER_COMMON_TEST_TARGET_ARCH ${X86_64}) get_test_cc_for_arch(x86_64 METADATA_TEST_TARGET_CC METADATA_TEST_TARGET_CFLAGS) configure_lit_site_cfg( @@ -12,6 +17,6 @@ add_lit_testsuite(check-sanmd "Running the SanitizerBinaryMetadata tests" ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${SANITIZER_COMMON_LIT_TEST_DEPS}) + DEPENDS ${METADATA_TEST_DEPS}) set_target_properties(check-sanmd PROPERTIES FOLDER "Compiler-RT Misc") endif() diff --git a/compiler-rt/test/metadata/uar.cpp b/compiler-rt/test/metadata/uar.cpp --- a/compiler-rt/test/metadata/uar.cpp +++ b/compiler-rt/test/metadata/uar.cpp @@ -1,4 +1,5 @@ // RUN: %clangxx %s -O1 -o %t -fexperimental-sanitize-metadata=covered,uar && %t | FileCheck %s +// RUN: %clangxx %s -O1 -o %t -fexperimental-sanitize-metadata=covered,uar -fsanitize=address,signed-integer-overflow && %t | FileCheck %s // CHECK: metadata add version 1 @@ -15,6 +16,16 @@ // CHECK: empty: features=0 stack_args=0 void empty() {} +// CHECK: simple: features=0 stack_args=0 +int simple(int *data, int index) { return data[index + 1]; } + +// CHECK: builtins: features=0 stack_args=0 +int builtins() { + int x = 0; + __builtin_prefetch(&x); + return x; +} + // CHECK: ellipsis: features=0 stack_args=0 void ellipsis(const char *fmt, ...) { int x; @@ -60,6 +71,11 @@ // CHECK: with_tail_call: features=2 int with_tail_call(int x) { [[clang::musttail]] return tail_called(x); } +__attribute__((noinline, noreturn)) int noreturn(int x) { __builtin_trap(); } + +// CHECK: with_noreturn_tail_call: features=0 +int with_noreturn_tail_call(int x) { return noreturn(x); } + // CHECK: local_array: features=0 void local_array(int x) { int data[10]; @@ -81,6 +97,8 @@ #define FUNCTIONS \ FN(empty); \ + FN(simple); \ + FN(builtins); \ FN(ellipsis); \ FN(non_empty_function); \ FN(no_stack_args); \ @@ -88,6 +106,7 @@ FN(more_stack_args); \ FN(struct_stack_args); \ FN(with_tail_call); \ + FN(with_noreturn_tail_call); \ FN(local_array); \ FN(local_alloca); \ FN(escaping_alloca); \ diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp --- a/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp @@ -271,11 +271,31 @@ } } +bool isUARSafeCall(CallInst *CI) { + auto *F = CI->getCalledFunction(); + // There are no intrinsic functions that leak arguments. + // If the called function does not return, the current function + // does not return as well, so no possibility of use-after-return. + // Sanitizer function also don't leak or don't return. + // It's safe to both pass pointers to local variables to them + // and to tail-call them. + return F && (F->isIntrinsic() || F->doesNotReturn() || + F->getName().startswith("__asan_") || + F->getName().startswith("__hwsan_") || + F->getName().startswith("__ubsan_") || + F->getName().startswith("__msan_") || + F->getName().startswith("__tsan_")); +} + bool hasUseAfterReturnUnsafeUses(Value &V) { for (User *U : V.users()) { if (auto *I = dyn_cast(U)) { if (I->isLifetimeStartOrEnd() || I->isDroppable()) continue; + if (auto *CI = dyn_cast(U)) { + if (isUARSafeCall(CI)) + continue; + } if (isa(U)) continue; if (auto *SI = dyn_cast(U)) { @@ -303,7 +323,7 @@ // at runtime because there is no call instruction. // So conservatively mark the caller as requiring checking. else if (auto *CI = dyn_cast(&I)) - return CI->isTailCall(); + return CI->isTailCall() && !isUARSafeCall(CI); return false; }