[compiler-rt] [compiler-rt][TySan] Use pointer-width types for shadow memory ops (PR #191602)

Brian Cain via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 10 21:57:33 PDT 2026


https://github.com/androm3da created https://github.com/llvm/llvm-project/pull/191602

The TySan runtime used uint64_t/int64_t casts for shadow memory pointer arithmetic and interior-byte marker values. These are incorrect on 32-bit targets where pointers are 4 bytes: the shadow entries are pointer-sized, so the offsets and marker values must also be pointer-sized.

Replace uint64_t/int64_t with uptr/sptr (sanitizer_common's pointer-width typedefs) throughout SetShadowType, GetNotAllBadTD, GetNotAllUnkTD, and __tysan_instrument_mem_inst. This is a no-op on 64-bit targets (where uptr == uint64_t) and fixes shadow corruption on 32-bit targets.

>From 4bbe6faef7b029ebab4e85ea27a4fc6485e3a582 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Fri, 10 Apr 2026 21:42:56 -0700
Subject: [PATCH] [compiler-rt][TySan] Use pointer-width types for shadow
 memory operations

The TySan runtime used uint64_t/int64_t casts for shadow memory pointer
arithmetic and interior-byte marker values. These are incorrect on
32-bit targets where pointers are 4 bytes: the shadow entries are
pointer-sized, so the offsets and marker values must also be
pointer-sized.

Replace uint64_t/int64_t with uptr/sptr (sanitizer_common's
pointer-width typedefs) throughout SetShadowType, GetNotAllBadTD,
GetNotAllUnkTD, and __tysan_instrument_mem_inst. This is a no-op on
64-bit targets (where uptr == uint64_t) and fixes shadow corruption
on 32-bit targets.
---
 compiler-rt/lib/tysan/tysan.cpp | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp
index 52f941180b8eb..7c1cf193d3cb6 100644
--- a/compiler-rt/lib/tysan/tysan.cpp
+++ b/compiler-rt/lib/tysan/tysan.cpp
@@ -265,33 +265,33 @@ static void SetShadowType(tysan_type_descriptor *td,
                           tysan_type_descriptor **shadowData,
                           uint64_t AccessSize) {
   *shadowData = td;
-  uint64_t shadowDataInt = (uint64_t)shadowData;
+  uptr shadowDataInt = (uptr)shadowData;
 
   for (uint64_t i = 1; i < AccessSize; ++i) {
-    int64_t dataOffset = i << PtrShift();
-    int64_t *badShadowData = (int64_t *)(shadowDataInt + dataOffset);
-    int64_t badTD = int64_t(i) * -1;
+    uptr dataOffset = i << PtrShift();
+    sptr *badShadowData = (sptr *)(shadowDataInt + dataOffset);
+    sptr badTD = sptr(i) * -1;
     *badShadowData = badTD;
   }
 }
 
 ALWAYS_INLINE
-static bool GetNotAllBadTD(uint64_t ShadowDataInt, uint64_t AccessSize) {
+static bool GetNotAllBadTD(uptr ShadowDataInt, uint64_t AccessSize) {
   bool notAllBadTD = false;
   for (uint64_t i = 1; i < AccessSize; ++i) {
-    int64_t **unkShadowData = (int64_t **)(ShadowDataInt + (i << PtrShift()));
-    int64_t *ILdTD = *unkShadowData;
+    sptr **unkShadowData = (sptr **)(ShadowDataInt + (i << PtrShift()));
+    sptr *ILdTD = *unkShadowData;
     notAllBadTD = notAllBadTD || (ILdTD != nullptr);
   }
   return notAllBadTD;
 }
 
 ALWAYS_INLINE
-static bool GetNotAllUnkTD(uint64_t ShadowDataInt, uint64_t AccessSize) {
+static bool GetNotAllUnkTD(uptr ShadowDataInt, uint64_t AccessSize) {
   bool notAllBadTD = false;
   for (uint64_t i = 1; i < AccessSize; ++i) {
-    int64_t *badShadowData = (int64_t *)(ShadowDataInt + (i << PtrShift()));
-    int64_t ILdTD = *badShadowData;
+    sptr *badShadowData = (sptr *)(ShadowDataInt + (i << PtrShift()));
+    sptr ILdTD = *badShadowData;
     notAllBadTD = notAllBadTD || (ILdTD >= 0);
   }
   return notAllBadTD;
@@ -307,9 +307,8 @@ __tysan_instrument_mem_inst(char *dest, char *src, uint64_t size,
     return;
   }
 
-  uint64_t srcInt = (uint64_t)src;
-  uint64_t srcShadowInt = ((srcInt & AppMask()) << PtrShift()) + ShadowAddr();
-  uint64_t *srcShadow = (uint64_t *)srcShadowInt;
+  uptr srcShadowInt = ((((uptr)src) & AppMask()) << PtrShift()) + ShadowAddr();
+  void *srcShadow = (void *)srcShadowInt;
 
   if (needsMemMove) {
     internal_memmove((char *)destShadowDataPtr, srcShadow, size << PtrShift());
@@ -389,7 +388,7 @@ __tysan_instrument_with_shadow_update(void *ptr, tysan_type_descriptor *td,
       if (shadowIsNull) {
         // We're about to set the type. Make sure that all bytes in the value
         // are also of unknown type.
-        bool isAllUnknownTD = GetNotAllUnkTD((uint64_t)shadowData, accessSize);
+        bool isAllUnknownTD = GetNotAllUnkTD((uptr)shadowData, accessSize);
         if (isAllUnknownTD) {
           GET_CALLER_PC_BP_SP;
           __tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);
@@ -402,7 +401,7 @@ __tysan_instrument_with_shadow_update(void *ptr, tysan_type_descriptor *td,
     } else {
       // We appear to have the right type. Make sure that all other bytes in
       // the type are still marked as interior bytes. If not, call the runtime.
-      bool isNotAllBadTD = GetNotAllBadTD((uint64_t)shadowData, accessSize);
+      bool isNotAllBadTD = GetNotAllBadTD((uptr)shadowData, accessSize);
       if (isNotAllBadTD) {
         GET_CALLER_PC_BP_SP;
         __tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);



More information about the llvm-commits mailing list