[compiler-rt] [tsan][go] use __builtin_memcpy for 128-bit Go atomic args buffer access (PR #213123)

Mauri de Souza Meneguzzo via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 17 05:53:45 PDT 2026


https://github.com/mauri870 updated https://github.com/llvm/llvm-project/pull/213123

>From 5b7b04f3e80967a8da97934c98a8f0ad641f8fcd Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 30 Jul 2026 16:23:47 -0300
Subject: [PATCH 1/4] [tsan][go] use __builtin_memcpy for 128-bit Go atomic
 args buffer access

The Go 128-bit atomic helpers read and write the 128-bit value
through an a128* cast of the u8* args buffer (e.g. *(a128*)(a + 8)).
Clang treats a128* as requiring 16-byte alignment and emits MOVAPS or
MOVDQA on x86_64. The Go runtime only guarantees 8-byte alignment of the args
buffer, so a+8 may not be 16-byte aligned. The misaligned MOVAPS raises
GP and causes a SIGSEGV.

Replace the direct a128* casts with __builtin_memcpy. Clang emits
MOVDQU (unaligned 128-bit move), which handles any 8-byte-aligned
address correctly. The target address of the atomic operation itself
(*(a128**)a) is always 16-byte aligned from the Go side.
---
 compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
index 6e6774eacb645..27071cbea9278 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
@@ -933,7 +933,8 @@ void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
 #  if __TSAN_HAS_INT128
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic128_load(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
-  *(a128*)(a + 8) = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a128**)a);
+  a128 ret = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a128**)a);
+  __builtin_memcpy(a + 8, &ret, sizeof(ret));
 }
 #  endif
 
@@ -950,7 +951,9 @@ void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
 #  if __TSAN_HAS_INT128
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic128_store(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
-  AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a128**)a, *(a128*)(a + 8));
+  a128 val;
+  __builtin_memcpy(&val, a + 8, sizeof(val));
+  AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a128**)a, val);
 }
 #  endif
 
@@ -1024,9 +1027,11 @@ void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic128_compare_exchange(ThreadState* thr, uptr cpc, uptr pc,
                                           u8* a) {
-  a128 cmp = *(a128*)(a + 8);
+  a128 cmp, xch;
+  __builtin_memcpy(&cmp, a + 8, sizeof(cmp));
+  __builtin_memcpy(&xch, a + 24, sizeof(xch));
   a128 cur = AtomicGoRet<OpCAS>(thr, cpc, pc, mo_acq_rel, mo_acquire,
-                                *(a128**)a, cmp, *(a128*)(a + 24));
+                                *(a128**)a, cmp, xch);
   *(bool*)(a + 40) = (cur == cmp);
 }
 #  endif

>From 8fee1c4e07204b7fa05cb85facc3a2013b7020f1 Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 17 Sep 2026 09:42:10 -0300
Subject: [PATCH 2/4] feedback from code review

---
 compiler-rt/lib/tsan/go/test.c                 | 18 ++++++++++++++++++
 .../lib/tsan/rtl/tsan_interface_atomic.cpp     |  8 ++++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
index fcd396227a4ab..dfefe5f5cf68e 100644
--- a/compiler-rt/lib/tsan/go/test.c
+++ b/compiler-rt/lib/tsan/go/test.c
@@ -34,6 +34,11 @@ void __tsan_acquire(void *thr, void *addr);
 void __tsan_release(void *thr, void *addr);
 void __tsan_release_acquire(void *thr, void *addr);
 void __tsan_release_merge(void *thr, void *addr);
+#if defined(__SIZEOF_INT128__)
+void __tsan_go_atomic128_load(void *thr, void *cpc, void *pc, char *a);
+void __tsan_go_atomic128_store(void *thr, void *cpc, void *pc, char *a);
+void __tsan_go_atomic128_compare_exchange(void *thr, void *cpc, void *pc, char *a);
+#endif
 
 void *current_proc;
 
@@ -113,6 +118,19 @@ int main(void) {
   __tsan_go_end(thr2);
   __tsan_proc_destroy(proc1);
   current_proc = proc0;
+#if defined(__SIZEOF_INT128__)
+  {
+    // Align `a` to 16 bytes so `a + 8` and `a + 24` are 8-byte aligned (8 mod 16).
+    __attribute__((aligned(16))) char a[64];
+    *(void **)(a + 0) = buf;
+    __builtin_memset(a + 8, 0x11, 16);
+    __tsan_go_atomic128_store(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+    __builtin_memset(a + 8, 0, 16);
+    __tsan_go_atomic128_load(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+    __builtin_memset(a + 24, 0x22, 16);
+    __tsan_go_atomic128_compare_exchange(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+  }
+#endif
   __tsan_fini();
   return 0;
 }
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
index 27071cbea9278..f16336deade7d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
@@ -934,7 +934,7 @@ void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic128_load(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
   a128 ret = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a128**)a);
-  __builtin_memcpy(a + 8, &ret, sizeof(ret));
+  internal_memcpy(a + 8, &ret, sizeof(ret));
 }
 #  endif
 
@@ -952,7 +952,7 @@ void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic128_store(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
   a128 val;
-  __builtin_memcpy(&val, a + 8, sizeof(val));
+  internal_memcpy(&val, a + 8, sizeof(val));
   AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a128**)a, val);
 }
 #  endif
@@ -1028,8 +1028,8 @@ SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic128_compare_exchange(ThreadState* thr, uptr cpc, uptr pc,
                                           u8* a) {
   a128 cmp, xch;
-  __builtin_memcpy(&cmp, a + 8, sizeof(cmp));
-  __builtin_memcpy(&xch, a + 24, sizeof(xch));
+  internal_memcpy(&cmp, a + 8, sizeof(cmp));
+  internal_memcpy(&xch, a + 24, sizeof(xch));
   a128 cur = AtomicGoRet<OpCAS>(thr, cpc, pc, mo_acq_rel, mo_acquire,
                                 *(a128**)a, cmp, xch);
   *(bool*)(a + 40) = (cur == cmp);

>From ab7d2fac1bf496b1bed0ef20e75caf4849f02431 Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 17 Sep 2026 09:46:38 -0300
Subject: [PATCH 3/4] clarify comment

---
 compiler-rt/lib/tsan/go/test.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
index dfefe5f5cf68e..7db59c28cd6cc 100644
--- a/compiler-rt/lib/tsan/go/test.c
+++ b/compiler-rt/lib/tsan/go/test.c
@@ -120,7 +120,8 @@ int main(void) {
   current_proc = proc0;
 #if defined(__SIZEOF_INT128__)
   {
-    // Align `a` to 16 bytes so `a + 8` and `a + 24` are 8-byte aligned (8 mod 16).
+    // Align `a` to 16 bytes, matching the alignment Go's runtime guarantees for
+    // the buffer it passes to these functions.
     __attribute__((aligned(16))) char a[64];
     *(void **)(a + 0) = buf;
     __builtin_memset(a + 8, 0x11, 16);

>From 33dad6f3ffb2e0d39b8c183f436ca1f344240ffc Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 17 Sep 2026 09:53:29 -0300
Subject: [PATCH 4/4] track memory of buf using tsan_malloc and free

---
 compiler-rt/lib/tsan/go/test.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
index 7db59c28cd6cc..1ca4cdeb6e0a3 100644
--- a/compiler-rt/lib/tsan/go/test.c
+++ b/compiler-rt/lib/tsan/go/test.c
@@ -123,6 +123,7 @@ int main(void) {
     // Align `a` to 16 bytes, matching the alignment Go's runtime guarantees for
     // the buffer it passes to these functions.
     __attribute__((aligned(16))) char a[64];
+    __tsan_malloc(thr0, (char *)&barfoo + 1, buf, 16);
     *(void **)(a + 0) = buf;
     __builtin_memset(a + 8, 0x11, 16);
     __tsan_go_atomic128_store(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
@@ -130,6 +131,7 @@ int main(void) {
     __tsan_go_atomic128_load(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
     __builtin_memset(a + 24, 0x22, 16);
     __tsan_go_atomic128_compare_exchange(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+    __tsan_free(buf, 16);
   }
 #endif
   __tsan_fini();



More information about the llvm-commits mailing list