[llvm] [TySan] Fix crash in instrumentation with scalar TBAA tag missing offset (PR #172878)
Matthew Nagy via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 17 06:19:16 PDT 2026
https://github.com/gbMattN updated https://github.com/llvm/llvm-project/pull/172878
>From f64a85d43b89e979c40d58861b5462eb18e0c575 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Thu, 18 Dec 2025 16:33:58 +0000
Subject: [PATCH 1/5] [TySan] Fix crash with scalar TBAA tag without offset
---
llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
index 1c91d833ea616..0657abf7461cc 100644
--- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
@@ -345,8 +345,13 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
Member = TypeDescriptors[MemberNode];
}
- uint64_t Offset =
- mdconst::extract<ConstantInt>(MD->getOperand(i + 1))->getZExtValue();
+ // According to the LLVM language reference, the third field is actually
+ // optional, its absence indicating an offset of zero.
+ uint64_t Offset = 0;
+ if ((unsigned)i + 1 < MD->getNumOperands()) {
+ Offset =
+ mdconst::extract<ConstantInt>(MD->getOperand(i + 1))->getZExtValue();
+ }
Members.push_back(std::make_pair(Member, Offset));
}
>From 94e88a4e73d897f76846de586acca28261a641c8 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 19 Dec 2025 11:24:42 +0000
Subject: [PATCH 2/5] Introduce regression test for crash caused by missing
offset
---
...19-crash-scalar-tbaa-tag-without-offset.ll | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll
diff --git a/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll b/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll
new file mode 100644
index 0000000000000..815b380f2bd39
--- /dev/null
+++ b/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll
@@ -0,0 +1,30 @@
+; RUN: opt -passes='tysan' -tysan-outline-instrumentation=false -S %s
+; RUN: opt -passes='tysan' -S %s
+
+; ModuleID = 'nooffset.c'
+source_filename = "nooffset.c"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+
+; Function Attrs: noinline nounwind optnone sanitize_type sspstrong uwtable
+define dso_local i32 @main() #0 {
+ %1 = alloca i32, align 4
+ store i32 10, ptr %1, align 4, !tbaa !6
+ ret i32 0
+}
+
+attributes #0 = { noinline nounwind optnone sanitize_type sspstrong uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4}
+!llvm.ident = !{!5}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 7, !"PIE Level", i32 2}
+!3 = !{i32 7, !"uwtable", i32 2}
+!4 = !{i32 7, !"frame-pointer", i32 2}
+!5 = !{!"clang version 21.1.5"}
+!6 = !{!7, !7, i64 0}
+!7 = !{!"int", !8} ; According to the LLVM language reference, the third argument is optional
+!8 = !{!"omnipotent char", !9, i64 0}
+!9 = !{!"Simple C/C++ TBAA"}
>From 1c1225a6bd18eb8e61937cc00fdf3c35980dc599 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 19 Dec 2025 11:32:22 +0000
Subject: [PATCH 3/5] Only accept scalar tags with 2 operands (still reject
struct tag with last offset missing)
---
llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
index 0657abf7461cc..6a04a6da89046 100644
--- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
@@ -345,12 +345,17 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
Member = TypeDescriptors[MemberNode];
}
- // According to the LLVM language reference, the third field is actually
- // optional, its absence indicating an offset of zero.
- uint64_t Offset = 0;
+ uint64_t Offset;
if ((unsigned)i + 1 < MD->getNumOperands()) {
Offset =
mdconst::extract<ConstantInt>(MD->getOperand(i + 1))->getZExtValue();
+ } else if (i == 1 && MD->getNumOperands() == 2) {
+ // According to the LLVM language reference, the third operand for a
+ // scalar tag is actually optional, its absence indicating an offset
+ // of zero.
+ Offset = 0;
+ } else {
+ assert(false && "Malformed TBAA MD.");
}
Members.push_back(std::make_pair(Member, Offset));
>From 47e16e74fe2b1688d47d4b63cf4c21fbd9cad5f7 Mon Sep 17 00:00:00 2001
From: Matthew Nagy <matthew.nagy at sony.com>
Date: Fri, 17 Apr 2026 13:34:49 +0100
Subject: [PATCH 4/5] Apply suggestions from code review
Co-authored-by: Florian Hahn <flo at fhahn.com>
---
llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
index 6a04a6da89046..71a95dcc1a3c2 100644
--- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
@@ -352,7 +352,7 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
} else if (i == 1 && MD->getNumOperands() == 2) {
// According to the LLVM language reference, the third operand for a
// scalar tag is actually optional, its absence indicating an offset
- // of zero.
+ // The third operand for a scalar tag is actually optional, its absence indicating an offset of zero.
Offset = 0;
} else {
assert(false && "Malformed TBAA MD.");
>From acf68430820a7a71af870f35af369be2689efef9 Mon Sep 17 00:00:00 2001
From: gbMattN <matthew.nagy at sony.com>
Date: Fri, 17 Apr 2026 14:17:32 +0100
Subject: [PATCH 5/5] Review remarks
---
.../Instrumentation/TypeSanitizer.cpp | 5 +-
...19-crash-scalar-tbaa-tag-without-offset.ll | 83 ++++++++++++++-----
2 files changed, 65 insertions(+), 23 deletions(-)
diff --git a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
index 71a95dcc1a3c2..5b758cff923f9 100644
--- a/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/TypeSanitizer.cpp
@@ -350,9 +350,8 @@ bool TypeSanitizer::generateBaseTypeDescriptor(
Offset =
mdconst::extract<ConstantInt>(MD->getOperand(i + 1))->getZExtValue();
} else if (i == 1 && MD->getNumOperands() == 2) {
- // According to the LLVM language reference, the third operand for a
- // scalar tag is actually optional, its absence indicating an offset
- // The third operand for a scalar tag is actually optional, its absence indicating an offset of zero.
+ // The third operand for a scalar tag is actually optional, its absence
+ // indicating an offset of zero.
Offset = 0;
} else {
assert(false && "Malformed TBAA MD.");
diff --git a/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll b/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll
index 815b380f2bd39..ab3d12ad57084 100644
--- a/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll
+++ b/llvm/test/Instrumentation/TypeSanitizer/2025-12-19-crash-scalar-tbaa-tag-without-offset.ll
@@ -1,30 +1,73 @@
-; RUN: opt -passes='tysan' -tysan-outline-instrumentation=false -S %s
-; RUN: opt -passes='tysan' -S %s
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes='tysan' -tysan-outline-instrumentation=false -S %s | FileCheck %s --check-prefix=CHECK-INLINE
+; RUN: opt -passes='tysan' -S %s | FileCheck %s --check-prefix=CHECK-OUTLINE
; ModuleID = 'nooffset.c'
-source_filename = "nooffset.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-pc-linux-gnu"
; Function Attrs: noinline nounwind optnone sanitize_type sspstrong uwtable
define dso_local i32 @main() #0 {
+; CHECK-INLINE-LABEL: define dso_local i32 @main() {
+; CHECK-INLINE-NEXT: [[APP_MEM_MASK:%.*]] = load i64, ptr @__tysan_app_memory_mask, align 8
+; CHECK-INLINE-NEXT: [[SHADOW_BASE:%.*]] = load i64, ptr @__tysan_shadow_memory_address, align 8
+; CHECK-INLINE-NEXT: [[TMP1:%.*]] = alloca i32, align 4
+; CHECK-INLINE-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64
+; CHECK-INLINE-NEXT: [[TMP3:%.*]] = and i64 [[TMP2]], [[APP_MEM_MASK]]
+; CHECK-INLINE-NEXT: [[TMP4:%.*]] = shl i64 [[TMP3]], 3
+; CHECK-INLINE-NEXT: [[TMP5:%.*]] = add i64 [[TMP4]], [[SHADOW_BASE]]
+; CHECK-INLINE-NEXT: [[TMP6:%.*]] = inttoptr i64 [[TMP5]] to ptr
+; CHECK-INLINE-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[TMP6]], i8 0, i64 32, i1 false)
+; CHECK-INLINE-NEXT: [[APP_PTR_INT:%.*]] = ptrtoint ptr [[TMP1]] to i64
+; CHECK-INLINE-NEXT: [[APP_PTR_MASKED:%.*]] = and i64 [[APP_PTR_INT]], [[APP_MEM_MASK]]
+; CHECK-INLINE-NEXT: [[APP_PTR_SHIFTED:%.*]] = shl i64 [[APP_PTR_MASKED]], 3
+; CHECK-INLINE-NEXT: [[SHADOW_PTR_INT:%.*]] = add i64 [[APP_PTR_SHIFTED]], [[SHADOW_BASE]]
+; CHECK-INLINE-NEXT: [[SHADOW_PTR:%.*]] = inttoptr i64 [[SHADOW_PTR_INT]] to ptr
+; CHECK-INLINE-NEXT: [[SHADOW_DESC:%.*]] = load ptr, ptr [[SHADOW_PTR]], align 8
+; CHECK-INLINE-NEXT: [[DESC_SET:%.*]] = icmp eq ptr [[SHADOW_DESC]], null
+; CHECK-INLINE-NEXT: br i1 [[DESC_SET]], label %[[SET_TYPE:.*]], label %[[BB7:.*]], !prof [[PROF0:![0-9]+]]
+; CHECK-INLINE: [[SET_TYPE]]:
+; CHECK-INLINE-NEXT: store ptr @__tysan_v1_int_o_0, ptr [[SHADOW_PTR]], align 8
+; CHECK-INLINE-NEXT: [[SHADOW_BYTE_1_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 8
+; CHECK-INLINE-NEXT: [[SHADOW_BYTE_1_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_1_OFFSET]] to ptr
+; CHECK-INLINE-NEXT: store ptr inttoptr (i64 -1 to ptr), ptr [[SHADOW_BYTE_1_PTR]], align 8
+; CHECK-INLINE-NEXT: [[SHADOW_BYTE_2_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 16
+; CHECK-INLINE-NEXT: [[SHADOW_BYTE_2_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_2_OFFSET]] to ptr
+; CHECK-INLINE-NEXT: store ptr inttoptr (i64 -2 to ptr), ptr [[SHADOW_BYTE_2_PTR]], align 8
+; CHECK-INLINE-NEXT: [[SHADOW_BYTE_3_OFFSET:%.*]] = add i64 [[SHADOW_PTR_INT]], 24
+; CHECK-INLINE-NEXT: [[SHADOW_BYTE_3_PTR:%.*]] = inttoptr i64 [[SHADOW_BYTE_3_OFFSET]] to ptr
+; CHECK-INLINE-NEXT: store ptr inttoptr (i64 -3 to ptr), ptr [[SHADOW_BYTE_3_PTR]], align 8
+; CHECK-INLINE-NEXT: br label %[[BB7]]
+; CHECK-INLINE: [[BB7]]:
+; CHECK-INLINE-NEXT: store i32 10, ptr [[TMP1]], align 4, !tbaa [[INT_TBAA1:![0-9]+]]
+; CHECK-INLINE-NEXT: ret i32 0
+;
+; CHECK-OUTLINE-LABEL: define dso_local i32 @main() {
+; CHECK-OUTLINE-NEXT: [[APP_MEM_MASK:%.*]] = load i64, ptr @__tysan_app_memory_mask, align 8
+; CHECK-OUTLINE-NEXT: [[SHADOW_BASE:%.*]] = load i64, ptr @__tysan_shadow_memory_address, align 8
+; CHECK-OUTLINE-NEXT: [[TMP1:%.*]] = alloca i32, align 4
+; CHECK-OUTLINE-NEXT: call void @__tysan_instrument_mem_inst(ptr [[TMP1]], ptr null, i64 4, i1 false)
+; CHECK-OUTLINE-NEXT: call void @__tysan_instrument_with_shadow_update(ptr [[TMP1]], ptr @__tysan_v1_int_o_0, i1 false, i64 4, i32 2)
+; CHECK-OUTLINE-NEXT: store i32 10, ptr [[TMP1]], align 4, !tbaa [[INT_TBAA0:![0-9]+]]
+; CHECK-OUTLINE-NEXT: ret i32 0
+;
%1 = alloca i32, align 4
- store i32 10, ptr %1, align 4, !tbaa !6
+ store i32 10, ptr %1, align 4, !tbaa !1
ret i32 0
}
-attributes #0 = { noinline nounwind optnone sanitize_type sspstrong uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-
-!llvm.module.flags = !{!0, !1, !2, !3, !4}
-!llvm.ident = !{!5}
-
-!0 = !{i32 1, !"wchar_size", i32 4}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 7, !"PIE Level", i32 2}
-!3 = !{i32 7, !"uwtable", i32 2}
-!4 = !{i32 7, !"frame-pointer", i32 2}
-!5 = !{!"clang version 21.1.5"}
-!6 = !{!7, !7, i64 0}
-!7 = !{!"int", !8} ; According to the LLVM language reference, the third argument is optional
-!8 = !{!"omnipotent char", !9, i64 0}
-!9 = !{!"Simple C/C++ TBAA"}
+!1 = !{!2, !2, i64 0}
+!2 = !{!"int", !3} ; According to the LLVM language reference, the third argument is optional
+!3 = !{!"omnipotent char", !4, i64 0}
+!4 = !{!"Simple C/C++ TBAA"}
+;.
+; CHECK-INLINE: [[PROF0]] = !{!"branch_weights", i32 1, i32 100000}
+; CHECK-INLINE: [[INT_TBAA1]] = !{[[META2:![0-9]+]], [[META2]], i64 0}
+; CHECK-INLINE: [[META2]] = !{!"int", [[META3:![0-9]+]]}
+; CHECK-INLINE: [[META3]] = !{!"omnipotent char", [[META4:![0-9]+]], i64 0}
+; CHECK-INLINE: [[META4]] = !{!"Simple C/C++ TBAA"}
+;.
+; CHECK-OUTLINE: [[INT_TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
+; CHECK-OUTLINE: [[META1]] = !{!"int", [[META2:![0-9]+]]}
+; CHECK-OUTLINE: [[META2]] = !{!"omnipotent char", [[META3:![0-9]+]], i64 0}
+; CHECK-OUTLINE: [[META3]] = !{!"Simple C/C++ TBAA"}
+;.
More information about the llvm-commits
mailing list