[compiler-rt] [llvm] [TySan] Add initial user facing interfaces (PR #169023)

via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 21 02:43:19 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Matthew Nagy (gbMattN)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/169023.diff


11 Files Affected:

- (modified) compiler-rt/include/CMakeLists.txt (+1) 
- (added) compiler-rt/include/sanitizer/tysan_interface.h (+42) 
- (modified) compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cpp (+1) 
- (modified) compiler-rt/lib/tysan/CMakeLists.txt (+2) 
- (modified) compiler-rt/lib/tysan/tysan.cpp (+24-12) 
- (modified) compiler-rt/lib/tysan/tysan.h (+2) 
- (added) compiler-rt/lib/tysan/tysan_interface.cpp (+34) 
- (added) compiler-rt/lib/tysan/tysan_interface.h (+42) 
- (added) compiler-rt/test/tysan/interface-manipulate-shadow.c (+99) 
- (added) compiler-rt/test/tysan/interface-print-type.c (+33) 
- (modified) llvm/utils/gn/secondary/compiler-rt/include/BUILD.gn (+1) 


``````````diff
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..8cb35ae4b5c09
--- /dev/null
+++ b/compiler-rt/include/sanitizer/tysan_interface.h
@@ -0,0 +1,42 @@
+//===-- tsan_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
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..fdffe784d0c74 100644
--- a/compiler-rt/lib/tysan/tysan.cpp
+++ b/compiler-rt/lib/tysan/tysan.cpp
@@ -56,29 +56,41 @@ static const char *getDisplayName(const char *Name) {
   return DName;
 }
 
-static void printTDName(tysan_type_descriptor *td) {
-  if (((sptr)td) <= 0) {
-    Printf("<unknown type>");
-    return;
+int getTDName(void *_td, char *buffer, uptr buffer_size, bool asset_on_error){
+  tysan_type_descriptor* td = (tysan_type_descriptor*)_td;
+  if(((sptr)td) <= 0){
+    return internal_snprintf(buffer, buffer_size, "<unknown type>");
   }
 
+  uptr written = 0;
   switch (td->Tag) {
   default:
-    CHECK(false && "invalid enum value");
+    if (asset_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) {
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..c2da6246b0044
--- /dev/null
+++ b/compiler-rt/lib/tysan/tysan_interface.cpp
@@ -0,0 +1,34 @@
+//===-- tysan_interface.inc --------------------------------------*- 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.h"
+#include "tysan_interface.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..5a5e5cc08d2b3
--- /dev/null
+++ b/compiler-rt/lib/tysan/tysan_interface.h
@@ -0,0 +1,42 @@
+//===-- 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
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..504de2876cf26
--- /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 <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
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..c61a7dc477353
--- /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 <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;
+}
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}}" ]

``````````

</details>


https://github.com/llvm/llvm-project/pull/169023


More information about the llvm-commits mailing list