This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new f0668e112d cache: fix shm sizing on large-page Linux (#13536)
f0668e112d is described below
commit f0668e112d79e55a9b9dbb4192c85c6a5e871f2c
Author: Brian Neradt <[email protected]>
AuthorDate: Tue Aug 11 21:11:15 2026 -0500
cache: fix shm sizing on large-page Linux (#13536)
POSIX shared-memory objects on Linux retain the exact length passed
to ftruncate(), but the cache shm gates accepted any size through the
next page boundary. On 64 KiB-page systems, a foreign control layout
could therefore be treated as compatible, causing cleanup and tooling
paths to walk an untrusted stripe table and leave segments behind.
This patch requires exact shared-memory object sizes outside macOS
while preserving macOS's page-rounded allowance. It keeps the
foreign-layout test at its original size and directly covers the
platform-specific sizing contract.
Fixes: #13534
---
include/shared/cache_shm/Layout.h | 21 +++++++++++++++++----
include/shared/cache_shm/Purge.h | 7 +++----
src/iocore/cache/CacheShm.cc | 8 ++------
src/iocore/cache/unit_tests/test_CacheShm.cc | 17 +++++++++++++++--
4 files changed, 37 insertions(+), 16 deletions(-)
diff --git a/include/shared/cache_shm/Layout.h
b/include/shared/cache_shm/Layout.h
index fa6a8e6522..be370a346a 100644
--- a/include/shared/cache_shm/Layout.h
+++ b/include/shared/cache_shm/Layout.h
@@ -87,13 +87,26 @@ constexpr std::size_t CONTROL_HEADER_SIZE =
offsetof(CacheShmControl, stripes);
static_assert(CONTROL_HEADER_SIZE == 48, "the control segment header is a
frozen layout; see the comment above");
static_assert(std::is_standard_layout_v<CacheShmControl>, "the control segment
is shared across processes and builds");
-// Whether a control segment of `actual` bytes was written by *this* build;
the kernel rounds an shm object up to a page.
-// Anything larger has a stripes[] of unknown stride and must never be walked
with our layout. Shared by the attach gate,
-// the purge primitive and `traffic_ctl cache shm status` so the three cannot
drift apart.
+/// Whether @a actual is the object size this platform reports after truncating
+/// a POSIX shared-memory object to @a requested bytes.
+inline bool
+is_expected_shm_size(std::size_t actual, std::size_t requested)
+{
+#if defined(__APPLE__)
+ // macOS rounds the reported object size up to the VM page size.
+ return actual >= requested && actual <= INK_ALIGN(requested, ats_pagesize());
+#else
+ return actual == requested;
+#endif
+}
+
+// Whether a control segment of `actual` bytes was written by *this* build.
Anything else has a stripes[] of unknown
+// stride and must never be walked with our layout. Shared by the attach gate,
the purge primitive and
+// `traffic_ctl cache shm status` so the three cannot drift apart.
inline bool
is_own_control_size(std::size_t actual)
{
- return actual >= CONTROL_SIZE && actual <= INK_ALIGN(CONTROL_SIZE,
ats_pagesize());
+ return is_expected_shm_size(actual, CONTROL_SIZE);
}
// Frame the operator's middle word (e.g. "ats") as "/<word>-". The framing is
supplied here so it cannot be mis-typed:
diff --git a/include/shared/cache_shm/Purge.h b/include/shared/cache_shm/Purge.h
index 1b170b385e..39ff9966bc 100644
--- a/include/shared/cache_shm/Purge.h
+++ b/include/shared/cache_shm/Purge.h
@@ -286,10 +286,9 @@ purge_segments(const std::string &prefix)
return report;
}
- // Larger than this build's page-rounded CONTROL_SIZE means a build with a
different
- // sizeof(CacheShmControl) wrote it. The frozen header prefix is still
readable (so
- // the owner guard above applies), but stripes[] may have a different stride
entirely,
- // so its names must not drive shm_unlink.
+ // A control size this build does not accept means a build with a different
sizeof(CacheShmControl) wrote it. The frozen
+ // header prefix is still readable (so the owner guard above applies), but
stripes[] may have a different stride
+ // entirely, so its names must not drive shm_unlink.
if (magic_ok && is_own_control_size(static_cast<std::size_t>(sb.st_size))) {
unlink_table_stripes(prefix, ctrl, report.unlinked);
} else {
diff --git a/src/iocore/cache/CacheShm.cc b/src/iocore/cache/CacheShm.cc
index 157a5097b5..69569822b7 100644
--- a/src/iocore/cache/CacheShm.cc
+++ b/src/iocore/cache/CacheShm.cc
@@ -270,14 +270,10 @@ open_and_map_shm(const std::string &name, std::size_t
size, ShmAccess access, [[
return nullptr;
}
} else {
- // The kernel rounds an shm object up to a page, so accept any size in
[requested, page-up].
struct stat sb {
};
- std::size_t expected_max = INK_ALIGN(size, ats_pagesize());
- if (fstat(fd, &sb) < 0 || sb.st_size < 0 ||
static_cast<std::size_t>(sb.st_size) < size ||
- static_cast<std::size_t>(sb.st_size) > expected_max) {
- Dbg(dbg_ctl, "shm %s size mismatch (have %lld, want %zu, max %zu)",
name.c_str(), static_cast<long long>(sb.st_size), size,
- expected_max);
+ if (fstat(fd, &sb) < 0 || sb.st_size < 0 ||
!cache_shm::is_expected_shm_size(static_cast<std::size_t>(sb.st_size), size)) {
+ Dbg(dbg_ctl, "shm %s size mismatch (have %lld, want %zu)", name.c_str(),
static_cast<long long>(sb.st_size), size);
return nullptr;
}
}
diff --git a/src/iocore/cache/unit_tests/test_CacheShm.cc
b/src/iocore/cache/unit_tests/test_CacheShm.cc
index 790e484184..87380133bf 100644
--- a/src/iocore/cache/unit_tests/test_CacheShm.cc
+++ b/src/iocore/cache/unit_tests/test_CacheShm.cc
@@ -225,6 +225,19 @@ TEST_CASE("CacheShm process liveness check backs the
concurrent-attach guard", "
CHECK_FALSE(CacheShm::process_is_alive(std::numeric_limits<int>::max()));
}
+TEST_CASE("CacheShm object size matching follows platform behavior",
"[cache][shm]")
+{
+ constexpr std::size_t requested = cache_shm::CONTROL_SIZE;
+
+ CHECK(cache_shm::is_expected_shm_size(requested, requested));
+ CHECK_FALSE(cache_shm::is_expected_shm_size(requested - 1, requested));
+#if defined(__APPLE__)
+ CHECK(cache_shm::is_expected_shm_size(INK_ALIGN(requested, ats_pagesize()),
requested));
+#else
+ CHECK_FALSE(cache_shm::is_expected_shm_size(requested +
sizeof(cache_shm::StripeEntry), requested));
+#endif
+}
+
// The rest of this file needs real shm objects, unlike the layout/fingerprint
cases
// above, so it is gated the same way the feature is.
#if TS_USE_CACHE_SHM
@@ -293,8 +306,8 @@ segment_exists(const std::string &name)
return true;
}
-// The kernel rounds an shm object up to a page, so a segment shorter than
CONTROL_SIZE is not representable everywhere:
-// Apple Silicon's 16 KB page already exceeds it. -1 if the segment is gone.
+// macOS rounds a POSIX shm object up to a page, so a segment shorter than
CONTROL_SIZE is not representable there.
+// -1 if the segment is gone.
long long
segment_size(const std::string &name)
{