[compiler-rt] [compiler-rt][memprof] fix off-by-one in shadow access-count accumula… (PR #208376)
David CARLIER via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 9 07:17:59 PDT 2026
https://github.com/devnexen updated https://github.com/llvm/llvm-project/pull/208376
>From 123ca1ef03e1f701e93ddeb01bd52bf39ec56a7b Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen at gmail.com>
Date: Thu, 9 Jul 2026 05:20:36 +0100
Subject: [PATCH 1/2] [compiler-rt][memprof] fix off-by-one in shadow
access-count accumulation.
GetShadowCount and GetShadowCountHistogram used an inclusive loop up to
MEM_TO_SHADOW(p + size), summing an extra adjacent cell when p + size is
granule-aligned. Use p + size - 1 to stay within the allocation.
---
compiler-rt/lib/memprof/memprof_allocator.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp
index 60f5c853f9d76..9d30d0e4ad324 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.cpp
+++ b/compiler-rt/lib/memprof/memprof_allocator.cpp
@@ -221,7 +221,7 @@ AllocatorCache *GetAllocatorCache(MemprofThreadLocalMallocStorage *ms) {
// Accumulates the access count from the shadow for the given pointer and size.
u64 GetShadowCount(uptr p, u32 size) {
u64 *shadow = (u64 *)MEM_TO_SHADOW(p);
- u64 *shadow_end = (u64 *)MEM_TO_SHADOW(p + size);
+ u64 *shadow_end = (u64 *)MEM_TO_SHADOW(p + size - 1);
u64 count = 0;
for (; shadow <= shadow_end; shadow++)
count += *shadow;
@@ -232,7 +232,7 @@ u64 GetShadowCount(uptr p, u32 size) {
// See memprof_mapping.h for an overview on histogram counters.
u64 GetShadowCountHistogram(uptr p, u32 size) {
u8 *shadow = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p);
- u8 *shadow_end = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p + size);
+ u8 *shadow_end = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p + size - 1);
u64 count = 0;
for (; shadow <= shadow_end; shadow++)
count += *shadow;
>From 3964040b8d27ede72b117ce96bb031bfac468ab0 Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen at gmail.com>
Date: Thu, 9 Jul 2026 13:17:39 +0100
Subject: [PATCH 2/2] add unit test.
Move GetShadowCount/GetShadowCountHistogram into memprof_mapping.h as
inline helpers and add a unit test covering the granule-aligned end case
that the previous off-by-one over-counted.
---
compiler-rt/lib/memprof/memprof_allocator.cpp | 21 --------
compiler-rt/lib/memprof/memprof_mapping.h | 21 ++++++++
compiler-rt/lib/memprof/tests/CMakeLists.txt | 1 +
compiler-rt/lib/memprof/tests/shadow.cpp | 51 +++++++++++++++++++
4 files changed, 73 insertions(+), 21 deletions(-)
create mode 100644 compiler-rt/lib/memprof/tests/shadow.cpp
diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp
index 9d30d0e4ad324..d11a909a02c5e 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.cpp
+++ b/compiler-rt/lib/memprof/memprof_allocator.cpp
@@ -218,27 +218,6 @@ AllocatorCache *GetAllocatorCache(MemprofThreadLocalMallocStorage *ms) {
return &ms->allocator_cache;
}
-// Accumulates the access count from the shadow for the given pointer and size.
-u64 GetShadowCount(uptr p, u32 size) {
- u64 *shadow = (u64 *)MEM_TO_SHADOW(p);
- u64 *shadow_end = (u64 *)MEM_TO_SHADOW(p + size - 1);
- u64 count = 0;
- for (; shadow <= shadow_end; shadow++)
- count += *shadow;
- return count;
-}
-
-// Accumulates the access count from the shadow for the given pointer and size.
-// See memprof_mapping.h for an overview on histogram counters.
-u64 GetShadowCountHistogram(uptr p, u32 size) {
- u8 *shadow = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p);
- u8 *shadow_end = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p + size - 1);
- u64 count = 0;
- for (; shadow <= shadow_end; shadow++)
- count += *shadow;
- return count;
-}
-
// Clears the shadow counters (when memory is allocated).
void ClearShadow(uptr addr, uptr size) {
CHECK(AddrIsAlignedByGranularity(addr));
diff --git a/compiler-rt/lib/memprof/memprof_mapping.h b/compiler-rt/lib/memprof/memprof_mapping.h
index 6da385ab3d6e2..b0d6b61caf2b5 100644
--- a/compiler-rt/lib/memprof/memprof_mapping.h
+++ b/compiler-rt/lib/memprof/memprof_mapping.h
@@ -128,6 +128,27 @@ inline bool AddrIsAlignedByGranularity(uptr a) {
return (a & (SHADOW_GRANULARITY - 1)) == 0;
}
+// Accumulates the access count from the shadow for the given pointer and size.
+inline u64 GetShadowCount(uptr p, u32 size) {
+ u64 *shadow = (u64 *)MEM_TO_SHADOW(p);
+ u64 *shadow_end = (u64 *)MEM_TO_SHADOW(p + size - 1);
+ u64 count = 0;
+ for (; shadow <= shadow_end; shadow++)
+ count += *shadow;
+ return count;
+}
+
+// Accumulates the access count from the shadow for the given pointer and size.
+// See the histogram overview above for the counter layout.
+inline u64 GetShadowCountHistogram(uptr p, u32 size) {
+ u8 *shadow = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p);
+ u8 *shadow_end = (u8 *)HISTOGRAM_MEM_TO_SHADOW(p + size - 1);
+ u64 count = 0;
+ for (; shadow <= shadow_end; shadow++)
+ count += *shadow;
+ return count;
+}
+
inline void RecordAccess(uptr a) {
// If we use a different shadow size then the type below needs adjustment.
CHECK_EQ(SHADOW_ENTRY_SIZE, 8);
diff --git a/compiler-rt/lib/memprof/tests/CMakeLists.txt b/compiler-rt/lib/memprof/tests/CMakeLists.txt
index 1603d47d019ed..282e3701ee732 100644
--- a/compiler-rt/lib/memprof/tests/CMakeLists.txt
+++ b/compiler-rt/lib/memprof/tests/CMakeLists.txt
@@ -28,6 +28,7 @@ set(MEMPROF_SOURCES
set(MEMPROF_UNITTESTS
histogram_encoding.cpp
rawprofile.cpp
+ shadow.cpp
driver.cpp)
include_directories(../../../include)
diff --git a/compiler-rt/lib/memprof/tests/shadow.cpp b/compiler-rt/lib/memprof/tests/shadow.cpp
new file mode 100644
index 0000000000000..28231914db551
--- /dev/null
+++ b/compiler-rt/lib/memprof/tests/shadow.cpp
@@ -0,0 +1,51 @@
+//===-- shadow.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "memprof/memprof_mapping.h"
+
+#include "gtest/gtest.h"
+
+// The shadow-mapping macros read this global, which the full runtime normally
+// initializes. Provide a definition so the mapping lands in the fake shadow
+// below.
+extern "C" {
+__sanitizer::uptr __memprof_shadow_memory_dynamic_address;
+}
+
+namespace __memprof {
+namespace {
+
+// A granule-aligned fake "application" address.
+static const uptr kFakeMem = 0x40000000;
+
+// Point the shadow so that MEM_TO_SHADOW(kFakeMem) == &Shadow[0].
+static void MapShadow(void *Shadow) {
+ __memprof_shadow_memory_dynamic_address =
+ reinterpret_cast<uptr>(Shadow) - (kFakeMem >> SHADOW_SCALE);
+}
+
+TEST(MemprofShadowCount, ExcludesCellPastGranuleAlignedEnd) {
+ // [0],[1] cover the 128-byte (two 64B-granule) allocation; [2] is the
+ // adjacent block's counter and must NOT be summed.
+ alignas(8) u64 Shadow[3] = {10, 20, 999};
+ MapShadow(Shadow);
+ // 128 is a multiple of MEM_GRANULARITY (64) and kFakeMem is aligned, so
+ // p + size lands exactly on the next granule boundary.
+ EXPECT_EQ(GetShadowCount(kFakeMem, 128), 30u);
+}
+
+TEST(MemprofShadowCount, HistogramExcludesCellPastGranuleAlignedEnd) {
+ // [0],[1] cover the 16-byte (two 8B-granule) allocation; [2] is adjacent.
+ alignas(8) u8 Shadow[3] = {5, 7, 200};
+ MapShadow(Shadow);
+ // 16 is a multiple of HISTOGRAM_GRANULARITY (8).
+ EXPECT_EQ(GetShadowCountHistogram(kFakeMem, 16), 12u);
+}
+
+} // namespace
+} // namespace __memprof
More information about the llvm-commits
mailing list