[clang] [compiler-rt] [llvm] [TySan] Let TypeSanitizer know about conservative path TBAA data (PR #227782)

Matthew Nagy via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 09:52:57 PDT 2026


https://github.com/gbMattN updated https://github.com/llvm/llvm-project/pull/227782

>From 9e91aaca49966ffe7a8665c562e8356cf4fae9a7 Mon Sep 17 00:00:00 2001
From: gbMattN <matthew.nagy at sony.com>
Date: Wed, 30 Sep 2026 17:07:22 +0100
Subject: [PATCH 1/2] [TySan] Let TypeSanitizer know about conservative path
 TBAA data

---
 clang/lib/CodeGen/CodeGenTBAA.cpp             |  3 ++
 compiler-rt/lib/tysan/tysan.cpp               | 22 +++++++++++++--
 .../tysan/no-false-positive-issue208646.cpp   | 11 ++++++++
 .../tysan/no-false-positive-issue208647.cpp   | 10 +++++++
 .../tysan/no-false-positive-issue208655.cpp   | 15 ++++++++++
 .../tysan/no-false-positive-issue210643.cpp   | 11 ++++++++
 .../Instrumentation/TypeSanitizer.cpp         | 28 ++++++++++++++++++-
 7 files changed, 96 insertions(+), 4 deletions(-)
 create mode 100644 compiler-rt/test/tysan/no-false-positive-issue208646.cpp
 create mode 100644 compiler-rt/test/tysan/no-false-positive-issue208647.cpp
 create mode 100644 compiler-rt/test/tysan/no-false-positive-issue208655.cpp
 create mode 100644 compiler-rt/test/tysan/no-false-positive-issue210643.cpp

diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp
index 1854df7c7c0f1..acdf6abc59b61 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.cpp
+++ b/clang/lib/CodeGen/CodeGenTBAA.cpp
@@ -367,6 +367,9 @@ llvm::MDNode *CodeGenTBAA::getTypeInfoHelper(const Type *Ty) {
   }
 
   // For now, handle any other kind of type conservatively.
+  if(Features.Sanitize.has(SanitizerKind::Type)){
+    return createScalarTypeNode("TysanConservativeTBAA", getChar(), 1);
+  }
   return getChar();
 }
 
diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp
index 8b3b60dc4cf83..cc638cef5db83 100644
--- a/compiler-rt/lib/tysan/tysan.cpp
+++ b/compiler-rt/lib/tysan/tysan.cpp
@@ -128,12 +128,27 @@ static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) {
   return RootTD;
 }
 
+// Currently, Clang's TBAA system does not correctly describe every possible
+// type. For unhandled types, it makes the most conservative choice, emitting
+// omnipotent char. TySan needs to handle this seperately to a real omnipotent
+// char otherwise the user may get false positives. When compiling with TySan
+// enabled, clang will emit a special TBAA type to show that the conservative
+// path has been taken. When the transformation pass finds this TBAA, it will
+// set this global variable. This then allows quick comparison of TDs at
+// runtime.
+static tysan_type_descriptor *__tysan_conservative_tbaa_descriptor = nullptr;
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__tysan_set_conservative_tbaa_descriptor(
+    tysan_type_descriptor *conservativeTBAATD) {
+  __tysan_conservative_tbaa_descriptor = conservativeTBAATD;
+}
+
 // Walk up TDA to see if it reaches TDB.
 static bool walkAliasTree(tysan_type_descriptor *TDA,
                           tysan_type_descriptor *TDB, uptr OffsetA,
                           uptr OffsetB) {
   do {
-    if (TDA == TDB)
+    if (TDA == TDB || TDA == __tysan_conservative_tbaa_descriptor)
       return OffsetA == OffsetB;
 
     if (TDA->Tag == TYSAN_STRUCT_TD) {
@@ -182,7 +197,6 @@ static bool isAliasingLegalUp(tysan_type_descriptor *TDA,
     OffsetA = TDA->Member.Offset;
     TDA = TDA->Member.Base;
   }
-
   return walkAliasTree(TDA, TDB, OffsetA, OffsetB);
 }
 
@@ -213,7 +227,9 @@ static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA,
 
 static bool isAliasingLegal(tysan_type_descriptor *TDA,
                             tysan_type_descriptor *TDB, uptr OffsetB = 0) {
-  if (TDA == TDB || !TDB || !TDA)
+  if (TDA == TDB || !TDB || !TDA ||
+      TDA == __tysan_conservative_tbaa_descriptor ||
+      TDB == __tysan_conservative_tbaa_descriptor)
     return true;
 
   // Aliasing is legal is the two types have different root nodes.
diff --git a/compiler-rt/test/tysan/no-false-positive-issue208646.cpp b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp
new file mode 100644
index 0000000000000..236b4a86d0a2b
--- /dev/null
+++ b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp
@@ -0,0 +1,11 @@
+// RUN: %clangxx_tysan -O0 %s -o %t && %run %t
+
+#include <string>
+#include <optional>
+
+static std::optional<std::string> optional_var = std::nullopt;
+
+int main() { 
+  optional_var = "this is a random long string (short one does not reproduce)";
+  return 0;
+}
diff --git a/compiler-rt/test/tysan/no-false-positive-issue208647.cpp b/compiler-rt/test/tysan/no-false-positive-issue208647.cpp
new file mode 100644
index 0000000000000..ef8b004496a16
--- /dev/null
+++ b/compiler-rt/test/tysan/no-false-positive-issue208647.cpp
@@ -0,0 +1,10 @@
+// RUN: %clangxx_tysan -O0 %s -o %t && %run %t
+
+#include <variant>
+
+int main() {
+  std::variant<int, double> v;
+  v = 1;
+  v = 3.5;
+  return 0;
+}
diff --git a/compiler-rt/test/tysan/no-false-positive-issue208655.cpp b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp
new file mode 100644
index 0000000000000..7518b244513cd
--- /dev/null
+++ b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp
@@ -0,0 +1,15 @@
+// RUN: %clangxx_tysan -O0 %s -o %t && %run %t
+
+#include <vector>
+
+struct Registry {
+    std::vector<int> arr;
+    char temp_byte;
+    bool bool_var = false;
+};
+
+int main() {
+    static Registry r;
+    r.arr.push_back(0);
+    r.bool_var = true;
+}
diff --git a/compiler-rt/test/tysan/no-false-positive-issue210643.cpp b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp
new file mode 100644
index 0000000000000..d667f75646cfd
--- /dev/null
+++ b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp
@@ -0,0 +1,11 @@
+// RUN: %clangxx_tysan -O0 %s -o %t && %run %t
+
+struct A {
+    int elems[3];
+};
+
+A a;
+
+int main() {
+    a.elems[0] = 1;
+}
diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
index cea6e9e316c1d..0f66904b0e8c2 100644
--- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
@@ -80,6 +80,7 @@ struct TypeSanitizer {
   TypeSanitizer(Module &M);
   bool sanitizeFunction(Function &F, const TargetLibraryInfo &TLI);
   void instrumentGlobals(Module &M);
+  void setConservativeTBAA(Module &M);
 
 private:
   typedef SmallDenseMap<const MDNode *, GlobalVariable *, 8>
@@ -127,6 +128,9 @@ struct TypeSanitizer {
   FunctionCallee TysanInstrumentWithShadowUpdate;
   FunctionCallee TysanSetShadowType;
 
+  FunctionCallee TysanSetConservativeTBAADescriptor;
+  GlobalVariable *ConservativeTBAADescriptor;
+
   /// Callback to set types for gloabls.
   Function *TysanGlobalsSetTypeFunction;
 };
@@ -134,7 +138,8 @@ struct TypeSanitizer {
 
 TypeSanitizer::TypeSanitizer(Module &M)
     : TargetTriple(M.getTargetTriple()),
-      AnonNameRegex("^_ZTS.*N[1-9][0-9]*_GLOBAL__N") {
+      AnonNameRegex("^_ZTS.*N[1-9][0-9]*_GLOBAL__N"),
+      ConservativeTBAADescriptor(nullptr) {
   const DataLayout &DL = M.getDataLayout();
   IntptrTy = DL.getIntPtrType(M.getContext());
   PtrShift = countr_zero(IntptrTy->getPrimitiveSizeInBits() / 8);
@@ -186,6 +191,12 @@ void TypeSanitizer::initializeCallbacks(Module &M) {
       IRB.getPtrTy(), // Pointer to the new type descriptor
       U64Ty           // Size of data we access in bytes
   );
+
+  TysanSetConservativeTBAADescriptor = M.getOrInsertFunction(
+      "__tysan_set_conservative_tbaa_descriptor", Attr, IRB.getVoidTy(),
+      IRB.getPtrTy() // Pointer to the type descriptor that describes the TBAA
+                     // clang generates under the conservative TBAA path
+  );
 }
 
 void TypeSanitizer::instrumentGlobals(Module &M) {
@@ -234,6 +245,16 @@ void TypeSanitizer::instrumentGlobals(Module &M) {
   }
 }
 
+void TypeSanitizer::setConservativeTBAA(Module &M) {
+  if (ConservativeTBAADescriptor) {
+    IRBuilder<> IRB(cast<Function>(TysanCtorFunction.getCallee())
+                        ->getEntryBlock()
+                        .getTerminator());
+    IRB.CreateCall(TysanSetConservativeTBAADescriptor,
+                   {ConservativeTBAADescriptor});
+  }
+}
+
 static const char LUT[] = "0123456789abcdef";
 
 static std::string encodeName(StringRef Name) {
@@ -391,6 +412,9 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
                          TD, EncodedName);
   M.insertGlobalVariable(TDGV);
 
+  if (Name == "TysanConservativeTBAA") {
+    ConservativeTBAADescriptor = TDGV;
+  }
   if (ShouldBeComdat) {
     if (TargetTriple.isOSBinFormatELF()) {
       Comdat *TDComdat = M.getOrInsertComdat(EncodedName);
@@ -973,5 +997,7 @@ PreservedAnalyses TypeSanitizerPass::run(Module &M,
     }
   }
 
+  TySan.setConservativeTBAA(M);
+
   return PreservedAnalyses::none();
 }

>From 65e40b0402ea00242eafc96de7aa933f599c5c7f Mon Sep 17 00:00:00 2001
From: gbMattN <matthew.nagy at sony.com>
Date: Wed, 30 Sep 2026 17:52:42 +0100
Subject: [PATCH 2/2] Format repro's copied from issues

---
 .../test/tysan/no-false-positive-issue208646.cpp     |  4 ++--
 .../test/tysan/no-false-positive-issue208655.cpp     | 12 ++++++------
 .../test/tysan/no-false-positive-issue210643.cpp     |  6 ++----
 3 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/compiler-rt/test/tysan/no-false-positive-issue208646.cpp b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp
index 236b4a86d0a2b..fc8a2da6e8b11 100644
--- a/compiler-rt/test/tysan/no-false-positive-issue208646.cpp
+++ b/compiler-rt/test/tysan/no-false-positive-issue208646.cpp
@@ -1,11 +1,11 @@
 // RUN: %clangxx_tysan -O0 %s -o %t && %run %t
 
-#include <string>
 #include <optional>
+#include <string>
 
 static std::optional<std::string> optional_var = std::nullopt;
 
-int main() { 
+int main() {
   optional_var = "this is a random long string (short one does not reproduce)";
   return 0;
 }
diff --git a/compiler-rt/test/tysan/no-false-positive-issue208655.cpp b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp
index 7518b244513cd..cba71c5b5be49 100644
--- a/compiler-rt/test/tysan/no-false-positive-issue208655.cpp
+++ b/compiler-rt/test/tysan/no-false-positive-issue208655.cpp
@@ -3,13 +3,13 @@
 #include <vector>
 
 struct Registry {
-    std::vector<int> arr;
-    char temp_byte;
-    bool bool_var = false;
+  std::vector<int> arr;
+  char temp_byte;
+  bool bool_var = false;
 };
 
 int main() {
-    static Registry r;
-    r.arr.push_back(0);
-    r.bool_var = true;
+  static Registry r;
+  r.arr.push_back(0);
+  r.bool_var = true;
 }
diff --git a/compiler-rt/test/tysan/no-false-positive-issue210643.cpp b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp
index d667f75646cfd..4a74abd43d02d 100644
--- a/compiler-rt/test/tysan/no-false-positive-issue210643.cpp
+++ b/compiler-rt/test/tysan/no-false-positive-issue210643.cpp
@@ -1,11 +1,9 @@
 // RUN: %clangxx_tysan -O0 %s -o %t && %run %t
 
 struct A {
-    int elems[3];
+  int elems[3];
 };
 
 A a;
 
-int main() {
-    a.elems[0] = 1;
-}
+int main() { a.elems[0] = 1; }



More information about the llvm-commits mailing list