[LTP] [PATCH v5] hugetlb/hugeshmat: Add hugeshmat06 migrated from libhugetlbfs shm-perms
Samir Mulani
samir@linux.ibm.com
Thu Aug 20 14:55:51 CEST 2026
Test shared memory behavior when multiple processes attach to a
hugepage-backed segment with different permissions.
At one point, reservation accounting of free hugepages between the
parent and child processes may become inconsistent during memory
operations. The parent creates a shared memory segment backed by
4 hugepages (permission 0640), attaches it read-write, initialises
each hugepage with a pattern (0x55), then detaches. Child processes
are forked in a loop, each reattaching the segment read-only via
SHM_RDONLY, verifying the data pattern, detaching, and exiting.
If the reservation accounting leaks, repeated read-only attaches
will exhaust the hugepage pool and shmat() will fail. The test
uses raw shmat() in each child and reports this failure as TFAIL.
Signed-off-by: Samir Mulani <samir@linux.ibm.com>
---
Changes in v5:
1. Use raw shmat() instead of SAFE_SHMAT() and report pool
exhaustion as TFAIL. HugetlbPages from /proc/<pid>/status
only tracks mapped pages and can return zero after shmdt()
even when resv_huge_pages is still leaked, so it is not a
reliable oracle for this regression. [LTP AI Reviewer]
2. Remove get_proc_hugetlb_kb() helper and the HugetlbPages
check as they are no longer needed. [LTP AI Reviewer]
3. Use IPC_PRIVATE instead of a fixed SEGMENT_KEY to avoid
accidentally reusing or destroying a pre-existing segment.
[LTP AI Reviewer]
4. Drop the deprecated [Description] tag from the /*\ block.
[LTP AI Reviewer]
Link: https://lore.kernel.org/ltp/49b1dbd9-4c51-414a-806c-1d5cc6b5e1c7@linux.ibm.com/ # v3
Link: https://lore.kernel.org/ltp/20260820042516.46631-1-samir@linux.ibm.com/ # v4
runtest/hugetlb | 1 +
testcases/kernel/mem/.gitignore | 1 +
.../mem/hugetlb/hugeshmat/hugeshmat06.c | 152 ++++++++++++++++++
3 files changed, 154 insertions(+)
create mode 100644 testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c
diff --git a/runtest/hugetlb b/runtest/hugetlb
index 6b35c1f42..1eb5c3339 100644
--- a/runtest/hugetlb
+++ b/runtest/hugetlb
@@ -49,6 +49,7 @@ hugeshmat02 hugeshmat02 -i 5
hugeshmat03 hugeshmat03 -i 5
hugeshmat04 hugeshmat04 -i 5
hugeshmat05 hugeshmat05 -i 5
+hugeshmat06 hugeshmat06 -i 5
hugeshmctl01 hugeshmctl01 -i 5
hugeshmctl02 hugeshmctl02 -i 5
diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore
index e63a6dde7..704f89d5e 100644
--- a/testcases/kernel/mem/.gitignore
+++ b/testcases/kernel/mem/.gitignore
@@ -45,6 +45,7 @@
/hugetlb/hugeshmat/hugeshmat03
/hugetlb/hugeshmat/hugeshmat04
/hugetlb/hugeshmat/hugeshmat05
+/hugetlb/hugeshmat/hugeshmat06
/hugetlb/hugeshmctl/hugeshmctl01
/hugetlb/hugeshmctl/hugeshmctl02
/hugetlb/hugeshmctl/hugeshmctl03
diff --git a/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c
new file mode 100644
index 000000000..440d85672
--- /dev/null
+++ b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2006 IBM Corporation.
+ * Author: David Gibson & Adam Litke
+ */
+
+/*\
+ * Tests shared memory behavior when multiple processes attach to a
+ * hugepage-backed segment with different permissions.
+ *
+ * The parent creates a shared memory segment (permission 0640) backed by
+ * hugepages, attaches it read-write, initialises each hugepage with a
+ * pattern (0x55), then detaches. A number of child processes are then
+ * forked; each child reattaches the segment read-only (SHM_RDONLY),
+ * verifies the expected pattern in every hugepage, detaches, and exits.
+ *
+ * The original libhugetlbfs shm-perms regression leaked resv_huge_pages
+ * on each read-only attach, eventually exhausting the hugepage reservation
+ * pool. This is detected by using raw shmat() in each child so that a
+ * pool-exhaustion failure is reported as TFAIL rather than TBROK.
+ */
+
+#include "hugetlb.h"
+#include "tst_safe_sysv_ipc.h"
+
+#define MNTPOINT "hugetlbfs/"
+#define HPAGES_IN_SEG 4
+#define PATTERN 0x55
+#define MAX_CHILDREN 128
+
+static int global_shmid = -1;
+static long hpage_size;
+static long segment_size;
+
+static void setup(void)
+{
+ int shmid;
+ char *p;
+ int i;
+
+ hpage_size = tst_get_hugepage_size();
+ if (!hpage_size)
+ tst_brk(TCONF, "Hugepages are not supported");
+
+ segment_size = HPAGES_IN_SEG * hpage_size;
+
+ /* Create the hugepage SHM segment with 0640 permissions */
+ shmid = SAFE_SHMGET(IPC_PRIVATE, segment_size,
+ IPC_CREAT | SHM_HUGETLB | 0640);
+ global_shmid = shmid;
+
+ /* Attach read-write, write a known pattern into each hugepage */
+ p = SAFE_SHMAT(shmid, NULL, 0);
+
+ for (i = 0; i < HPAGES_IN_SEG; i++)
+ memset(p + (i * hpage_size), PATTERN, hpage_size);
+
+ SAFE_SHMDT((const void *)p);
+}
+
+static void cleanup(void)
+{
+ if (global_shmid >= 0)
+ SAFE_SHMCTL(global_shmid, IPC_RMID, NULL);
+}
+
+static void run_test(void)
+{
+ int i, iterations;
+ pid_t pid;
+
+ /*
+ * Number of attach/detach cycles to exercise reservation accounting.
+ * Use tst_hugepages (reserved by the framework) but cap at
+ * MAX_CHILDREN to avoid spawning an unreasonable number of children
+ * on large-memory machines.
+ */
+ iterations = MIN((long)tst_hugepages, (long)MAX_CHILDREN);
+
+ tst_res(TINFO, "Running %d child attach/detach iterations", iterations);
+
+ for (i = 0; i < iterations; i++) {
+ pid = SAFE_FORK();
+ if (pid == 0) {
+ /* ---- child ---- */
+ char *shmaddr;
+ int j;
+
+ /*
+ * Use raw shmat() instead of SAFE_SHMAT() so that a
+ * failure caused by resv_huge_pages exhaustion is
+ * reported as TFAIL, not TBROK.
+ */
+ shmaddr = shmat(global_shmid, NULL, SHM_RDONLY);
+ if (shmaddr == (void *)-1) {
+ tst_res(TFAIL | TERRNO,
+ "Child %d: shmat() failed, hugepage reservation pool may be exhausted",
+ getpid());
+ exit(EXIT_FAILURE);
+ }
+
+ tst_res(TINFO, "Child %d attached segment successfully",
+ getpid());
+
+ /* Verify the pattern written by the parent */
+ for (j = 0; j < HPAGES_IN_SEG; j++) {
+ unsigned char val =
+ *((unsigned char *)shmaddr +
+ (j * hpage_size));
+
+ if (val != PATTERN) {
+ tst_res(TFAIL,
+ "Child %d: hugepage[%d] data mismatch: got 0x%02x, expected 0x%02x",
+ getpid(), j, (unsigned int)val, PATTERN);
+ SAFE_SHMDT((const void *)shmaddr);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ SAFE_SHMDT((const void *)shmaddr);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ /*
+ * Wait for all children. tst_reap_children() calls tst_brk(TBROK)
+ * if any child exited non-zero, so a TFAIL inside a child is
+ * automatically promoted to a test-level failure here.
+ */
+ tst_reap_children();
+
+ tst_res(TPASS,
+ "All %d children attached read-only and verified reservation accounting",
+ iterations);
+}
+
+static struct tst_test test = {
+ .needs_root = 1,
+ .mntpoint = MNTPOINT,
+ .needs_hugetlbfs = 1,
+ .needs_tmpdir = 1,
+ .forks_child = 1,
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run_test,
+ /*
+ * Request HPAGES_IN_SEG hugepages; that is all the test needs.
+ * TST_NEEDS means the test is skipped if the kernel cannot
+ * provide them.
+ */
+ .hugepages = {HPAGES_IN_SEG, TST_NEEDS},
+};
--
2.52.0
More information about the ltp
mailing list