Skip to content

Conversation

@anoopkg6
Copy link
Contributor

Add SystemZ specific changes for dataflow sanitizer on top of following two common code changes
i) Fix Endianness issue #162881
ii) Fix ShadowAddress computation #162864

See conversation in original pr##162195

@llvmbot llvmbot added compiler-rt clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' compiler-rt:sanitizer llvm:transforms labels Nov 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 21, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-clang-driver

Author: None (anoopkg6)

Changes

Add SystemZ specific changes for dataflow sanitizer on top of following two common code changes
i) Fix Endianness issue #162881
ii) Fix ShadowAddress computation #162864

See conversation in original pr##162195


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

11 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/Linux.cpp (+1-1)
  • (modified) compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake (+2-1)
  • (modified) compiler-rt/lib/dfsan/dfsan_allocator.cpp (+16)
  • (modified) compiler-rt/lib/dfsan/dfsan_custom.cpp (+13)
  • (modified) compiler-rt/lib/dfsan/dfsan_platform.h (+14)
  • (modified) compiler-rt/test/dfsan/custom.cpp (+1-1)
  • (modified) compiler-rt/test/dfsan/lit.cfg.py (+6-1)
  • (modified) compiler-rt/test/dfsan/origin_endianness.c (+2-2)
  • (modified) compiler-rt/test/dfsan/pair.cpp (+24-12)
  • (modified) compiler-rt/test/dfsan/struct.c (+6-6)
  • (modified) llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp (+11)
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index 020e7465548fe..eb2330e35d003 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -914,7 +914,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
   Res |= SanitizerKind::KernelAddress;
   Res |= SanitizerKind::Vptr;
   Res |= SanitizerKind::SafeStack;
-  if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64)
+  if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64 || IsSystemZ)
     Res |= SanitizerKind::DataFlow;
   if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 ||
       IsRISCV64 || IsSystemZ || IsHexagon || IsLoongArch64)
diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
index ca45d7bd2af7f..2bc695922ef2d 100644
--- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
+++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
@@ -35,7 +35,8 @@ set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
     ${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
     ${LOONGARCH64})
 set(ALL_ASAN_ABI_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM64_32})
-set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64})
+set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64}
+    ${S390X})
 set(ALL_RTSAN_SUPPORTED_ARCH ${X86_64} ${ARM64})
 
 if(ANDROID)
diff --git a/compiler-rt/lib/dfsan/dfsan_allocator.cpp b/compiler-rt/lib/dfsan/dfsan_allocator.cpp
index 160b1a64d8f6f..700b2fccf9f6c 100644
--- a/compiler-rt/lib/dfsan/dfsan_allocator.cpp
+++ b/compiler-rt/lib/dfsan/dfsan_allocator.cpp
@@ -44,9 +44,24 @@ struct DFsanMapUnmapCallback {
 // duplicated as MappingDesc::ALLOCATOR in dfsan_platform.h.
 #if defined(__aarch64__)
 const uptr kAllocatorSpace = 0xE00000000000ULL;
+#elif defined(__s390x__)
+const uptr kAllocatorSpace = 0x440000000000ULL;
 #else
 const uptr kAllocatorSpace = 0x700000000000ULL;
 #endif
+#if defined(__s390x__)
+const uptr kMaxAllowedMallocSize = 2UL << 30;  // 2G
+
+struct AP64 {  // Allocator64 parameters. Deliberately using a short name.
+  static const uptr kSpaceBeg = kAllocatorSpace;
+  static const uptr kSpaceSize = 0x020000000000;  // 2T.
+  static const uptr kMetadataSize = sizeof(Metadata);
+  using SizeClassMap = DefaultSizeClassMap;
+  using MapUnmapCallback = DFsanMapUnmapCallback;
+  static const uptr kFlags = 0;
+  using AddressSpaceView = LocalAddressSpaceView;
+};
+#else
 const uptr kMaxAllowedMallocSize = 1ULL << 40;
 
 struct AP64 {  // Allocator64 parameters. Deliberately using a short name.
@@ -59,6 +74,7 @@ struct AP64 {  // Allocator64 parameters. Deliberately using a short name.
   using AddressSpaceView = LocalAddressSpaceView;
 };
 
+#endif
 typedef SizeClassAllocator64<AP64> PrimaryAllocator;
 
 typedef CombinedAllocator<PrimaryAllocator> Allocator;
diff --git a/compiler-rt/lib/dfsan/dfsan_custom.cpp b/compiler-rt/lib/dfsan/dfsan_custom.cpp
index dbc00d7ac3ea3..b060e5c56edbe 100644
--- a/compiler-rt/lib/dfsan/dfsan_custom.cpp
+++ b/compiler-rt/lib/dfsan/dfsan_custom.cpp
@@ -2332,7 +2332,20 @@ static int format_buffer(char *str, size_t size, const char *fmt,
         case 'g':
         case 'G':
           if (*(formatter.fmt_cur - 1) == 'L') {
+#if defined(__s390x__)
+            // SystemZ treats float128 argument as an aggregate type and copies
+            // shadow and Origin to passed argument temporary. But passed
+            // argument va_labels and va_origins are zero. Here. we get
+            // Shadow/Origin corresponding to in-memory argument and update
+            // va_labels and va_origins.
+            long double* arg = va_arg(ap, long double*);
+            *va_labels = *shadow_for(arg);
+            if (va_origins != nullptr)
+              *va_origins = *origin_for(arg);
+            retval = formatter.format(*arg);
+#else
             retval = formatter.format(va_arg(ap, long double));
+#endif
           } else {
             retval = formatter.format(va_arg(ap, double));
           }
diff --git a/compiler-rt/lib/dfsan/dfsan_platform.h b/compiler-rt/lib/dfsan/dfsan_platform.h
index 01f0de47d960d..7e6a1dafddb50 100644
--- a/compiler-rt/lib/dfsan/dfsan_platform.h
+++ b/compiler-rt/lib/dfsan/dfsan_platform.h
@@ -67,6 +67,20 @@ const MappingDesc kMemoryLayout[] = {
 };
 #    define MEM_TO_SHADOW(mem) ((uptr)mem ^ 0xB00000000000ULL)
 #    define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x200000000000ULL)
+#  elif SANITIZER_LINUX && SANITIZER_S390_64
+const MappingDesc kMemoryLayout[] = {
+    {0x000000000000ULL, 0x040000000000ULL, MappingDesc::APP, "low memory"},
+    {0x040000000000ULL, 0x080000000000ULL, MappingDesc::INVALID, "invalid"},
+    {0x080000000000ULL, 0x180000000000ULL, MappingDesc::SHADOW, "shadow"},
+    {0x180000000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"},
+    {0x1C0000000000ULL, 0x2C0000000000ULL, MappingDesc::ORIGIN, "origin"},
+    {0x2C0000000000ULL, 0x440000000000ULL, MappingDesc::INVALID, "invalid"},
+    {0x440000000000ULL, 0x460000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
+    {0x460000000000ULL, 0x500000000000ULL, MappingDesc::APP, "high memory"}};
+
+#    define MEM_TO_SHADOW(mem) \
+      ((((uptr)(mem)) & ~0xC00000000000ULL) + 0x080000000000ULL)
+#    define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x140000000000ULL)
 
 #  else
 // All of the following configurations are supported.
diff --git a/compiler-rt/test/dfsan/custom.cpp b/compiler-rt/test/dfsan/custom.cpp
index 873af5cd934e2..b4d6b186cb61e 100644
--- a/compiler-rt/test/dfsan/custom.cpp
+++ b/compiler-rt/test/dfsan/custom.cpp
@@ -2240,7 +2240,7 @@ void test_sscanf() {
   strcpy(input_buf, "-559038737");
   test_sscanf_chunk(-559038737, "%d", input_ptr, 1);
   strcpy(input_buf, "3735928559");
-  test_sscanf_chunk(3735928559, "%u", input_ptr, 1);
+  test_sscanf_chunk(3735928559, "%lu", input_ptr, 1);
   strcpy(input_buf, "12345");
   test_sscanf_chunk(12345, "%i", input_ptr, 1);
   strcpy(input_buf, "0751");
diff --git a/compiler-rt/test/dfsan/lit.cfg.py b/compiler-rt/test/dfsan/lit.cfg.py
index b26ff3e367942..1b4ca6a258bba 100644
--- a/compiler-rt/test/dfsan/lit.cfg.py
+++ b/compiler-rt/test/dfsan/lit.cfg.py
@@ -10,6 +10,8 @@
 
 # Setup default compiler flags used with -fsanitize=dataflow option.
 clang_dfsan_cflags = ["-fsanitize=dataflow"] + [config.target_cflags]
+if config.target_arch == "s390x":
+    clang_dfsan_cflags.append("-mbackchain")
 
 clang_dfsan_cxxflags = config.cxx_mode_flags + clang_dfsan_cflags
 
@@ -25,5 +27,8 @@ def build_invocation(compile_flags):
 config.suffixes = [".c", ".cpp"]
 
 # DataFlowSanitizer tests are currently supported on Linux only.
-if not (config.target_os in ["Linux"] and config.target_arch in ["aarch64", "x86_64", "loongarch64"]):
+if not (
+    config.target_os in ["Linux"]
+    and config.target_arch in ["aarch64", "x86_64", "loongarch64", "s390x"]
+):
     config.unsupported = True
diff --git a/compiler-rt/test/dfsan/origin_endianness.c b/compiler-rt/test/dfsan/origin_endianness.c
index a73dcda080e79..cd0b198017f57 100644
--- a/compiler-rt/test/dfsan/origin_endianness.c
+++ b/compiler-rt/test/dfsan/origin_endianness.c
@@ -16,10 +16,10 @@ __attribute__((noinline)) FULL_TYPE foo(FULL_TYPE a, FULL_TYPE b) {
 int main(int argc, char *argv[]) {
   FULL_TYPE a = 1;
   FULL_TYPE b = 10;
-  dfsan_set_label(4, (HALF_TYPE *)&a, sizeof(HALF_TYPE));
+  dfsan_set_label(4, (HALF_TYPE *)&a + 1, sizeof(HALF_TYPE));
   FULL_TYPE c = foo(a, b);
   dfsan_print_origin_trace(&c, NULL);
-  dfsan_print_origin_trace((HALF_TYPE *)&c, NULL);
+  dfsan_print_origin_trace((HALF_TYPE *)&c + 1, NULL);
 }
 
 // CHECK: Taint value 0x4 {{.*}} origin tracking ()
diff --git a/compiler-rt/test/dfsan/pair.cpp b/compiler-rt/test/dfsan/pair.cpp
index 94bbfc72bc649..cb00339e81ec4 100644
--- a/compiler-rt/test/dfsan/pair.cpp
+++ b/compiler-rt/test/dfsan/pair.cpp
@@ -66,8 +66,10 @@ void test_simple_constructors() {
   int *ptr1 = pair1.first;
 
 #ifdef O0
-  assert(dfsan_read_label(&i1, sizeof(i1)) == 10);
-  assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 10);
+  assert(dfsan_read_label(&i1, sizeof(i1)) == 8 ||
+         dfsan_read_label(&i1, sizeof(i1)) == 10);
+  assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 2 ||
+         dfsan_read_label(&ptr1, sizeof(ptr1)) == 10);
 #else
   assert(dfsan_read_label(&i1, sizeof(i1)) == 8);
   assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 2);
@@ -78,8 +80,10 @@ void test_simple_constructors() {
   int *ptr2 = pair2.first;
 
 #ifdef O0
-  assert(dfsan_read_label(&i2, sizeof(i2)) == 10);
-  assert(dfsan_read_label(&ptr2, sizeof(ptr2)) == 10);
+  assert(dfsan_read_label(&i2, sizeof(i2)) == 8 ||
+         dfsan_read_label(&i2, sizeof(i2)) == 10);
+  assert(dfsan_read_label(&ptr2, sizeof(ptr2)) == 2 ||
+         dfsan_read_label(&ptr2, sizeof(ptr2)) == 10);
 #else
   assert(dfsan_read_label(&i2, sizeof(i2)) == 8);
   assert(dfsan_read_label(&ptr2, sizeof(ptr2)) == 2);
@@ -90,8 +94,10 @@ void test_simple_constructors() {
   int *ptr3 = pair3.first;
 
 #ifdef O0
-  assert(dfsan_read_label(&i3, sizeof(i3)) == 10);
-  assert(dfsan_read_label(&ptr3, sizeof(ptr3)) == 10);
+  assert(dfsan_read_label(&i3, sizeof(i3)) == 8 ||
+         dfsan_read_label(&i3, sizeof(i3)) == 10);
+  assert(dfsan_read_label(&ptr3, sizeof(ptr3)) == 2 ||
+         dfsan_read_label(&ptr3, sizeof(ptr3)) == 10);
 #else
   assert(dfsan_read_label(&i3, sizeof(i3)) == 8);
   assert(dfsan_read_label(&ptr3, sizeof(ptr3)) == 2);
@@ -102,8 +108,10 @@ void test_simple_constructors() {
   int *ptr4 = pair4.first;
 
 #ifdef O0
-  assert(dfsan_read_label(&i4, sizeof(i4)) == 10);
-  assert(dfsan_read_label(&ptr4, sizeof(ptr4)) == 10);
+  assert(dfsan_read_label(&i4, sizeof(i4)) == 8 ||
+         dfsan_read_label(&i4, sizeof(i4)) == 10);
+  assert(dfsan_read_label(&ptr4, sizeof(ptr4)) == 2 ||
+         dfsan_read_label(&ptr4, sizeof(ptr4)) == 10);
 #else
   assert(dfsan_read_label(&i4, sizeof(i4)) == 8);
   assert(dfsan_read_label(&ptr4, sizeof(ptr4)) == 2);
@@ -140,8 +148,10 @@ void test_branches() {
     {
       std::pair<const char *, uint32_t> r = return_ptr_and_i32(q, res);
 #ifdef O0
-      assert(dfsan_read_label(&r.first, sizeof(r.first)) == 10);
-      assert(dfsan_read_label(&r.second, sizeof(r.second)) == 10);
+      assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2 ||
+             dfsan_read_label(&r.first, sizeof(r.first)) == 10);
+      assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8 ||
+             dfsan_read_label(&r.second, sizeof(r.second)) == 10);
 #else
       assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2);
       assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8);
@@ -151,8 +161,10 @@ void test_branches() {
     {
       std::pair<const char *, uint64_t> r = return_ptr_and_i64(q, res);
 #ifdef O0
-      assert(dfsan_read_label(&r.first, sizeof(r.first)) == 10);
-      assert(dfsan_read_label(&r.second, sizeof(r.second)) == 10);
+      assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2 ||
+             dfsan_read_label(&r.first, sizeof(r.first)) == 10);
+      assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8 ||
+             dfsan_read_label(&r.second, sizeof(r.second)) == 10);
 #else
       assert(dfsan_read_label(&r.first, sizeof(r.first)) == 2);
       assert(dfsan_read_label(&r.second, sizeof(r.second)) == 8);
diff --git a/compiler-rt/test/dfsan/struct.c b/compiler-rt/test/dfsan/struct.c
index 7ba0016f7beee..48285e022a98a 100644
--- a/compiler-rt/test/dfsan/struct.c
+++ b/compiler-rt/test/dfsan/struct.c
@@ -48,8 +48,8 @@ int main(void) {
   dfsan_label i1_label = dfsan_read_label(&i1, sizeof(i1));
   dfsan_label ptr1_label = dfsan_read_label(&ptr1, sizeof(ptr1));
 #if defined(O0)
-  assert(i1_label == (i_label | ptr_label));
-  assert(ptr1_label == (i_label | ptr_label));
+  assert(i1_label == i_label || i1_label == (i_label | ptr_label));
+  assert(ptr1_label == ptr_label || ptr1_label == (i_label | ptr_label));
 #else
   assert(i1_label == i_label);
   assert(ptr1_label == ptr_label);
@@ -62,8 +62,8 @@ int main(void) {
   dfsan_label i2_label = dfsan_read_label(&i2, sizeof(i2));
   dfsan_label ptr2_label = dfsan_read_label(&ptr2, sizeof(ptr2));
 #if defined(O0)
-  assert(i2_label == (i_label | ptr_label));
-  assert(ptr2_label == (i_label | ptr_label));
+  assert(i2_label == i_label || i2_label == (i_label | ptr_label));
+  assert(ptr2_label == ptr_label || ptr2_label == (i_label | ptr_label));
 #else
   assert(i2_label == i_label);
   assert(ptr2_label == ptr_label);
@@ -76,8 +76,8 @@ int main(void) {
   dfsan_label i3_label = dfsan_read_label(&i3, sizeof(i3));
   dfsan_label ptr3_label = dfsan_read_label(&ptr3, sizeof(ptr3));
 #if defined(O0)
-  assert(i3_label == (i_label | ptr_label));
-  assert(ptr3_label == (i_label | ptr_label));
+  assert(i3_label == i_label || i3_label == (i_label | ptr_label));
+  assert(ptr3_label == ptr_label || ptr3_label == (i_label | ptr_label));
 #else
   assert(i3_label == i_label);
   assert(ptr3_label == ptr_label);
diff --git a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
index e984ac46fca4a..7f63bf6b4941f 100644
--- a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
@@ -318,6 +318,14 @@ const MemoryMapParams Linux_LoongArch64_MemoryMapParams = {
     0x100000000000, // OriginBase
 };
 
+// s390x Linux
+const MemoryMapParams Linux_S390X_MemoryMapParams = {
+    0xC00000000000, // AndMask
+    0,              // XorMask (not used)
+    0x080000000000, // ShadowBase
+    0x1C0000000000, // OriginBase
+};
+
 namespace {
 
 class DFSanABIList {
@@ -1146,6 +1154,9 @@ bool DataFlowSanitizer::initializeModule(Module &M) {
   case Triple::loongarch64:
     MapParams = &Linux_LoongArch64_MemoryMapParams;
     break;
+  case Triple::systemz:
+    MapParams = &Linux_S390X_MemoryMapParams;
+    break;
   default:
     report_fatal_error("unsupported architecture");
   }

#ifdef O0
assert(dfsan_read_label(&i1, sizeof(i1)) == 10);
assert(dfsan_read_label(&ptr1, sizeof(ptr1)) == 10);
assert(dfsan_read_label(&i1, sizeof(i1)) == 8 ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC the test changes are only needed for SystemZ, and therefore make the tests unnecessarily weaker for other platforms. Is it possible to make the alternative options only allowed for SystemZ?

@github-actions
Copy link

🐧 Linux x64 Test Results

  • 192854 tests passed
  • 6189 tests skipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' compiler-rt:sanitizer compiler-rt llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants