[LTP] [PATCH v14] semctl01: fix SEM_STAT failures under parallel LTP runs

Stephen Bertram sbertram@redhat.com
Thu Jul 30 17:01:47 CEST 2026


SEM_STAT relied on the global high index from IPC_INFO to find this
test's semaphore set. That index is not stable when other IPC tests run
concurrently, and could trigger TBROK aborts under parallel LTP runs.
Look up the index for this test's own semaphore set instead.

This also updates func_iinfo(), which no longer derives sem_index, and
func_sstat() now also verifies the semaphore data SEM_STAT returns
(sem_nsems, sem_perm.mode), not just the id used to locate it.

The per-index lookup duplicated shmctl01's get_shm_idx_from_id(). Move
the common loop into a new get_ipc_idx_from_id() helper in libs/newipc,
shared by semctl01.c and shmctl01.c behind a small per-family stat()
wrapper on each side.

Signed-off-by: Stephen Bertram <sbertram@redhat.com>
Assisted-by: Cursor
---
Changes since v13:
- Added a shared get_ipc_idx_from_id() helper in libs/newipc
  (include/tse_newipc.h, libs/newipc/tse_newipc.c) and converted
  get_sem_idx_from_id() to use it, per review feedback.
- Converted shmctl01's get_shm_idx_from_id() to use the same shared
  helper.
- Added semctl01 to LTPLDLIBS = -lltpnewipc in
  testcases/kernel/syscalls/semctl/Makefile (semctl01 now links
  against libltpnewipc.a).
- Wrapped two tst_res()/TINFO lines in semctl01.c that exceeded 80
  columns.

Test: ./kirk -w 4 -f syscalls_32 -p semctl01 -i 1000
Before changes:
Total runs:  32000
Runtime:    16m 32s
Passed:     415968
Failed:     0
Skipped:    0
Broken:     16
Warnings:   0

After changes:
Total runs:  32000
Runtime:    16m 37s
Passed:     416000
Failed:     0
Skipped:    0
Broken:     0
Warnings:   0

Test: ./kirk -w 4 -f syscalls_32 -p shmctl01 -i 1000
After changes:
Total runs:  32000
Runtime:    24m 57s
Passed:     384000
Failed:     0
Skipped:    0
Broken:     0
Warnings:   0

 include/tse_newipc.h                        |  3 +
 libs/newipc/tse_newipc.c                    | 13 ++++
 testcases/kernel/syscalls/semctl/Makefile   |  2 +-
 testcases/kernel/syscalls/semctl/semctl01.c | 76 +++++++++++++++++----
 testcases/kernel/syscalls/shmctl/shmctl01.c | 14 ++--
 5 files changed, 88 insertions(+), 20 deletions(-)

diff --git a/include/tse_newipc.h b/include/tse_newipc.h
index 1d3bbd129..2c78f22ef 100644
--- a/include/tse_newipc.h
+++ b/include/tse_newipc.h
@@ -49,4 +49,7 @@ void *probe_free_addr(const char *file, const int lineno);
 #define PROBE_FREE_ADDR() \
 	probe_free_addr(__FILE__, __LINE__)
 
+int get_ipc_idx_from_id(int id, int max_idx,
+			 int (*stat_fn)(int idx, void *buf), void *buf);
+
 #endif /* tse_newipc.h */
diff --git a/libs/newipc/tse_newipc.c b/libs/newipc/tse_newipc.c
index f7edda6b5..aee286a31 100644
--- a/libs/newipc/tse_newipc.c
+++ b/libs/newipc/tse_newipc.c
@@ -86,3 +86,16 @@ void *probe_free_addr(const char *file, const int lineno)
 
 	return addr;
 }
+
+int get_ipc_idx_from_id(int id, int max_idx,
+			int (*stat_fn)(int idx, void *buf), void *buf)
+{
+	int i;
+
+	for (i = 0; i <= max_idx; i++) {
+		if (stat_fn(i, buf) == id)
+			return i;
+	}
+
+	return -1;
+}
diff --git a/testcases/kernel/syscalls/semctl/Makefile b/testcases/kernel/syscalls/semctl/Makefile
index 0e7223998..1f1ec9c81 100644
--- a/testcases/kernel/syscalls/semctl/Makefile
+++ b/testcases/kernel/syscalls/semctl/Makefile
@@ -8,6 +8,6 @@ LTPLIBS = ipc newipc
 include $(top_srcdir)/include/mk/testcases.mk
 
 semctl06: LTPLDLIBS = -lltpipc
-semctl02 semctl03 semctl04 semctl05 semctl07 semctl08 semctl09: LTPLDLIBS = -lltpnewipc
+semctl01 semctl02 semctl03 semctl04 semctl05 semctl07 semctl08 semctl09: LTPLDLIBS = -lltpnewipc
 
 include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/semctl/semctl01.c b/testcases/kernel/syscalls/semctl/semctl01.c
index 5bd675ab6..398cb3028 100644
--- a/testcases/kernel/syscalls/semctl/semctl01.c
+++ b/testcases/kernel/syscalls/semctl/semctl01.c
@@ -210,13 +210,16 @@ static void func_rmid(void)
 
 static void func_iinfo(int hidx)
 {
-	if (hidx >= 0) {
-		sem_index = hidx;
-		tst_res(TPASS, "the highest index is correct");
-	} else {
-		sem_index = 0;
-		tst_res(TFAIL, "the highest index is incorrect");
-	}
+	/*
+	 * Return value is the highest used index. Our set is alive, so the
+	 * highest used index must be at least our index.
+	 */
+	if (hidx >= sem_index)
+		tst_res(TPASS, "IPC_INFO highest index %d >= our index %d",
+			hidx, sem_index);
+	else
+		tst_res(TFAIL, "IPC_INFO highest index %d < our index %d",
+			hidx, sem_index);
 }
 
 static void func_sinfo(void)
@@ -227,12 +230,54 @@ static void func_sinfo(void)
 		tst_res(TPASS, "number of semaphore sets is correct");
 }
 
-static void func_sstat(int semidx)
+/*
+ * SEM_STAT fills buf the same way IPC_STAT does. sem_nsems and
+ * sem_perm.mode (set via IPC_SET earlier) verify SEM_STAT's own data,
+ * not just the id already used to find sem_index.
+ */
+static void func_sstat(int semid)
 {
-	if (semidx >= 0)
-		tst_res(TPASS, "id of the semaphore set is correct");
+	if (semid != sem_id) {
+		tst_res(TFAIL, "expected sem_id %d, got %d", sem_id, semid);
+		return;
+	}
+
+	if (buf.sem_nsems == PSEMS && buf.sem_perm.mode == (SEM_RA | NEWMODE))
+		tst_res(TPASS, "id and semaphore STAT info are correct (id=%d)",
+			sem_id);
 	else
-		tst_res(TFAIL, "id of the semaphore set is incorrect");
+		tst_res(TFAIL, "nsems=%d (expected %d), mode=%o (expected %o)",
+			(int)buf.sem_nsems, PSEMS,
+			buf.sem_perm.mode, (SEM_RA | NEWMODE));
+}
+
+/*
+ * SAFE_SEMCTL() would abort the test on per-index failures.
+ */
+static int sem_stat(int idx, void *buf)
+{
+	union semun arg;
+
+	arg.buf = buf;
+	return semctl(idx, 0, SEM_STAT, arg);
+}
+
+/*
+ * SEM_STAT takes an index into the kernel's internal array, not a semid.
+ * Return the index that maps to this test's set.
+ */
+static int get_sem_idx_from_id(int id)
+{
+	struct seminfo info;
+	struct semid_ds dummy_ds;
+	union semun arg;
+	int max_idx;
+
+	arg.__buf = &info;
+	/* SEM_INFO ignores semid; but SAFE_SEMCTL requires an lvalue */
+	max_idx = SAFE_SEMCTL(id, 0, SEM_INFO, arg);
+
+	return get_ipc_idx_from_id(id, max_idx, sem_stat, &dummy_ds);
 }
 
 static struct tcases {
@@ -263,8 +308,15 @@ static void verify_semctl(unsigned int n)
 	struct tcases *tc = &tests[n];
 	int rval;
 
-	if (sem_id == -1)
+	if (sem_id == -1) {
 		sem_id = SAFE_SEMGET(IPC_PRIVATE, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA);
+		sem_index = get_sem_idx_from_id(sem_id);
+		if (sem_index < 0)
+			tst_brk(TBROK,
+				"Failed to get sem_id %d to idx mapping", sem_id);
+		tst_res(TINFO, "sem_id=%d maps to kernel index=%d",
+			sem_id, sem_index);
+	}
 	if (tc->func_setup) {
 		switch (tc->cmd) {
 		case GETNCNT:
diff --git a/testcases/kernel/syscalls/shmctl/shmctl01.c b/testcases/kernel/syscalls/shmctl/shmctl01.c
index 05aea58cc..bfc11fa39 100644
--- a/testcases/kernel/syscalls/shmctl/shmctl01.c
+++ b/testcases/kernel/syscalls/shmctl/shmctl01.c
@@ -224,20 +224,20 @@ static void dummy_sighandler(int sig)
 	(void)sig;
 }
 
+static int shm_stat(int idx, void *buf)
+{
+	return shmctl(idx, SHM_STAT, buf);
+}
+
 static int get_shm_idx_from_id(int shm_id)
 {
 	struct shm_info dummy;
 	struct shmid_ds dummy_ds;
-	int max_idx, i;
+	int max_idx;
 
 	max_idx = SAFE_SHMCTL(shm_id, SHM_INFO, (void *)&dummy);
 
-	for (i = 0; i <= max_idx; i++) {
-		if (shmctl(i, SHM_STAT, &dummy_ds) == shm_id)
-			return i;
-	}
-
-	return -1;
+	return get_ipc_idx_from_id(shm_id, max_idx, shm_stat, &dummy_ds);
 }
 
 static void setup(void)
-- 
2.55.0



More information about the ltp mailing list