Skip to content

Commit 7a957bd

Browse files
authored
[TySan] Add option to outline instrumentation (llvm#120582)
Added a command line option to use function calls rather than inline checks for TySan instrumentation.
1 parent 4200419 commit 7a957bd

File tree

9 files changed

+1136
-34
lines changed

9 files changed

+1136
-34
lines changed

compiler-rt/lib/tysan/tysan.cpp

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "tysan/tysan.h"
2424

25+
#include <stdint.h>
2526
#include <string.h>
2627

2728
using namespace __sanitizer;
@@ -254,10 +255,68 @@ static void reportError(void *Addr, int Size, tysan_type_descriptor *TD,
254255
}
255256
}
256257

258+
ALWAYS_INLINE
259+
static void SetShadowType(tysan_type_descriptor *td,
260+
tysan_type_descriptor **shadowData,
261+
uint64_t AccessSize) {
262+
*shadowData = td;
263+
uint64_t shadowDataInt = (uint64_t)shadowData;
264+
265+
for (uint64_t i = 1; i < AccessSize; ++i) {
266+
int64_t dataOffset = i << PtrShift();
267+
int64_t *badShadowData = (int64_t *)(shadowDataInt + dataOffset);
268+
int64_t badTD = int64_t(i) * -1;
269+
*badShadowData = badTD;
270+
}
271+
}
272+
273+
ALWAYS_INLINE
274+
static bool GetNotAllBadTD(uint64_t ShadowDataInt, uint64_t AccessSize) {
275+
bool notAllBadTD = false;
276+
for (uint64_t i = 1; i < AccessSize; ++i) {
277+
int64_t **unkShadowData = (int64_t **)(ShadowDataInt + (i << PtrShift()));
278+
int64_t *ILdTD = *unkShadowData;
279+
notAllBadTD = notAllBadTD || (ILdTD != nullptr);
280+
}
281+
return notAllBadTD;
282+
}
283+
284+
ALWAYS_INLINE
285+
static bool GetNotAllUnkTD(uint64_t ShadowDataInt, uint64_t AccessSize) {
286+
bool notAllBadTD = false;
287+
for (uint64_t i = 1; i < AccessSize; ++i) {
288+
int64_t *badShadowData = (int64_t *)(ShadowDataInt + (i << PtrShift()));
289+
int64_t ILdTD = *badShadowData;
290+
notAllBadTD = notAllBadTD || (ILdTD >= 0);
291+
}
292+
return notAllBadTD;
293+
}
294+
257295
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
258-
__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
259-
GET_CALLER_PC_BP_SP;
296+
__tysan_instrument_mem_inst(char *dest, char *src, uint64_t size,
297+
bool needsMemMove) {
298+
tysan_type_descriptor **destShadowDataPtr = shadow_for(dest);
299+
300+
if (!src) {
301+
internal_memset((char *)destShadowDataPtr, 0, size << PtrShift());
302+
return;
303+
}
304+
305+
uint64_t srcInt = (uint64_t)src;
306+
uint64_t srcShadowInt = ((srcInt & AppMask()) << PtrShift()) + ShadowAddr();
307+
uint64_t *srcShadow = (uint64_t *)srcShadowInt;
260308

309+
if (needsMemMove) {
310+
internal_memmove((char *)destShadowDataPtr, srcShadow, size << PtrShift());
311+
} else {
312+
internal_memcpy((char *)destShadowDataPtr, srcShadow, size << PtrShift());
313+
}
314+
}
315+
316+
ALWAYS_INLINE
317+
static void __tysan_check_internal(void *addr, int size,
318+
tysan_type_descriptor *td, int flags,
319+
uptr pc, uptr bp, uptr sp) {
261320
bool IsRead = flags & 1;
262321
bool IsWrite = flags & 2;
263322
const char *AccessStr;
@@ -300,6 +359,64 @@ __tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
300359
}
301360
}
302361

362+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
363+
__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
364+
GET_CALLER_PC_BP_SP;
365+
__tysan_check_internal(addr, size, td, flags, pc, bp, sp);
366+
}
367+
368+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
369+
__tysan_instrument_with_shadow_update(void *ptr, tysan_type_descriptor *td,
370+
bool sanitizeFunction,
371+
uint64_t accessSize, int flags) {
372+
tysan_type_descriptor **shadowData = shadow_for(ptr);
373+
tysan_type_descriptor *loadedTD = *shadowData;
374+
bool shadowIsNull = loadedTD == nullptr;
375+
376+
// TODO, sanitizeFunction is known at compile time, so maybe this is split
377+
// into two different functions
378+
if (sanitizeFunction) {
379+
380+
if (td != loadedTD) {
381+
382+
// We now know that the types did not match (we're on the slow path). If
383+
// the type is unknown, then set it.
384+
if (shadowIsNull) {
385+
// We're about to set the type. Make sure that all bytes in the value
386+
// are also of unknown type.
387+
bool isAllUnknownTD = GetNotAllUnkTD((uint64_t)shadowData, accessSize);
388+
if (isAllUnknownTD) {
389+
GET_CALLER_PC_BP_SP;
390+
__tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
391+
}
392+
SetShadowType(td, shadowData, accessSize);
393+
} else {
394+
GET_CALLER_PC_BP_SP;
395+
__tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
396+
}
397+
} else {
398+
// We appear to have the right type. Make sure that all other bytes in
399+
// the type are still marked as interior bytes. If not, call the runtime.
400+
bool isNotAllBadTD = GetNotAllBadTD((uint64_t)shadowData, accessSize);
401+
if (isNotAllBadTD) {
402+
GET_CALLER_PC_BP_SP;
403+
__tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
404+
}
405+
}
406+
} else if (shadowIsNull) {
407+
SetShadowType(td, shadowData, accessSize);
408+
}
409+
}
410+
411+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
412+
__tysan_set_shadow_type(void *ptr, tysan_type_descriptor *td,
413+
uint64_t accessSize) {
414+
// In the mode where writes always set the type, for a write (which does
415+
// not also read), we just set the type.
416+
tysan_type_descriptor **shadow = shadow_for(ptr);
417+
SetShadowType(td, shadow, accessSize);
418+
}
419+
303420
Flags __tysan::flags_data;
304421

305422
SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;

compiler-rt/lib/tysan/tysan_platform.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,28 @@ struct Mapping {
2121
static const uptr kShadowAddr = 0x010000000000ull;
2222
static const uptr kAppAddr = 0x550000000000ull;
2323
static const uptr kAppMemMsk = ~0x780000000000ull;
24+
static const uptr kPtrShift = 3;
2425
};
2526
#elif defined(__aarch64__)
2627
struct Mapping39 {
2728
static const uptr kShadowAddr = 0x0800000000ull;
2829
static const uptr kAppAddr = 0x5500000000ull;
2930
static const uptr kAppMemMsk = ~0x7800000000ull;
31+
static const uptr kPtrShift = 3;
3032
};
3133

3234
struct Mapping42 {
3335
static const uptr kShadowAddr = 0x10000000000ull;
3436
static const uptr kAppAddr = 0x2aa00000000ull;
3537
static const uptr kAppMemMsk = ~0x3c000000000ull;
38+
static const uptr kPtrShift = 3;
3639
};
3740

3841
struct Mapping48 {
3942
static const uptr kShadowAddr = 0x0002000000000ull;
4043
static const uptr kAppAddr = 0x0aaaa00000000ull;
4144
static const uptr kAppMemMsk = ~0x0fff800000000ull;
45+
static const uptr kPtrShift = 3;
4246
};
4347
#define TYSAN_RUNTIME_VMA 1
4448
#else
@@ -49,7 +53,12 @@ struct Mapping48 {
4953
extern int vmaSize;
5054
#endif
5155

52-
enum MappingType { MAPPING_SHADOW_ADDR, MAPPING_APP_ADDR, MAPPING_APP_MASK };
56+
enum MappingType {
57+
MAPPING_SHADOW_ADDR,
58+
MAPPING_APP_ADDR,
59+
MAPPING_APP_MASK,
60+
MAPPING_PTR_SHIFT
61+
};
5362

5463
template <typename Mapping, int Type> uptr MappingImpl(void) {
5564
switch (Type) {
@@ -59,6 +68,8 @@ template <typename Mapping, int Type> uptr MappingImpl(void) {
5968
return Mapping::kAppAddr;
6069
case MAPPING_APP_MASK:
6170
return Mapping::kAppMemMsk;
71+
case MAPPING_PTR_SHIFT:
72+
return Mapping::kPtrShift;
6273
}
6374
}
6475

@@ -88,6 +99,9 @@ uptr AppAddr() { return MappingArchImpl<MAPPING_APP_ADDR>(); }
8899
ALWAYS_INLINE
89100
uptr AppMask() { return MappingArchImpl<MAPPING_APP_MASK>(); }
90101

102+
ALWAYS_INLINE
103+
uptr PtrShift() { return MappingArchImpl<MAPPING_PTR_SHIFT>(); }
104+
91105
} // namespace __tysan
92106

93107
#endif

compiler-rt/test/tysan/basic.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
// RUN: %clang_tysan -O0 %s -o %t && %run %t 10 >%t.out.0 2>&1
1+
// RUN: %clang_tysan -O0 -mllvm -tysan-outline-instrumentation=false %s -o %t && %run %t 10 >%t.out.0 2>&1
22
// RUN: FileCheck %s < %t.out.0
3-
// RUN: %clang_tysan -O2 %s -o %t && %run %t 10 >%t.out 2>&1
3+
// RUN: %clang_tysan -O2 -mllvm -tysan-outline-instrumentation=false %s -o %t && %run %t 10 >%t.out 2>&1
4+
// RUN: FileCheck %s < %t.out
5+
// RUN: %clang_tysan -O0 -mllvm -tysan-outline-instrumentation=true %s -o %t && %run %t 10 >%t.out.0 2>&1
6+
// RUN: FileCheck %s < %t.out.0
7+
// RUN: %clang_tysan -O2 -mllvm -tysan-outline-instrumentation=true %s -o %t && %run %t 10 >%t.out 2>&1
48
// RUN: FileCheck %s < %t.out
59

610
#include <stdio.h>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_tysan -mllvm -tysan-outline-instrumentation=true -mllvm -tysan-verify-outlined-instrumentation=true %s -o %t && %run %t >%t.out.0 2>&1
2+
// RUN: FileCheck %s < %t.out.0
3+
4+
#include <stdio.h>
5+
6+
void printInt(int *i) { printf("%d\n", *i); }
7+
8+
int main() {
9+
10+
float value = 5.0f;
11+
printInt((int *)&value);
12+
13+
return 0;
14+
}
15+
16+
// CHECK: ERROR: TypeSanitizer: type-aliasing-violation
17+
// CHECK-NEXT: READ of size 4 at {{.*}} with type int accesses an existing object of type float
18+
// CHECK-NEXT: {{#0 0x.* in printInt}}
19+
// CHECK-EMPTY:
20+
// CHECK-NEXT: ERROR: TypeSanitizer: type-aliasing-violation
21+
// CHECK-NEXT: READ of size 4 at {{.*}} with type int accesses an existing object of type float
22+
// CHECK-NEXT: {{#0 0x.* in printInt}}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %clang_tysan -mllvm -tysan-outline-instrumentation=true -O0 %s -o %t && %run %t >%t.out 2>&1
2+
// RUN: FileCheck %s < %t.out
3+
// RUN: %clang_tysan -mllvm -tysan-outline-instrumentation=true -mllvm -tysan-verify-outlined-instrumentation=true -O0 %s -o %t && %run %t >%t.out 2>&1
4+
// RUN: FileCheck %s --check-prefixes='CHECK,CHECK-VERIFY' < %t.out
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
9+
struct X {
10+
int i;
11+
int j;
12+
};
13+
14+
int foo(struct X *p, struct X *q) {
15+
q->j = 1;
16+
p->i = 0;
17+
// CHECK: ERROR: TypeSanitizer: type-aliasing-violation
18+
// CHECK-NEXT: WRITE of size 4 at {{.*}} with type int (in X at offset 0) accesses an existing object of type int (in X at offset 4)
19+
// CHECK-NEXT: {{#0 0x.* in foo .*struct-offset-outline.c:}}[[@LINE-3]]
20+
// CHECK-VERIFY-EMPTY:
21+
// CHECK-VERIFY-NEXT: ERROR: TypeSanitizer: type-aliasing-violation
22+
// CHECK-VERIFY-NEXT: WRITE of size 4 at {{.*}} with type int (in X at offset 0) accesses an existing object of type int (in X at offset 4)
23+
// CHECK-VERIFY-NEXT: {{#0 0x.* in foo .*struct-offset-outline.c:}}[[@LINE-7]]
24+
return q->j;
25+
}
26+
27+
int main() {
28+
unsigned char *p = malloc(3 * sizeof(int));
29+
printf("%i\n", foo((struct X *)(p + sizeof(int)), (struct X *)p));
30+
}
31+
32+
// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation

0 commit comments

Comments
 (0)