[compiler-rt] [TSan] Fix potentially problematic shadow end calculations (PR #144648)
Kunqiu Chen via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 21 06:51:56 PDT 2025
https://github.com/Camsyn updated https://github.com/llvm/llvm-project/pull/144648
>From 49f9872d3fa7b476bc55386b3340322d66f90d7d Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Tue, 17 Jun 2025 15:33:15 +0800
Subject: [PATCH 1/6] [TSan] Fix potentially problematic shadow end
calculations
This is an improvement that enhances the robustness of the code.
Previously, the correct calculation of exclusive EndShadow relied on the
assumption that `addr_end % kShadowCell == 0`; however, in many current
usages, this assumption was not strictly guaranteed (although it did in
fact meet).
In addition, computing EndShadow does not require the corresponding
address to be AppMem; for example, HighAppEnd is not AppMem, but can
still be used to calculate EndShadow.
For example, for the AppMem range [0, 1), `s = MemToShadow(0)` is equal
to `MemToShadow(1)`. The previous logic would incorrectly deduce an
empty shadow range [s, s) while the correct shadow range should be
[s, s + kShadowSize * kShadowCnt) to cover all the related shadow memory
for the accessed cell.
This commit addresses this in two ways:
1. It introduces a dedicated utility function, i.e., `MemToEndShadow`,
to correctly calculate the end of a shadow memory range, accounting
for the memory cell granularity.
2. It replaces existing (and potentially incorrect) calculations of the
shadow end with this new utility function.
Additionally, the previous commit 4052de6 resolved a problem with
overestimating the shadow end; it did not consider `kShadowCell` and
could therefore lead to underestimates. This is also corrected by
utilizing the `MemToEndShadow` function.
---
.../lib/tsan/rtl/tsan_interface_java.cpp | 3 +--
compiler-rt/lib/tsan/rtl/tsan_platform.h | 18 ++++++++++++++++++
.../lib/tsan/rtl/tsan_platform_linux.cpp | 2 +-
compiler-rt/lib/tsan/rtl/tsan_rtl.cpp | 6 +++---
compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp | 9 ++++-----
5 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp
index 7c15a16388268..3e324eef9cb8d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp
@@ -122,7 +122,6 @@ void __tsan_java_move(jptr src, jptr dst, jptr size) {
DCHECK_GE(dst, jctx->heap_begin);
DCHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
DCHECK_NE(dst, src);
- DCHECK_NE(size, 0);
// Assuming it's not running concurrently with threads that do
// memory accesses and mutex operations (stop-the-world phase).
@@ -132,7 +131,7 @@ void __tsan_java_move(jptr src, jptr dst, jptr size) {
// We used to move shadow from src to dst, but the trace format does not
// support that anymore as it contains addresses of accesses.
RawShadow *d = MemToShadow(dst);
- RawShadow *dend = MemToShadow(dst + size);
+ RawShadow *dend = MemToEndShadow(dst + size);
ShadowSet(d, dend, Shadow::kEmpty);
}
diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform.h b/compiler-rt/lib/tsan/rtl/tsan_platform.h
index 354f6da6a64a1..5b589df482256 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_platform.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_platform.h
@@ -968,6 +968,24 @@ RawShadow *MemToShadow(uptr x) {
return reinterpret_cast<RawShadow *>(SelectMapping<MemToShadowImpl>(x));
}
+struct MemToEndShadowImpl {
+ template <typename Mapping>
+ static uptr Apply(uptr x) {
+ return (((x + kShadowCell - 1) &
+ ~(Mapping::kShadowMsk | (kShadowCell - 1))) ^
+ Mapping::kShadowXor) *
+ kShadowMultiplier +
+ Mapping::kShadowAdd;
+ }
+};
+
+// If addr % kShadowCell == 0, then MemToEndShadow(addr) == MemToShadow(addr)
+// Otherwise, MemToEndShadow(addr) == MemToShadow(addr) + kShadowCnt
+ALWAYS_INLINE
+RawShadow *MemToEndShadow(uptr x) {
+ return reinterpret_cast<RawShadow *>(SelectMapping<MemToEndShadowImpl>(x));
+}
+
struct MemToMetaImpl {
template <typename Mapping>
static u32 *Apply(uptr x) {
diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
index 2c55645a15479..dbf583b362359 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
@@ -195,7 +195,7 @@ static NOINLINE void MapRodata(char* buffer, uptr size) {
!segment.IsWritable() && IsAppMem(segment.start)) {
// Assume it's .rodata
char *shadow_start = (char *)MemToShadow(segment.start);
- char *shadow_end = (char *)MemToShadow(segment.end);
+ char *shadow_end = (char *)MemToEndShadow(segment.end);
for (char *p = shadow_start; p < shadow_end;
p += marker.size() * sizeof(RawShadow)) {
internal_mmap(
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index c83efec8eaca2..4afb84e89936a 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -532,7 +532,7 @@ static void StopBackgroundThread() {
void DontNeedShadowFor(uptr addr, uptr size) {
ReleaseMemoryPagesToOS(reinterpret_cast<uptr>(MemToShadow(addr)),
- reinterpret_cast<uptr>(MemToShadow(addr + size)));
+ reinterpret_cast<uptr>(MemToEndShadow(addr + size)));
}
#if !SANITIZER_GO
@@ -588,12 +588,12 @@ void MapShadow(uptr addr, uptr size) {
// CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
const uptr kPageSize = GetPageSizeCached();
uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), kPageSize);
- uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), kPageSize);
+ uptr shadow_end = RoundUpTo((uptr)MemToEndShadow(addr + size), kPageSize);
if (!MmapFixedNoReserve(shadow_begin, shadow_end - shadow_begin, "shadow"))
Die();
#else
uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), (64 << 10));
- uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), (64 << 10));
+ uptr shadow_end = RoundUpTo((uptr)MemToEndShadow(addr + size), (64 << 10));
VPrintf(2, "MapShadow for (0x%zx-0x%zx), begin/end: (0x%zx-0x%zx)\n",
addr, addr + size, shadow_begin, shadow_end);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp
index cf07686d968dc..9652cd0267a79 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp
@@ -684,16 +684,15 @@ void MemoryAccessRangeT(ThreadState* thr, uptr pc, uptr addr, uptr size) {
DCHECK(IsShadowMem(shadow_mem));
}
- RawShadow* shadow_mem_end = reinterpret_cast<RawShadow*>(
- reinterpret_cast<uptr>(shadow_mem) + size * kShadowMultiplier - 1);
- if (!IsShadowMem(shadow_mem_end)) {
- Printf("Bad shadow end addr: %p (%p)\n", shadow_mem_end,
+ RawShadow* shadow_mem_end = MemToEndShadow(addr + size);
+ if (size > 0 && !IsShadowMem(shadow_mem_end - 1)) {
+ Printf("Bad shadow end addr: %p (%p)\n", shadow_mem_end - 1,
(void*)(addr + size - 1));
Printf(
"Shadow start addr (ok): %p (%p); size: 0x%zx; kShadowMultiplier: "
"%zx\n",
shadow_mem, (void*)addr, size, kShadowMultiplier);
- DCHECK(IsShadowMem(shadow_mem_end));
+ DCHECK(IsShadowMem(shadow_mem_end - 1));
}
#endif
>From 4c59a82aa57bb5568c275b9188f12252b7817b51 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 21 Jun 2025 15:37:42 +0800
Subject: [PATCH 2/6] Fix the wrong description.
Because [beg, beg + PageSize - 1] obviously contains a full page, but
under the current implementation, if end = beg + PageSize - 1, the page
is not processed. So I concluded that the original description of
include-end was wrong and should be changed to exclude-end
---
compiler-rt/lib/sanitizer_common/sanitizer_common.h | 2 +-
compiler-rt/lib/sanitizer_common/sanitizer_linux.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index d9e7ded593feb..3f52cfcaeecaa 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -166,7 +166,7 @@ uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
// Used to check if we can map shadow memory to a fixed location.
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
-// Releases memory pages entirely within the [beg, end] address range. Noop if
+// Releases memory pages entirely within the [beg, end) address range. Noop if
// the provided range does not contain at least one entire page.
void ReleaseMemoryPagesToOS(uptr beg, uptr end);
void IncreaseTotalMmap(uptr size);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
index 6959f785990f9..05b7d2e28a610 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
@@ -130,7 +130,7 @@ bool LibraryNameIs(const char *full_name, const char *base_name);
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
-// Releases memory pages entirely within the [beg, end] address range.
+// Releases memory pages entirely within the [beg, end) address range.
// The pages no longer count toward RSS; reads are guaranteed to return 0.
// Requires (but does not verify!) that pages are MAP_PRIVATE.
inline void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) {
>From 0b868ad1655c13d7c6f3187d078877fed66f105a Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 21 Jun 2025 15:53:43 +0800
Subject: [PATCH 3/6] Add two test cases related to incorrect shadow-end
calculation
---
compiler-rt/test/tsan/java_heap_init2.cpp | 32 ++++++++++++
compiler-rt/test/tsan/munmap_clear_shadow.c | 56 +++++++++++++++++++++
2 files changed, 88 insertions(+)
create mode 100644 compiler-rt/test/tsan/java_heap_init2.cpp
create mode 100644 compiler-rt/test/tsan/munmap_clear_shadow.c
diff --git a/compiler-rt/test/tsan/java_heap_init2.cpp b/compiler-rt/test/tsan/java_heap_init2.cpp
new file mode 100644
index 0000000000000..09d2b5faf4f38
--- /dev/null
+++ b/compiler-rt/test/tsan/java_heap_init2.cpp
@@ -0,0 +1,32 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <errno.h>
+#include <sys/mman.h>
+
+int main() {
+ // Test a non-regular kHeapSize
+ // Previously __tsan_java_init failed because it encountered non-zero meta
+ // shadow for the destination.
+ size_t const kPageSize = sysconf(_SC_PAGESIZE);
+ int const kSize = kPageSize - 1;
+ jptr jheap2 = (jptr)mmap(0, kSize, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (jheap2 == (jptr)MAP_FAILED)
+ return printf("mmap failed with %d\n", errno);
+ __atomic_store_n((int*)(jheap2 + kSize - 3), 1, __ATOMIC_RELEASE);
+ // Due to the previous incorrect meta-end calculation, the following munmap
+ // did not clear the tail meta shadow.
+ munmap((void*)jheap2, kSize);
+ int const kHeapSize2 = kSize + 1;
+ jheap2 = (jptr)mmap((void*)jheap2, kHeapSize2, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (jheap2 == (jptr)MAP_FAILED)
+ return printf("second mmap failed with %d\n", errno);
+ __tsan_java_init(jheap2, kHeapSize2);
+ __tsan_java_move(jheap2, jheap2 + kHeapSize2 - 8, 8);
+ fprintf(stderr, "DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/compiler-rt/test/tsan/munmap_clear_shadow.c b/compiler-rt/test/tsan/munmap_clear_shadow.c
new file mode 100644
index 0000000000000..b64a531d9759f
--- /dev/null
+++ b/compiler-rt/test/tsan/munmap_clear_shadow.c
@@ -0,0 +1,56 @@
+// RUN: %clang_tsan %s -o %t && %run %t | FileCheck %s
+#include "test.h"
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+void __tsan_read1(void *addr);
+
+struct thread_params {
+ char *buf;
+ unsigned int size;
+};
+
+static void *thread_func(void *arg) {
+ struct thread_params *p = (struct thread_params *)arg;
+ // Access 1
+ p->buf[0] = 0x42;
+ p->buf[p->size - 1] = 0x42;
+ barrier_wait(&barrier);
+ return 0;
+}
+
+int main() {
+ const unsigned int kPageSize = sysconf(_SC_PAGESIZE);
+ // The relevant shadow memory size should be exactly multiple of kPageSize,
+ // even if Size = kPageSize - 1.
+ const unsigned int Size = kPageSize - 1;
+
+ barrier_init(&barrier, 2);
+ char *buf = (char *)mmap(NULL, Size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ assert(buf != MAP_FAILED);
+ assert(((uintptr_t)buf % kPageSize) == 0);
+
+ pthread_t t;
+ struct thread_params p = {buf, Size};
+ pthread_create(&t, 0, thread_func, &p);
+
+ barrier_wait(&barrier);
+ // Should clear all the shadow memory related to the mmaped memory.
+ munmap(buf, Size);
+
+ // If the shadow memory is cleared completely, the following read should not
+ // cause a race.
+ // CHECK-NOT: WARNING: ThreadSanitizer: data race
+ __tsan_read1(&buf[0]); // Access 2
+ __tsan_read1(&buf[Size - 1]); // Access 2
+ pthread_join(t, 0);
+
+ puts("DONE");
+
+ return 0;
+}
>From 89eb2222930c745bd6ce72038af73469dda6bf10 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 21 Jun 2025 20:45:42 +0800
Subject: [PATCH 4/6] Use RoundUp on demand instead of a new util function
We don't always need to get the real shadow end, for example, some
shadow clear, if using the real shadow end, it will cause overcleaning
(i.e., clear [0, 1) makes [1, 8) inaccessible when kShadowCell == 8).
So when we do need to get the real end, use RoundUp in place.
For example, `unmap(addr, sz)` makes `[addr + sz, addr + PageSize)`
inaccessible, so we can safely clean up the full shadow (by getting
the real shadow end).
---
.../lib/tsan/rtl/tsan_interface_java.cpp | 2 +-
compiler-rt/lib/tsan/rtl/tsan_platform.h | 18 ------------------
.../lib/tsan/rtl/tsan_platform_linux.cpp | 2 +-
compiler-rt/lib/tsan/rtl/tsan_rtl.cpp | 13 +++++++++----
compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp | 6 ++++--
5 files changed, 15 insertions(+), 26 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp
index 3e324eef9cb8d..cb4d767d903d3 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_java.cpp
@@ -131,7 +131,7 @@ void __tsan_java_move(jptr src, jptr dst, jptr size) {
// We used to move shadow from src to dst, but the trace format does not
// support that anymore as it contains addresses of accesses.
RawShadow *d = MemToShadow(dst);
- RawShadow *dend = MemToEndShadow(dst + size);
+ RawShadow *dend = MemToShadow(dst + size);
ShadowSet(d, dend, Shadow::kEmpty);
}
diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform.h b/compiler-rt/lib/tsan/rtl/tsan_platform.h
index 5b589df482256..354f6da6a64a1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_platform.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_platform.h
@@ -968,24 +968,6 @@ RawShadow *MemToShadow(uptr x) {
return reinterpret_cast<RawShadow *>(SelectMapping<MemToShadowImpl>(x));
}
-struct MemToEndShadowImpl {
- template <typename Mapping>
- static uptr Apply(uptr x) {
- return (((x + kShadowCell - 1) &
- ~(Mapping::kShadowMsk | (kShadowCell - 1))) ^
- Mapping::kShadowXor) *
- kShadowMultiplier +
- Mapping::kShadowAdd;
- }
-};
-
-// If addr % kShadowCell == 0, then MemToEndShadow(addr) == MemToShadow(addr)
-// Otherwise, MemToEndShadow(addr) == MemToShadow(addr) + kShadowCnt
-ALWAYS_INLINE
-RawShadow *MemToEndShadow(uptr x) {
- return reinterpret_cast<RawShadow *>(SelectMapping<MemToEndShadowImpl>(x));
-}
-
struct MemToMetaImpl {
template <typename Mapping>
static u32 *Apply(uptr x) {
diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
index dbf583b362359..2c55645a15479 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
@@ -195,7 +195,7 @@ static NOINLINE void MapRodata(char* buffer, uptr size) {
!segment.IsWritable() && IsAppMem(segment.start)) {
// Assume it's .rodata
char *shadow_start = (char *)MemToShadow(segment.start);
- char *shadow_end = (char *)MemToEndShadow(segment.end);
+ char *shadow_end = (char *)MemToShadow(segment.end);
for (char *p = shadow_start; p < shadow_end;
p += marker.size() * sizeof(RawShadow)) {
internal_mmap(
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index 4afb84e89936a..52a608c01163a 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -532,7 +532,7 @@ static void StopBackgroundThread() {
void DontNeedShadowFor(uptr addr, uptr size) {
ReleaseMemoryPagesToOS(reinterpret_cast<uptr>(MemToShadow(addr)),
- reinterpret_cast<uptr>(MemToEndShadow(addr + size)));
+ reinterpret_cast<uptr>(MemToShadow(addr + size)));
}
#if !SANITIZER_GO
@@ -566,13 +566,18 @@ static bool IsValidMmapRange(uptr addr, uptr size) {
return false;
}
-void UnmapShadow(ThreadState *thr, uptr addr, uptr size) {
+void UnmapShadow(ThreadState* thr, uptr addr, uptr size) {
if (size == 0 || !IsValidMmapRange(addr, size))
return;
- DontNeedShadowFor(addr, size);
+ // unmap shadow is related to semantic of mmap/munmap, so we
+ // should clear the whole shadow range, including the tail shadow
+ // while addr + size % kShadowCell != 0.
+ uptr size_for_shadow = RoundUp(addr + size, kShadowCell) - addr;
+ DontNeedShadowFor(addr, size_for_shadow);
ScopedGlobalProcessor sgp;
SlotLocker locker(thr, true);
- ctx->metamap.ResetRange(thr->proc(), addr, size, true);
+ uptr size_for_meta = RoundUp(addr + size, kMetaShadowCell) - addr;
+ ctx->metamap.ResetRange(thr->proc(), addr, size_for_meta, true);
}
#endif
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp
index 9652cd0267a79..21096a0cf831d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_access.cpp
@@ -684,8 +684,10 @@ void MemoryAccessRangeT(ThreadState* thr, uptr pc, uptr addr, uptr size) {
DCHECK(IsShadowMem(shadow_mem));
}
- RawShadow* shadow_mem_end = MemToEndShadow(addr + size);
- if (size > 0 && !IsShadowMem(shadow_mem_end - 1)) {
+ uptr size1 =
+ (RoundUpTo(addr + size, kShadowCell) - RoundDownTo(addr, kShadowCell));
+ RawShadow* shadow_mem_end = shadow_mem + size1 / kShadowCell * kShadowCnt;
+ if (!IsShadowMem(shadow_mem_end - 1)) {
Printf("Bad shadow end addr: %p (%p)\n", shadow_mem_end - 1,
(void*)(addr + size - 1));
Printf(
>From faea01ff3a490ddc66d2b62bb56c0953c529ff7d Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 21 Jun 2025 20:50:27 +0800
Subject: [PATCH 5/6] add some assertions
---
compiler-rt/lib/tsan/rtl/tsan_rtl.cpp | 10 ++++++++++
compiler-rt/lib/tsan/rtl/tsan_sync.cpp | 14 ++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index 52a608c01163a..8ef59ef733790 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -582,6 +582,16 @@ void UnmapShadow(ThreadState* thr, uptr addr, uptr size) {
#endif
void MapShadow(uptr addr, uptr size) {
+ // Although named MapShadow, this function's semantic is unrelated to
+ // UnmapShadow. This function currently only used for Go's lazy allocation
+ // of shadow, whose targets are program section (e.g., bss, data, etc.).
+ // Therefore, we can guarantee that the addr and size align to kShadowCell
+ // and kMetaShadowCell by the following assertions.
+ DCHECK_EQ(addr % kShadowCell, 0);
+ DCHECK_EQ(size % kShadowCell, 0);
+ DCHECK_EQ(addr % kMetaShadowCell, 0);
+ DCHECK_EQ(size % kMetaShadowCell, 0);
+
// Ensure thead registry lock held, so as to synchronize
// with DoReset, which also access the mapped_shadow_* ctxt fields.
ThreadRegistryLock lock0(&ctx->thread_registry);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_sync.cpp b/compiler-rt/lib/tsan/rtl/tsan_sync.cpp
index 09d41780d188a..d1cfe2ab075ea 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_sync.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_sync.cpp
@@ -246,6 +246,20 @@ void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
// there are no concurrent accesses to the regions (e.g. stop-the-world).
CHECK_NE(src, dst);
CHECK_NE(sz, 0);
+
+ // The current MoveMemory implementation behaves incorrectly when src, dst,
+ // and sz are not aligned to kMetaShadowCell.
+ // For example, with kMetaShadowCell == 8:
+ // - src = 4: unexpectedly clears the metadata for the range [0, 4).
+ // - src = 16, dst = 4, size = 8: A sync variable for addr = 20, which should
+ // be moved to the metadata for address 8, is incorrectly moved to the
+ // metadata for address 0 instead.
+ // - src = 0, sz = 4: fails to move the tail metadata.
+ // Therefore, the following assertions is needed.
+ DCHECK_EQ(src % kMetaShadowCell, 0);
+ DCHECK_EQ(dst % kMetaShadowCell, 0);
+ DCHECK_EQ(sz % kMetaShadowCell, 0);
+
uptr diff = dst - src;
u32 *src_meta = MemToMeta(src);
u32 *dst_meta = MemToMeta(dst);
>From 1d9f6329451b20381941874b2c9d6750e03807e7 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Sat, 21 Jun 2025 21:51:01 +0800
Subject: [PATCH 6/6] Revert "Fix the wrong description."
This reverts commit 4c59a82aa57bb5568c275b9188f12252b7817b51.
Reason: move this unrelated change to another NFC PR.
---
compiler-rt/lib/sanitizer_common/sanitizer_common.h | 2 +-
compiler-rt/lib/sanitizer_common/sanitizer_linux.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index 3f52cfcaeecaa..d9e7ded593feb 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -166,7 +166,7 @@ uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
// Used to check if we can map shadow memory to a fixed location.
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
-// Releases memory pages entirely within the [beg, end) address range. Noop if
+// Releases memory pages entirely within the [beg, end] address range. Noop if
// the provided range does not contain at least one entire page.
void ReleaseMemoryPagesToOS(uptr beg, uptr end);
void IncreaseTotalMmap(uptr size);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
index 05b7d2e28a610..6959f785990f9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
@@ -130,7 +130,7 @@ bool LibraryNameIs(const char *full_name, const char *base_name);
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
-// Releases memory pages entirely within the [beg, end) address range.
+// Releases memory pages entirely within the [beg, end] address range.
// The pages no longer count toward RSS; reads are guaranteed to return 0.
// Requires (but does not verify!) that pages are MAP_PRIVATE.
inline void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) {
More information about the llvm-commits
mailing list