Skip to content

Commit 9d4f1a9

Browse files
committed
sanmd: refine selection of functions for UAR checking
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. Reviewed By: melver Differential Revision: https://reviews.llvm.org/D142190
1 parent 3cbc72e commit 9d4f1a9

File tree

3 files changed

+46
-2
lines changed

3 files changed

+46
-2
lines changed

compiler-rt/test/metadata/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ if(CAN_TARGET_x86_64)
44
set(METADATA_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
55
set(METADATA_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
66

7+
set(METADATA_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
8+
if (NOT COMPILER_RT_STANDALONE_BUILD)
9+
list(APPEND METADATA_TEST_DEPS asan ubsan)
10+
endif()
11+
712
set(SANITIZER_COMMON_TEST_TARGET_ARCH ${X86_64})
813
get_test_cc_for_arch(x86_64 METADATA_TEST_TARGET_CC METADATA_TEST_TARGET_CFLAGS)
914
configure_lit_site_cfg(
@@ -12,6 +17,6 @@ if(CAN_TARGET_x86_64)
1217

1318
add_lit_testsuite(check-sanmd "Running the SanitizerBinaryMetadata tests"
1419
${CMAKE_CURRENT_BINARY_DIR}
15-
DEPENDS ${SANITIZER_COMMON_LIT_TEST_DEPS})
20+
DEPENDS ${METADATA_TEST_DEPS})
1621
set_target_properties(check-sanmd PROPERTIES FOLDER "Compiler-RT Misc")
1722
endif()

compiler-rt/test/metadata/uar.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clangxx %s -O1 -o %t -fexperimental-sanitize-metadata=covered,uar && %t | FileCheck %s
2+
// RUN: %clangxx %s -O1 -o %t -fexperimental-sanitize-metadata=covered,uar -fsanitize=address,signed-integer-overflow && %t | FileCheck %s
23

34
// CHECK: metadata add version 1
45

@@ -15,6 +16,16 @@ __attribute__((noinline, not_tail_called)) void use(int x) {
1516
// CHECK: empty: features=0 stack_args=0
1617
void empty() {}
1718

19+
// CHECK: simple: features=0 stack_args=0
20+
int simple(int *data, int index) { return data[index + 1]; }
21+
22+
// CHECK: builtins: features=0 stack_args=0
23+
int builtins() {
24+
int x = 0;
25+
__builtin_prefetch(&x);
26+
return x;
27+
}
28+
1829
// CHECK: ellipsis: features=0 stack_args=0
1930
void ellipsis(const char *fmt, ...) {
2031
int x;
@@ -60,6 +71,11 @@ __attribute__((noinline)) int tail_called(int x) { return x; }
6071
// CHECK: with_tail_call: features=2
6172
int with_tail_call(int x) { [[clang::musttail]] return tail_called(x); }
6273

74+
__attribute__((noinline, noreturn)) int noreturn(int x) { __builtin_trap(); }
75+
76+
// CHECK: with_noreturn_tail_call: features=0
77+
int with_noreturn_tail_call(int x) { return noreturn(x); }
78+
6379
// CHECK: local_array: features=0
6480
void local_array(int x) {
6581
int data[10];
@@ -81,13 +97,16 @@ void escaping_alloca(int size, int i) {
8197

8298
#define FUNCTIONS \
8399
FN(empty); \
100+
FN(simple); \
101+
FN(builtins); \
84102
FN(ellipsis); \
85103
FN(non_empty_function); \
86104
FN(no_stack_args); \
87105
FN(stack_args); \
88106
FN(more_stack_args); \
89107
FN(struct_stack_args); \
90108
FN(with_tail_call); \
109+
FN(with_noreturn_tail_call); \
91110
FN(local_array); \
92111
FN(local_alloca); \
93112
FN(escaping_alloca); \

llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,31 @@ void SanitizerBinaryMetadata::runOn(Function &F, MetadataInfoSet &MIS) {
271271
}
272272
}
273273

274+
bool isUARSafeCall(CallInst *CI) {
275+
auto *F = CI->getCalledFunction();
276+
// There are no intrinsic functions that leak arguments.
277+
// If the called function does not return, the current function
278+
// does not return as well, so no possibility of use-after-return.
279+
// Sanitizer function also don't leak or don't return.
280+
// It's safe to both pass pointers to local variables to them
281+
// and to tail-call them.
282+
return F && (F->isIntrinsic() || F->doesNotReturn() ||
283+
F->getName().startswith("__asan_") ||
284+
F->getName().startswith("__hwsan_") ||
285+
F->getName().startswith("__ubsan_") ||
286+
F->getName().startswith("__msan_") ||
287+
F->getName().startswith("__tsan_"));
288+
}
289+
274290
bool hasUseAfterReturnUnsafeUses(Value &V) {
275291
for (User *U : V.users()) {
276292
if (auto *I = dyn_cast<Instruction>(U)) {
277293
if (I->isLifetimeStartOrEnd() || I->isDroppable())
278294
continue;
295+
if (auto *CI = dyn_cast<CallInst>(U)) {
296+
if (isUARSafeCall(CI))
297+
continue;
298+
}
279299
if (isa<LoadInst>(U))
280300
continue;
281301
if (auto *SI = dyn_cast<StoreInst>(U)) {
@@ -303,7 +323,7 @@ bool useAfterReturnUnsafe(Instruction &I) {
303323
// at runtime because there is no call instruction.
304324
// So conservatively mark the caller as requiring checking.
305325
else if (auto *CI = dyn_cast<CallInst>(&I))
306-
return CI->isTailCall();
326+
return CI->isTailCall() && !isUARSafeCall(CI);
307327
return false;
308328
}
309329

0 commit comments

Comments
 (0)