Skip to content

Commit 5addb73

Browse files
committed
sanmd: improve precision of UAR analysis
Only mark functions that have address-taken locals as requiring UAR checking. On a large internal app this reduces number of marked functions from 78441 to 66618. Mostly small, trivial getter/setter-type functions are unmarked, but also some amount of larger number-crunching-type functions are unmarked as well. Reviewed By: melver Differential Revision: https://reviews.llvm.org/D139811
1 parent 6cf7f32 commit 5addb73

File tree

3 files changed

+122
-36
lines changed

3 files changed

+122
-36
lines changed

compiler-rt/test/metadata/covered.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=atomics,uar && %t | FileCheck -check-prefix=CHECK-AU %s
88
// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered,atomics,uar && %t | FileCheck -check-prefix=CHECK-CAU %s
99

10+
__attribute__((noinline, not_tail_called)) void escape(const volatile void *p) {
11+
static const volatile void *sink;
12+
sink = p;
13+
}
14+
1015
// CHECK-NOT: metadata add
1116
// CHECK: main
1217
// CHECK-NOT: metadata del
@@ -28,19 +33,31 @@ void empty() {}
2833
// CHECK-AU: normal: features=3
2934
// CHECK-CAU:normal: features=3
3035
void normal() {
31-
volatile int x;
32-
x = 0;
36+
int x;
37+
escape(&x);
3338
}
3439

35-
// CHECK-C: with_atomic: features=0
36-
// CHECK-A: with_atomic: features=1
37-
// CHECK-U: with_atomic: features=2
38-
// CHECK-CA: with_atomic: features=1
39-
// CHECK-CU: with_atomic: features=2
40-
// CHECK-AU: with_atomic: features=3
41-
// CHECK-CAU: with_atomic: features=3
40+
// CHECK-C: with_atomic: features=0
41+
// CHECK-A: with_atomic: features=1
42+
// CHECK-U-NOT: with_atomic:
43+
// CHECK-CA: with_atomic: features=1
44+
// CHECK-CU: with_atomic: features=0
45+
// CHECK-AU: with_atomic: features=1
46+
// CHECK-CAU: with_atomic: features=1
4247
int with_atomic(int *p) { return __atomic_load_n(p, __ATOMIC_RELAXED); }
4348

49+
// CHECK-C: with_atomic_escape: features=0
50+
// CHECK-A: with_atomic_escape: features=1
51+
// CHECK-U: with_atomic_escape: features=2
52+
// CHECK-CA: with_atomic_escape: features=1
53+
// CHECK-CU: with_atomic_escape: features=2
54+
// CHECK-AU: with_atomic_escape: features=3
55+
// CHECK-CAU: with_atomic_escape: features=3
56+
int with_atomic_escape(int *p) {
57+
escape(&p);
58+
return __atomic_load_n(p, __ATOMIC_RELAXED);
59+
}
60+
4461
// CHECK-C: ellipsis: features=0
4562
// CHECK-A: ellipsis: features=1
4663
// CHECK-U-NOT: ellipsis:
@@ -49,6 +66,7 @@ int with_atomic(int *p) { return __atomic_load_n(p, __ATOMIC_RELAXED); }
4966
// CHECK-AU: ellipsis: features=1
5067
// CHECK-CAU: ellipsis: features=1
5168
void ellipsis(int *p, ...) {
69+
escape(&p);
5270
volatile int x;
5371
x = 0;
5472
}
@@ -61,13 +79,15 @@ void ellipsis(int *p, ...) {
6179
// CHECK-AU: ellipsis_with_atomic: features=1
6280
// CHECK-CAU: ellipsis_with_atomic: features=1
6381
int ellipsis_with_atomic(int *p, ...) {
82+
escape(&p);
6483
return __atomic_load_n(p, __ATOMIC_RELAXED);
6584
}
6685

6786
#define FUNCTIONS \
6887
FN(empty); \
6988
FN(normal); \
7089
FN(with_atomic); \
90+
FN(with_atomic_escape); \
7191
FN(ellipsis); \
7292
FN(ellipsis_with_atomic); \
7393
/**/

compiler-rt/test/metadata/uar.cpp

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,82 @@
1-
// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered,uar && %t | FileCheck %s
1+
// RUN: %clangxx %s -O1 -o %t -fexperimental-sanitize-metadata=covered,uar && %t | FileCheck %s
22

33
// CHECK: metadata add version 1
44

5+
__attribute__((noinline, not_tail_called)) void escape(const volatile void *p) {
6+
static const volatile void *sink;
7+
sink = p;
8+
}
9+
10+
__attribute__((noinline, not_tail_called)) void use(int x) {
11+
static volatile int sink;
12+
sink += x;
13+
}
14+
515
// CHECK: empty: features=0 stack_args=0
616
void empty() {}
717

818
// CHECK: ellipsis: features=0 stack_args=0
919
void ellipsis(const char *fmt, ...) {
10-
volatile int x;
11-
x = 1;
20+
int x;
21+
escape(&x);
1222
}
1323

1424
// CHECK: non_empty_function: features=2 stack_args=0
1525
void non_empty_function() {
16-
// Completely empty functions don't get uar metadata.
17-
volatile int x;
18-
x = 1;
26+
int x;
27+
escape(&x);
1928
}
2029

2130
// CHECK: no_stack_args: features=2 stack_args=0
2231
void no_stack_args(long a0, long a1, long a2, long a3, long a4, long a5) {
23-
volatile int x;
24-
x = 1;
32+
int x;
33+
escape(&x);
2534
}
2635

2736
// CHECK: stack_args: features=2 stack_args=16
2837
void stack_args(long a0, long a1, long a2, long a3, long a4, long a5, long a6) {
29-
volatile int x;
30-
x = 1;
38+
int x;
39+
escape(&x);
3140
}
3241

3342
// CHECK: more_stack_args: features=2 stack_args=32
3443
void more_stack_args(long a0, long a1, long a2, long a3, long a4, long a5,
3544
long a6, long a7, long a8) {
36-
volatile int x;
37-
x = 1;
45+
int x;
46+
escape(&x);
3847
}
3948

4049
// CHECK: struct_stack_args: features=2 stack_args=144
4150
struct large {
4251
char x[131];
4352
};
4453
void struct_stack_args(large a) {
45-
volatile int x;
46-
x = 1;
54+
int x;
55+
escape(&x);
56+
}
57+
58+
__attribute__((noinline)) int tail_called(int x) { return x; }
59+
60+
// CHECK: with_tail_call: features=2
61+
int with_tail_call(int x) { [[clang::musttail]] return tail_called(x); }
62+
63+
// CHECK: local_array: features=0
64+
void local_array(int x) {
65+
int data[10];
66+
use(data[x]);
67+
}
68+
69+
// CHECK: local_alloca: features=0
70+
void local_alloca(int size, int i, int j) {
71+
volatile int *p = static_cast<int *>(__builtin_alloca(size));
72+
p[i] = 0;
73+
use(p[j]);
74+
}
75+
76+
// CHECK: escaping_alloca: features=2
77+
void escaping_alloca(int size, int i) {
78+
volatile int *p = static_cast<int *>(__builtin_alloca(size));
79+
escape(&p[i]);
4780
}
4881

4982
#define FUNCTIONS \
@@ -54,6 +87,10 @@ void struct_stack_args(large a) {
5487
FN(stack_args); \
5588
FN(more_stack_args); \
5689
FN(struct_stack_args); \
90+
FN(with_tail_call); \
91+
FN(local_array); \
92+
FN(local_alloca); \
93+
FN(escaping_alloca); \
5794
/**/
5895

5996
#include "common.h"

llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,10 @@ void SanitizerBinaryMetadata::runOn(Function &F, MetadataInfoSet &MIS) {
250250

251251
if (F.isVarArg())
252252
FeatureMask &= ~kSanitizerBinaryMetadataUAR;
253-
if (FeatureMask & kSanitizerBinaryMetadataUAR)
253+
if (FeatureMask & kSanitizerBinaryMetadataUAR) {
254+
RequiresCovered = true;
254255
NumMetadataUAR++;
256+
}
255257

256258
// Covered metadata is always emitted if explicitly requested, otherwise only
257259
// if some other metadata requires it to unambiguously interpret it for
@@ -269,23 +271,50 @@ void SanitizerBinaryMetadata::runOn(Function &F, MetadataInfoSet &MIS) {
269271
}
270272
}
271273

274+
bool hasUseAfterReturnUnsafeUses(Value &V) {
275+
for (User *U : V.users()) {
276+
if (auto *I = dyn_cast<Instruction>(U)) {
277+
if (I->isLifetimeStartOrEnd() || I->isDroppable())
278+
continue;
279+
if (isa<LoadInst>(U))
280+
continue;
281+
if (auto *SI = dyn_cast<StoreInst>(U)) {
282+
// If storing TO the alloca, then the address isn't taken.
283+
if (SI->getOperand(1) == &V)
284+
continue;
285+
}
286+
if (auto *GEPI = dyn_cast<GetElementPtrInst>(U)) {
287+
if (!hasUseAfterReturnUnsafeUses(*GEPI))
288+
continue;
289+
} else if (auto *BCI = dyn_cast<BitCastInst>(U)) {
290+
if (!hasUseAfterReturnUnsafeUses(*BCI))
291+
continue;
292+
}
293+
}
294+
return true;
295+
}
296+
return false;
297+
}
298+
299+
bool useAfterReturnUnsafe(Instruction &I) {
300+
if (isa<AllocaInst>(I))
301+
return hasUseAfterReturnUnsafeUses(I);
302+
// Tail-called functions are not necessary intercepted
303+
// at runtime because there is no call instruction.
304+
// So conservatively mark the caller as requiring checking.
305+
else if (auto *CI = dyn_cast<CallInst>(&I))
306+
return CI->isTailCall();
307+
return false;
308+
}
309+
272310
bool SanitizerBinaryMetadata::runOn(Instruction &I, MetadataInfoSet &MIS,
273311
MDBuilder &MDB, uint32_t &FeatureMask) {
274312
SmallVector<const MetadataInfo *, 1> InstMetadata;
275313
bool RequiresCovered = false;
276314

277-
if (Options.UAR) {
278-
for (unsigned i = 0; i < I.getNumOperands(); ++i) {
279-
const Value *V = I.getOperand(i);
280-
// TODO(dvyukov): check if V is an address of alloca/function arg.
281-
// See isSafeAndProfitableToSinkLoad for addr-taken allocas
282-
// and DeadArgumentEliminationPass::removeDeadStuffFromFunction
283-
// for iteration over function args.
284-
if (V) {
285-
RequiresCovered = true;
286-
FeatureMask |= kSanitizerBinaryMetadataUAR;
287-
}
288-
}
315+
if (Options.UAR && !(FeatureMask & kSanitizerBinaryMetadataUAR)) {
316+
if (useAfterReturnUnsafe(I))
317+
FeatureMask |= kSanitizerBinaryMetadataUAR;
289318
}
290319

291320
if (Options.Atomics && I.mayReadOrWriteMemory()) {

0 commit comments

Comments
 (0)