Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 119 additions & 2 deletions compiler-rt/lib/tysan/tysan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "tysan/tysan.h"

#include <stdint.h>
#include <string.h>

using namespace __sanitizer;
Expand Down Expand Up @@ -254,10 +255,68 @@ static void reportError(void *Addr, int Size, tysan_type_descriptor *TD,
}
}

ALWAYS_INLINE
static void SetShadowType(tysan_type_descriptor *td,
tysan_type_descriptor **shadowData,
uint64_t AccessSize) {
*shadowData = td;
uint64_t shadowDataInt = (uint64_t)shadowData;

for (uint64_t i = 1; i < AccessSize; ++i) {
int64_t dataOffset = i << PtrShift();
int64_t *badShadowData = (int64_t *)(shadowDataInt + dataOffset);
int64_t badTD = int64_t(i) * -1;
*badShadowData = badTD;
}
}

ALWAYS_INLINE
static bool GetNotAllBadTD(uint64_t ShadowDataInt, uint64_t AccessSize) {
bool notAllBadTD = false;
for (uint64_t i = 1; i < AccessSize; ++i) {
int64_t **unkShadowData = (int64_t **)(ShadowDataInt + (i << PtrShift()));
int64_t *ILdTD = *unkShadowData;
notAllBadTD = notAllBadTD || (ILdTD != nullptr);
}
return notAllBadTD;
}

ALWAYS_INLINE
static bool GetNotAllUnkTD(uint64_t ShadowDataInt, uint64_t AccessSize) {
bool notAllBadTD = false;
for (uint64_t i = 1; i < AccessSize; ++i) {
int64_t *badShadowData = (int64_t *)(ShadowDataInt + (i << PtrShift()));
int64_t ILdTD = *badShadowData;
notAllBadTD = notAllBadTD || (ILdTD >= 0);
}
return notAllBadTD;
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
GET_CALLER_PC_BP_SP;
__tysan_instrument_mem_inst(char *dest, char *src, uint64_t size,
bool needsMemMove) {
tysan_type_descriptor **destShadowDataPtr = shadow_for(dest);

if (!src) {
internal_memset((char *)destShadowDataPtr, 0, size << PtrShift());
return;
}

uint64_t srcInt = (uint64_t)src;
uint64_t srcShadowInt = ((srcInt & AppMask()) << PtrShift()) + ShadowAddr();
uint64_t *srcShadow = (uint64_t *)srcShadowInt;

if (needsMemMove) {
internal_memmove((char *)destShadowDataPtr, srcShadow, size << PtrShift());
} else {
internal_memcpy((char *)destShadowDataPtr, srcShadow, size << PtrShift());
}
}

ALWAYS_INLINE
static void __tysan_check_internal(void *addr, int size,
tysan_type_descriptor *td, int flags,
uptr pc, uptr bp, uptr sp) {
bool IsRead = flags & 1;
bool IsWrite = flags & 2;
const char *AccessStr;
Expand Down Expand Up @@ -300,6 +359,64 @@ __tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
}
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
GET_CALLER_PC_BP_SP;
__tysan_check_internal(addr, size, td, flags, pc, bp, sp);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__tysan_instrument_with_shadow_update(void *ptr, tysan_type_descriptor *td,
bool sanitizeFunction,
uint64_t accessSize, int flags) {
tysan_type_descriptor **shadowData = shadow_for(ptr);
tysan_type_descriptor *loadedTD = *shadowData;
bool shadowIsNull = loadedTD == nullptr;

// TODO, sanitizeFunction is known at compile time, so maybe this is split
// into two different functions
if (sanitizeFunction) {

Comment on lines +378 to +379
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be an early exit?

Suggested change
if (sanitizeFunction) {
if (sanitizeFunction)
return;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would skip the else if on line 374, so I don't think we can do that. If this is a major performance concern, I could split this into two RT functions since sanitizeFunction is known at compile time

if (td != loadedTD) {

// We now know that the types did not match (we're on the slow path). If
// the type is unknown, then set it.
if (shadowIsNull) {
// We're about to set the type. Make sure that all bytes in the value
// are also of unknown type.
bool isAllUnknownTD = GetNotAllUnkTD((uint64_t)shadowData, accessSize);
if (isAllUnknownTD) {
GET_CALLER_PC_BP_SP;
__tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
}
SetShadowType(td, shadowData, accessSize);
} else {
GET_CALLER_PC_BP_SP;
__tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
}
} else {
// We appear to have the right type. Make sure that all other bytes in
// the type are still marked as interior bytes. If not, call the runtime.
bool isNotAllBadTD = GetNotAllBadTD((uint64_t)shadowData, accessSize);
if (isNotAllBadTD) {
GET_CALLER_PC_BP_SP;
__tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
}
}
} else if (shadowIsNull) {
SetShadowType(td, shadowData, accessSize);
}
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__tysan_set_shadow_type(void *ptr, tysan_type_descriptor *td,
uint64_t accessSize) {
// In the mode where writes always set the type, for a write (which does
// not also read), we just set the type.
tysan_type_descriptor **shadow = shadow_for(ptr);
SetShadowType(td, shadow, accessSize);
}

Flags __tysan::flags_data;

SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;
Expand Down
16 changes: 15 additions & 1 deletion compiler-rt/lib/tysan/tysan_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,28 @@ struct Mapping {
static const uptr kShadowAddr = 0x010000000000ull;
static const uptr kAppAddr = 0x550000000000ull;
static const uptr kAppMemMsk = ~0x780000000000ull;
static const uptr kPtrShift = 3;
};
#elif defined(__aarch64__)
struct Mapping39 {
static const uptr kShadowAddr = 0x0800000000ull;
static const uptr kAppAddr = 0x5500000000ull;
static const uptr kAppMemMsk = ~0x7800000000ull;
static const uptr kPtrShift = 3;
};

struct Mapping42 {
static const uptr kShadowAddr = 0x10000000000ull;
static const uptr kAppAddr = 0x2aa00000000ull;
static const uptr kAppMemMsk = ~0x3c000000000ull;
static const uptr kPtrShift = 3;
};

struct Mapping48 {
static const uptr kShadowAddr = 0x0002000000000ull;
static const uptr kAppAddr = 0x0aaaa00000000ull;
static const uptr kAppMemMsk = ~0x0fff800000000ull;
static const uptr kPtrShift = 3;
};
#define TYSAN_RUNTIME_VMA 1
#else
Expand All @@ -49,7 +53,12 @@ struct Mapping48 {
extern int vmaSize;
#endif

enum MappingType { MAPPING_SHADOW_ADDR, MAPPING_APP_ADDR, MAPPING_APP_MASK };
enum MappingType {
MAPPING_SHADOW_ADDR,
MAPPING_APP_ADDR,
MAPPING_APP_MASK,
MAPPING_PTR_SHIFT
};

template <typename Mapping, int Type> uptr MappingImpl(void) {
switch (Type) {
Expand All @@ -59,6 +68,8 @@ template <typename Mapping, int Type> uptr MappingImpl(void) {
return Mapping::kAppAddr;
case MAPPING_APP_MASK:
return Mapping::kAppMemMsk;
case MAPPING_PTR_SHIFT:
return Mapping::kPtrShift;
}
}

Expand Down Expand Up @@ -88,6 +99,9 @@ uptr AppAddr() { return MappingArchImpl<MAPPING_APP_ADDR>(); }
ALWAYS_INLINE
uptr AppMask() { return MappingArchImpl<MAPPING_APP_MASK>(); }

ALWAYS_INLINE
uptr PtrShift() { return MappingArchImpl<MAPPING_PTR_SHIFT>(); }

} // namespace __tysan

#endif
8 changes: 6 additions & 2 deletions compiler-rt/test/tysan/basic.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// RUN: %clang_tysan -O0 %s -o %t && %run %t 10 >%t.out.0 2>&1
// RUN: %clang_tysan -O0 -mllvm -tysan-outline-instrumentation=false %s -o %t && %run %t 10 >%t.out.0 2>&1
// RUN: FileCheck %s < %t.out.0
// RUN: %clang_tysan -O2 %s -o %t && %run %t 10 >%t.out 2>&1
// RUN: %clang_tysan -O2 -mllvm -tysan-outline-instrumentation=false %s -o %t && %run %t 10 >%t.out 2>&1
// RUN: FileCheck %s < %t.out
// RUN: %clang_tysan -O0 -mllvm -tysan-outline-instrumentation=true %s -o %t && %run %t 10 >%t.out.0 2>&1
// RUN: FileCheck %s < %t.out.0
// RUN: %clang_tysan -O2 -mllvm -tysan-outline-instrumentation=true %s -o %t && %run %t 10 >%t.out 2>&1
// RUN: FileCheck %s < %t.out

#include <stdio.h>
Expand Down
22 changes: 22 additions & 0 deletions compiler-rt/test/tysan/simple_verify_outlines.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %clang_tysan -mllvm -tysan-outline-instrumentation=true -mllvm -tysan-verify-outlined-instrumentation=true %s -o %t && %run %t >%t.out.0 2>&1
// RUN: FileCheck %s < %t.out.0

#include <stdio.h>

void printInt(int *i) { printf("%d\n", *i); }

int main() {

float value = 5.0f;
printInt((int *)&value);

return 0;
}

// CHECK: ERROR: TypeSanitizer: type-aliasing-violation
// CHECK-NEXT: READ of size 4 at {{.*}} with type int accesses an existing object of type float
// CHECK-NEXT: {{#0 0x.* in printInt}}
// CHECK-EMPTY:
// CHECK-NEXT: ERROR: TypeSanitizer: type-aliasing-violation
// CHECK-NEXT: READ of size 4 at {{.*}} with type int accesses an existing object of type float
// CHECK-NEXT: {{#0 0x.* in printInt}}
32 changes: 32 additions & 0 deletions compiler-rt/test/tysan/struct-offset-outline.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang_tysan -mllvm -tysan-outline-instrumentation=true -O0 %s -o %t && %run %t >%t.out 2>&1
// RUN: FileCheck %s < %t.out
// RUN: %clang_tysan -mllvm -tysan-outline-instrumentation=true -mllvm -tysan-verify-outlined-instrumentation=true -O0 %s -o %t && %run %t >%t.out 2>&1
// RUN: FileCheck %s --check-prefixes='CHECK,CHECK-VERIFY' < %t.out

#include <stdio.h>
#include <stdlib.h>

struct X {
int i;
int j;
};

int foo(struct X *p, struct X *q) {
q->j = 1;
p->i = 0;
// CHECK: ERROR: TypeSanitizer: type-aliasing-violation
// 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)
// CHECK-NEXT: {{#0 0x.* in foo .*struct-offset.c:}}[[@LINE-3]]
// CHECK-VERIFY-EMPTY:
// CHECK-VERIFY-NEXT: ERROR: TypeSanitizer: type-aliasing-violation
// 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)
// CHECK-VERIFY-NEXT: {{#0 0x.* in foo .*struct-offset.c:}}[[@LINE-7]]
return q->j;
}

int main() {
unsigned char *p = malloc(3 * sizeof(int));
printf("%i\n", foo((struct X *)(p + sizeof(int)), (struct X *)p));
}

// CHECK-NOT: ERROR: TypeSanitizer: type-aliasing-violation
Loading
Loading