Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions compiler-rt/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ if (COMPILER_RT_BUILD_SANITIZERS)
sanitizer/scudo_interface.h
sanitizer/tsan_interface.h
sanitizer/tsan_interface_atomic.h
sanitizer/tysan_interface.h
sanitizer/ubsan_interface.h
)
set(FUZZER_HEADERS
Expand Down
48 changes: 48 additions & 0 deletions compiler-rt/include/sanitizer/tysan_interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===-- tysan_interface.h ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of TypeSanitizer.
//
// Public interface header for TySan.
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_TYSAN_INTERFACE_H
#define SANITIZER_TYSAN_INTERFACE_H

#include <sanitizer/common_interface_defs.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

// Copies the shadow memory for the source user memory into the shadow memory
// for the destination user memory
void SANITIZER_CDECL __tysan_copy_shadow(const void *dst, const void *src,
size_t type_size);

// Copies the shadow memory for the source user memory into the shadow memory
// for each element in the destination array in user memory
void SANITIZER_CDECL __tysan_copy_shadow_array(const void *dst_array,
const void *src,
size_t type_size,
size_t arraySize);

// Clears the shadow memory for the given range of user memory.
void SANITIZER_CDECL __tysan_reset_shadow(const void *addr, size_t size);

// Writes the name of the type represented in the shadow memory for the given
// location in user memory into the given buffer, up to the given size. Returns
// the length written.
int SANITIZER_CDECL __tysan_get_type_name(const void *addr, char *buffer,
size_t buffer_size);

#ifdef __cplusplus
}
#endif

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "../../../include/sanitizer/asan_interface.h"
#include "../../../include/sanitizer/msan_interface.h"
#include "../../../include/sanitizer/tsan_interface.h"
#include "../../../include/sanitizer/tysan_interface.h"
#include "gtest/gtest.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/tysan/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ include_directories(..)
set(TYSAN_SOURCES
tysan.cpp
tysan_interceptors.cpp
tysan_interface.cpp
)

SET(TYSAN_HEADERS
tysan.h
tysan_flags.inc
tysan_interface.h
tysan_platform.h
)

Expand Down
38 changes: 27 additions & 11 deletions compiler-rt/lib/tysan/tysan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,45 @@ static const char *getDisplayName(const char *Name) {
return DName;
}

static void printTDName(tysan_type_descriptor *td) {
int getTDName(void *_td, char *buffer, uptr buffer_size, bool assert_on_error) {
tysan_type_descriptor *td = (tysan_type_descriptor *)_td;
if (((sptr)td) <= 0) {
Printf("<unknown type>");
return;
return internal_snprintf(buffer, buffer_size, "<unknown type>");
}

uptr written = 0;
switch (td->Tag) {
default:
CHECK(false && "invalid enum value");
if (assert_on_error)
CHECK(false && "invalid enum value");
else
written = internal_snprintf(buffer, buffer_size, "<invalid shadow>");
break;
case TYSAN_MEMBER_TD:
printTDName(td->Member.Access);
if (td->Member.Access != td->Member.Base) {
Printf(" (in ");
printTDName(td->Member.Base);
Printf(" at offset %zu)", td->Member.Offset);
written = getTDName(td->Member.Access, buffer, buffer_size, false);
if (td->Member.Access != td->Member.Base && written != buffer_size) {
written +=
internal_snprintf(&buffer[written], buffer_size - written, " (in ");
written += getTDName(td->Member.Base, &buffer[written],
buffer_size - written, false);
written += internal_snprintf(&buffer[written], buffer_size - written,
" at offset %zu)", td->Member.Offset);
}
break;
case TYSAN_STRUCT_TD:
Printf("%s", getDisplayName(
(char *)(td->Struct.Members + td->Struct.MemberCount)));
written = internal_snprintf(
buffer, buffer_size, "%s",
getDisplayName((char *)(td->Struct.Members + td->Struct.MemberCount)));
break;
}
return written;
}

static void printTDName(tysan_type_descriptor *td) {
static const uptr nameBufferSize = 512;
static char nameBuffer[nameBufferSize];
getTDName(td, nameBuffer, nameBufferSize, true);
Printf("%s", nameBuffer);
}

static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) {
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/tysan/tysan.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ using __sanitizer::sptr;
using __sanitizer::u16;
using __sanitizer::uptr;

#include "tysan_interface.h"
#include "tysan_platform.h"

extern "C" {
void tysan_set_type_unknown(const void *addr, uptr size);
void tysan_copy_types(const void *daddr, const void *saddr, uptr size);
int getTDName(void *td, char *buffer, uptr buffer_size, bool assert_on_error);
}

namespace __tysan {
Expand Down
35 changes: 35 additions & 0 deletions compiler-rt/lib/tysan/tysan_interface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===-- tysan_interface.cpp -------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of TypeSanitizer.
//
//===----------------------------------------------------------------------===//
#include "tysan_interface.h"
#include "tysan.h"

void __tysan_copy_shadow(const void *dst, const void *src, size_t type_size) {
tysan_copy_types(dst, src, type_size);
}

void __tysan_copy_shadow_array(const void *dst_array, const void *src,
size_t type_size, size_t arraySize) {
const void *dst = dst_array;
for (size_t i = 0; i < arraySize; i++) {
tysan_copy_types(dst, src, type_size);
dst = (void *)(((uptr)dst) + type_size);
}
}

void __tysan_reset_shadow(const void *addr, size_t size) {
tysan_set_type_unknown(addr, size);
}

int __tysan_get_type_name(const void *addr, char *buffer, size_t buffer_size) {
void **shadow = (void **)__tysan::shadow_for(addr);
return getTDName(*shadow, buffer, buffer_size, false);
}
43 changes: 43 additions & 0 deletions compiler-rt/lib/tysan/tysan_interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===-- tysan_interface.h ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of TypeSanitizer.
//
// The functions declared in this header will be inserted by the instrumentation
// module.
// This header can be included by the instrumented program or by TySan tests.
//===----------------------------------------------------------------------===//

#ifndef TYSAN_INTERFACE_H
#define TYSAN_INTERFACE_H

#include <sanitizer_common/sanitizer_internal_defs.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

SANITIZER_INTERFACE_ATTRIBUTE
void __tysan_copy_shadow(const void *dst, const void *src, size_t type_size);

SANITIZER_INTERFACE_ATTRIBUTE
void __tysan_copy_shadow_array(const void *dst_array, const void *src,
size_t type_size, size_t arraySize);

SANITIZER_INTERFACE_ATTRIBUTE
void __tysan_reset_shadow(const void *addr, size_t size);

SANITIZER_INTERFACE_ATTRIBUTE
int __tysan_get_type_name(const void *addr, char *buffer, size_t buffer_size);

#ifdef __cplusplus
} // extern "C"
#endif

#endif
99 changes: 99 additions & 0 deletions compiler-rt/test/tysan/interface-manipulate-shadow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// REQUIRES: system-linux || system-darwin
// RUN: %clang_tysan %s -g -shared -fpic -o %t.so -DBUILD_SO
// RUN: %clang_tysan %s -g -o %t
// RUN: %run %t %t.so 2>&1 | FileCheck %s

// Compilers can't optimize using type aliasing across the bounds of dynamic librarys
// When passing memory between instrumented executables and dlls, you may want to alter TySan's
// shadow to prevent it from catching technically correct, yet harmless aliasing violations

#ifdef BUILD_SO
float useFloatArray(float *mem) {
mem[0] = 2.f;
mem[1] = 3.f;
return mem[0] + mem[1];
}

int useIntArray(int *mem) {
mem[0] = 2;
mem[1] = 3;
mem[2] = 5;
return mem[0] + mem[1] + mem[2];
}
#else

# include <assert.h>
# include <dlfcn.h>
# include <sanitizer/tysan_interface.h>
# include <stdio.h>

typedef float (*lib_func1_t)(float *);
typedef int (*lib_func2_t)(int *);

void print_flush(const char *message) {
printf("%s\n", message);
fflush(stdout);
}

int main(int argc, char *argv[]) {
assert(argc >= 2);
void *libHandle = dlopen(argv[1], RTLD_LAZY);
assert(libHandle);

lib_func1_t useFloatArray = (lib_func1_t)dlsym(libHandle, "useFloatArray");
lib_func2_t useIntArray = (lib_func2_t)dlsym(libHandle, "useIntArray");

char memory[sizeof(int) * 3];
int iResult = 0;
float fResult = 0.f;
print_flush("Calling with omnipotent char memory");
fResult = useFloatArray((float *)memory);
print_flush("Shadow now has floats in");
iResult = useIntArray((int *)memory);

// CHECK: Calling with omnipotent char memory
// CHECK-NEXT: Shadow now has floats in
// CHECK-NEXT: ERROR: TypeSanitizer: type-aliasing-violation on address
// CHECK-NEXT: WRITE of size 4 at 0x{{.*}} with type int accesses an existing object of type float

__tysan_reset_shadow(memory, sizeof(memory));
print_flush("Shadow has been reset");
useIntArray((int *)memory);
print_flush("Completed int array");

// CHECK: Shadow has been reset
// CHECK-NEXT: Completed int array

// Set shadow type to float
__tysan_copy_shadow_array(memory, &fResult, sizeof(float), 3);
print_flush("Float array with float set shadow");
useFloatArray((float *)memory);
print_flush("Int array with float set shadow");
useIntArray((int *)memory);

// CHECK: Float array with float set shadow
// CHECK-NEXT: Int array with float set shadow
// CHECK-NEXT: ERROR: TypeSanitizer: type-aliasing-violation on address
// CHECK-NEXT: WRITE of size 4 at 0x{{.*}} with type int accesses an existing object of type float

// Set shadow type to int
for (size_t i = 0; i < 3; i++) {
__tysan_copy_shadow(&memory[sizeof(int) * i], &iResult, sizeof(int));
}
print_flush("Float array with int set shadow");
useFloatArray((float *)memory);
print_flush("Int array with int set shadow");
useIntArray((int *)memory);
print_flush("Completed int array");

// CHECK: Float array with int set shadow
// CHECK-NEXT: ERROR: TypeSanitizer: type-aliasing-violation on address
// CHECK-NEXT: WRITE of size 4 at 0x{{.*}} with type float accesses an existing object of type int
// CHECK: Int array with int set shadow
// CHECK-NEXT: Completed int array

dlclose(libHandle);
return 0;
}

#endif
33 changes: 33 additions & 0 deletions compiler-rt/test/tysan/interface-print-type.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %clang_tysan %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s

#include <sanitizer/tysan_interface.h>
#include <stdio.h>

struct S {
int i;
float f;
};

void printInt(int *i) {
const int bufferSize = 512;
static char nameBuffer[bufferSize];
__tysan_get_type_name(i, nameBuffer, 512);
printf("%d, %s\n", *i, nameBuffer);
fflush(stdout);
}

int main() {
struct S s;
s.i = 4;
printInt((int *)&s);
// CHECK: 4, int (in S at offset 0)

s.f = 5.0f;
// CHECK: ERROR: TypeSanitizer: type-aliasing-violation
// CHECK: READ of size 4 at 0x{{.*}} with type int accesses an existing object of type float (in S at offset 4)
// CHECK: {{.*}}, float (in S at offset 4)
printInt((int *)&s.f);

return 0;
}
1 change: 1 addition & 0 deletions llvm/utils/gn/secondary/compiler-rt/include/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ copy("include") {
"sanitizer/scudo_interface.h",
"sanitizer/tsan_interface.h",
"sanitizer/tsan_interface_atomic.h",
sanitizer/tysan_interface.h
"sanitizer/ubsan_interface.h",
]
outputs = [ "$clang_resource_dir/include/{{source_target_relative}}" ]
Expand Down