[compiler-rt] ef84e19 - [compiler-rt][memprof] fix off-by-one in shadow access-count accumula… (#208376)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 9 09:20:00 PDT 2026
Author: David CARLIER
Date: 2026-07-09T17:19:54+01:00
New Revision: ef84e19e3fd3f13ffbc87544054e6605226dfc79
URL: https://github.com/llvm/llvm-project/commit/ef84e19e3fd3f13ffbc87544054e6605226dfc79
DIFF: https://github.com/llvm/llvm-project/commit/ef84e19e3fd3f13ffbc87544054e6605226dfc79.diff
LOG: [compiler-rt][memprof] fix off-by-one in shadow access-count accumula… (#208376)
…tion.
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.
Added:
compiler-rt/lib/memprof/tests/shadow.cpp
Modified:
compiler-rt/lib/memprof/memprof_allocator.cpp
compiler-rt/lib/memprof/memprof_mapping.h
compiler-rt/lib/memprof/tests/CMakeLists.txt
Removed:
################################################################################
diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp
index 60f5c853f9d76..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);
- 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);
- 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
diff erent 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