diff --git a/compiler-rt/include/CMakeLists.txt b/compiler-rt/include/CMakeLists.txt index 242d62b9b447b..8443e309d1e95 100644 --- a/compiler-rt/include/CMakeLists.txt +++ b/compiler-rt/include/CMakeLists.txt @@ -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 diff --git a/compiler-rt/include/sanitizer/tysan_interface.h b/compiler-rt/include/sanitizer/tysan_interface.h new file mode 100644 index 0000000000000..d198363df3a34 --- /dev/null +++ b/compiler-rt/include/sanitizer/tysan_interface.h @@ -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 +#include + +#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 diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp index 111e55ef36bfb..917ba847ad478 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp @@ -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" diff --git a/compiler-rt/lib/tysan/CMakeLists.txt b/compiler-rt/lib/tysan/CMakeLists.txt index 7d13ae3963919..a878a46322bd2 100644 --- a/compiler-rt/lib/tysan/CMakeLists.txt +++ b/compiler-rt/lib/tysan/CMakeLists.txt @@ -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 ) diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp index 1c67adeba0fc5..4a9f563ee43df 100644 --- a/compiler-rt/lib/tysan/tysan.cpp +++ b/compiler-rt/lib/tysan/tysan.cpp @@ -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(""); - return; + return internal_snprintf(buffer, buffer_size, ""); } + 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, ""); 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) { diff --git a/compiler-rt/lib/tysan/tysan.h b/compiler-rt/lib/tysan/tysan.h index 97df28037b0d2..b7b1306cf10bd 100644 --- a/compiler-rt/lib/tysan/tysan.h +++ b/compiler-rt/lib/tysan/tysan.h @@ -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 { diff --git a/compiler-rt/lib/tysan/tysan_interface.cpp b/compiler-rt/lib/tysan/tysan_interface.cpp new file mode 100644 index 0000000000000..3d7d9d101c5f9 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan_interface.cpp @@ -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); +} diff --git a/compiler-rt/lib/tysan/tysan_interface.h b/compiler-rt/lib/tysan/tysan_interface.h new file mode 100644 index 0000000000000..9af0c66f0b606 --- /dev/null +++ b/compiler-rt/lib/tysan/tysan_interface.h @@ -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 +#include + +#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 diff --git a/compiler-rt/test/tysan/interface-manipulate-shadow.c b/compiler-rt/test/tysan/interface-manipulate-shadow.c new file mode 100644 index 0000000000000..25614e25e1f6c --- /dev/null +++ b/compiler-rt/test/tysan/interface-manipulate-shadow.c @@ -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 +# include +# include +# include + +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 diff --git a/compiler-rt/test/tysan/interface-print-type.c b/compiler-rt/test/tysan/interface-print-type.c new file mode 100644 index 0000000000000..18c3bb0ceab20 --- /dev/null +++ b/compiler-rt/test/tysan/interface-print-type.c @@ -0,0 +1,33 @@ +// RUN: %clang_tysan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include +#include + +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; +} diff --git a/llvm/utils/gn/secondary/compiler-rt/include/BUILD.gn b/llvm/utils/gn/secondary/compiler-rt/include/BUILD.gn index 273fd7172da62..8e8ce837ef21e 100644 --- a/llvm/utils/gn/secondary/compiler-rt/include/BUILD.gn +++ b/llvm/utils/gn/secondary/compiler-rt/include/BUILD.gn @@ -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}}" ]