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

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 09:46:03 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Matthew Nagy (gbMattN)

<details>
<summary>Changes</summary>

TypeSanitizer uses Clang's TBAA metadata to instrument the user program. TBAA metadata does not describe every possible type Clang can come across. In such situations, Clang conservatively emits `omnipotent char` to prevent any mis-compilation later when doing alias analysis. However, when TypeSanitizer uses this conservative metadata to check the user code, this results in false-positives.

This PR changes CodeGenTBAA, such that when running TypeSanitizer, a new TBAA scalar is emitted, `TysanConservativeTBAA`. The transformation pass can detect this, and once it makes the type descriptor for this TBAA, passes it on to the runtime. At runtime TypeSanitizer can conservatively assume that any type can alias whatever is stored in memory.

This prevents a vast majority of the false-positives currently reported. #<!-- -->210643 (and thus #<!-- -->208650), #<!-- -->208655, #<!-- -->208647 and #<!-- -->208646 are all examples, and have been added as test cases.

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


7 Files Affected:

- (modified) clang/lib/CodeGen/CodeGenTBAA.cpp (+3) 
- (modified) compiler-rt/lib/tysan/tysan.cpp (+19-3) 
- (added) compiler-rt/test/tysan/no-false-positive-issue208646.cpp (+11) 
- (added) compiler-rt/test/tysan/no-false-positive-issue208647.cpp (+10) 
- (added) compiler-rt/test/tysan/no-false-positive-issue208655.cpp (+15) 
- (added) compiler-rt/test/tysan/no-false-positive-issue210643.cpp (+11) 
- (modified) llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp (+27-1) 


``````````diff
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();
 }

``````````

</details>


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


More information about the llvm-commits mailing list