[LTP] [PATCH v2 10/10] syscalls/ipc: Rewrite shmctl01

Cyril Hrubis chrubis@suse.cz
Mon Sep 7 17:01:57 CEST 2020


This commit rewrites the shmctl01 and only keep testcases not covered
by the rest of the testcases.

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
Reviewed-by: Martin Doucha <mdoucha@suse.cz>
---
 testcases/kernel/syscalls/ipc/shmctl/Makefile |   1 -
 .../kernel/syscalls/ipc/shmctl/shmctl01.c     | 586 ++++++------------
 2 files changed, 178 insertions(+), 409 deletions(-)

diff --git a/testcases/kernel/syscalls/ipc/shmctl/Makefile b/testcases/kernel/syscalls/ipc/shmctl/Makefile
index 9d535014f..106b73697 100644
--- a/testcases/kernel/syscalls/ipc/shmctl/Makefile
+++ b/testcases/kernel/syscalls/ipc/shmctl/Makefile
@@ -10,7 +10,6 @@ shmctl05: LDLIBS += -lrt
 
 include $(top_srcdir)/include/mk/testcases.mk
 
-shmctl01: LTPLDLIBS = -lltpipc
 shmctl02 shmctl04 shmctl06: LTPLDLIBS = -lltpnewipc
 
 include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c
index 52bf23a40..3a39a4d74 100644
--- a/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c
+++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c
@@ -1,499 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Copyright (c) International Business Machines  Corp., 2001
- *
- * This program is free software;  you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY;  without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
- * the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program;  if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Copyright (C) 2020 Cyril Hrubis <chrubis@suse.cz>
  */
-
 /*
- * NAME
- *	shmctl01.c
+ * Verify that shmctl() IPC_STAT and SHM_STAT reports correct data.
+ *
+ * The shm_nattach is excercised by:
  *
- * DESCRIPTION
- *	shmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as
- *		   they are used with shmctl()
+ * 1. forking() children that attach and detach SHM
+ * 2. attaching the SHM before fork and letting the children detach it
  *
- * ALGORITHM
- *	loop if that option was specified
- *	create a shared memory segment with read and write permission
- *	set up any test case specific conditions
- *	call shmctl() using the TEST macro
- *	check the return code
- *	  if failure, issue a FAIL message.
- *	otherwise,
- *	  if doing functionality testing
- *		call the correct test function
- *		if the conditions are correct,
- *			issue a PASS message
- *		otherwise
- *			issue a FAIL message
- *	  otherwise
- *	    issue a PASS message
- *	call cleanup
+ * We check that the number shm_nattach is correct after each step we do.
  */
 
-#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
-#endif
-#include "ipcshm.h"
-#include "safe_macros.h"
-
-char *TCID = "shmctl01";
-
-static int shm_id_1 = -1;
-static int shm_index;
-static struct shmid_ds buf;
-static struct shminfo info;
-static long save_time;
-
-#define FIRST	0
-#define SECOND	1
-static int stat_time;
-
-static void *set_shared;
-
-#define N_ATTACH	4
-
-static pid_t pid_arr[N_ATTACH];
-
-/* Setup, cleanup and check routines for IPC_STAT */
-static void stat_setup(void), func_istat(int ret);
-static void stat_cleanup(void);
-
-/* Setup and check routines for IPC_SET */
-static void set_setup(void), func_set(int ret);
-
-/* Check routine for IPC_INFO */
-static void func_info(int ret);
-
-/* Check routine for SHM_STAT */
-static void func_sstat(int ret);
-static void func_sstat_setup(void);
-
-/* Check routine for SHM_LOCK */
-static void func_lock(int ret);
-
-/* Check routine for SHM_UNLOCK */
-static void func_unlock(int ret);
-
-/* Check routine for IPC_RMID */
-static void func_rmid(int ret);
-
-/* Child function */
-static void do_child(void);
-
-static struct test_case_t {
-	int *shmid;
-	int cmd;
-	struct shmid_ds *arg;
-	void (*func_test) (int);
-	void (*func_setup) (void);
-} TC[] = {
-	{&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
-#ifndef UCLINUX
-	    /*
-	     * The second test is not applicable to uClinux;
-	     * shared memory segments are detached on exec(),
-	     * so cannot be passed to uClinux children.
-	     */
-	{&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
-#endif
-	{&shm_id_1, IPC_SET, &buf, func_set, set_setup},
-	{&shm_id_1, IPC_INFO, (struct shmid_ds *) &info, func_info, NULL},
-	{&shm_index, SHM_STAT, &buf, func_sstat, func_sstat_setup},
-	{&shm_id_1, SHM_LOCK, NULL, func_lock, NULL},
-	{&shm_id_1, SHM_UNLOCK, NULL, func_unlock, NULL},
-	{&shm_id_1, IPC_RMID, NULL, func_rmid, NULL},
-};
+#include <stdlib.h>
+#include "tst_test.h"
+#include "tst_safe_sysv_ipc.h"
+#include "libnewipc.h"
 
-static int TST_TOTAL = ARRAY_SIZE(TC);
+#define NCHILD 20
 
-#define NEWMODE	0066
+static pid_t children[NCHILD];
 
-#ifdef UCLINUX
-#define PIPE_NAME	"shmctl01"
-static char *argv0;
-#endif
+static int shm_id;
+static int shm_idx;
+static time_t ctime_min, ctime_max;
 
-static int stat_i;
+static void *addr;
 
-int main(int argc, char *argv[])
+static void attach_child(void)
 {
-	int lc;
-	int i;
-
-	tst_parse_opts(argc, argv, NULL, NULL);
-#ifdef UCLINUX
-	argv0 = argv[0];
-	maybe_run_child(do_child, "ddd", &stat_i, &stat_time, &shm_id_1);
-#endif
-
-	setup();
-
-	for (lc = 0; TEST_LOOPING(lc); lc++) {
-		tst_count = 0;
-
-		stat_time = FIRST;
-
-		/*
-		 * Create a shared memory segment with read and write
-		 * permissions.  Do this here instead of in setup()
-		 * so that looping (-i) will work correctly.
-		 */
-		shm_id_1 = shmget(shmkey, SHM_SIZE,
-				  IPC_CREAT | IPC_EXCL | SHM_RW);
-		if (shm_id_1 == -1)
-			tst_brkm(TBROK, cleanup, "couldn't create the shared"
-				 " memory segment");
-
-		for (i = 0; i < TST_TOTAL; i++) {
-
-			/*
-			 * if needed, set up any required conditions by
-			 * calling the appropriate setup function
-			 */
-			if (TC[i].func_setup != NULL)
-				(*TC[i].func_setup) ();
-
-			TEST(shmctl(*(TC[i].shmid), TC[i].cmd, TC[i].arg));
-
-			if (TEST_RETURN == -1) {
-				tst_resm(TFAIL, "%s call failed - errno "
-					 "= %d : %s", TCID, TEST_ERRNO,
-					 strerror(TEST_ERRNO));
-				continue;
-			}
-			(*TC[i].func_test) (TEST_RETURN);
-		}
-	}
+	pause();
+
+	addr = SAFE_SHMAT(shm_id, NULL, 0);
+
+	pause();
 
-	cleanup();
-	tst_exit();
+	SAFE_SHMDT(addr);
+
+	pause();
+
+	exit(0);
 }
 
-/*
- * set_shmat() - Attach the shared memory and return the pointer.  Use
- *		 this seperate routine to avoid code duplication in
- *		 stat_setup() below.
- */
-void *set_shmat(void)
+static void detach_child(void)
 {
-	void *rval;
-
-	/* attach the shared memory */
-	rval = shmat(shm_id_1, 0, 0);
-
-	/*
-	 * if shmat() fails, the only thing we can do is
-	 * print a message to that effect.
-	 */
-	if (rval == (void *)-1) {
-		tst_resm(TBROK, "shmat() failed - %s", strerror(errno));
-		cleanup();
-	}
+	pause();
+
+	SAFE_SHMDT(addr);
 
-	return rval;
+	pause();
+
+	exit(0);
 }
 
-/*
- * stat_setup() - Set up for the IPC_STAT command with shmctl().
- *		  Make things interesting by forking some children
- *		  that will either attach or inherit the shared memory.
- */
-void stat_setup(void)
+static void fork_children(void (*child_func)(void))
 {
-	void *set_shmat();
-	pid_t pid;
-
-	/*
-	 * The first time through, let the children attach the memory.
-	 * The second time through, attach the memory first and let
-	 * the children inherit the memory.
-	 */
-
-	if (stat_time == SECOND)
-		/*
-		 * use the global "set_shared" variable here so that
-		 * it can be removed in the stat_func() routine.
-		 */
-		set_shared = set_shmat();
-
-	tst_old_flush();
-	for (stat_i = 0; stat_i < N_ATTACH; stat_i++) {
-		pid = FORK_OR_VFORK();
-		if (pid == -1)
-			tst_brkm(TBROK, cleanup, "could not fork");
-
-		if (pid == 0) {
-#ifdef UCLINUX
-			if (self_exec(argv0, "ddd", stat_i, stat_time,
-				      shm_id_1) < 0)
-				tst_brkm(TBROK, cleanup, "could not self_exec");
-#else
-			do_child();
-#endif
-
-		} else {
-			/* save the child's pid for cleanup later */
-			pid_arr[stat_i] = pid;
-			TST_PROCESS_STATE_WAIT(cleanup, pid, 'S');
-		}
+	unsigned int i;
+
+	for (i = 0; i < NCHILD; i++) {
+		pid_t pid = SAFE_FORK();
+
+		if (!pid)
+			child_func();
+
+		children[i] = pid;
 	}
 }
 
-void do_child(void)
+static void wait_for_children(void)
 {
-	void *test;
+	unsigned int i;
 
-	if (stat_time == FIRST)
-		test = set_shmat();
-	else
-		test = set_shared;
+	for (i = 0; i < NCHILD; i++)
+		TST_PROCESS_STATE_WAIT(children[i], 'S', 0);
+}
 
-	memcpy(test, &stat_i, sizeof(stat_i));
+static void signal_children(void)
+{
+	unsigned int i;
 
-	/* pause until we get a signal from stat_cleanup() */
-	pause();
+	for (i = 0; i < NCHILD; i++)
+		SAFE_KILL(children[i], SIGUSR1);
+}
 
-	/* now we're back - detach the memory and exit */
-	if (shmdt(test) == -1)
-		tst_resm(TBROK, "shmdt() failed - %d", errno);
+static void reap_children(void)
+{
+	unsigned int i;
 
-	tst_exit();
+	for (i = 0; i < NCHILD; i++)
+		SAFE_WAITPID(children[i], NULL, 0);
 }
 
-/*
- * func_istat() - check the functionality of the IPC_STAT command with shmctl()
- *		 by looking at the pid of the creator, the segement size,
- *		 the number of attaches and the mode.
- */
-void func_istat(int ret)
+static void check_nattch(int exp_nattch, const char *msg)
 {
-	int fail = 0;
-	pid_t pid;
+	struct shmid_ds ds1;
+	struct shmid_ds ds2;
 
-	/* check perm, pid, nattach and size */
+	SAFE_SHMCTL(shm_id, IPC_STAT, &ds1);
+	SAFE_SHMCTL(shm_idx, SHM_STAT, &ds2);
 
-	pid = getpid();
-
-	if (buf.shm_cpid != pid) {
-		tst_resm(TFAIL, "creator pid is incorrect");
-		fail = 1;
+	if (ds1.shm_nattch != ds2.shm_nattch) {
+		tst_res(TFAIL, "IPC_STAT nattch=%li SHM_STAT nattch=%li",
+			(long)ds1.shm_nattch, (long)ds2.shm_nattch);
+		return;
 	}
 
-	if (!fail && buf.shm_segsz != SHM_SIZE) {
-		tst_resm(TFAIL, "segment size is incorrect");
-		fail = 1;
+	if ((int)ds1.shm_nattch == exp_nattch) {
+		tst_res(TPASS, "%s shm_nattch=%i", msg, exp_nattch);
+		return;
 	}
 
-	/*
-	 * The first time through, only the children attach the memory, so
-	 * the attaches equal N_ATTACH + stat_time (0).  The second time
-	 * through, the parent attaches the memory and the children inherit
-	 * that memory so the attaches equal N_ATTACH + stat_time (1).
-	 */
-	if (!fail && buf.shm_nattch != N_ATTACH + stat_time) {
-		tst_resm(TFAIL, "# of attaches is incorrect - %ld",
-			 buf.shm_nattch);
-		fail = 1;
-	}
+	tst_res(TFAIL, "%s shm_nattcg=%li expected %i",
+	        msg, (long)ds1.shm_nattch, exp_nattch);
+}
 
-	/* use MODE_MASK to make sure we are comparing the last 9 bits */
-	if (!fail && (buf.shm_perm.mode & MODE_MASK) !=
-			((SHM_RW) & MODE_MASK)) {
-		tst_resm(TFAIL, "segment mode is incorrect");
-		fail = 1;
-	}
+static void verify_shmstat_attach(void)
+{
+	fork_children(attach_child);
+	wait_for_children();
 
-	stat_cleanup();
+	check_nattch(0, "before child shmat()");
 
-	/* save the change time for use in the next test */
-	save_time = buf.shm_ctime;
+	signal_children();
+	wait_for_children();
 
-	if (fail)
-		return;
+	check_nattch(NCHILD, "after child shmat()");
+
+	signal_children();
+	wait_for_children();
+
+	check_nattch(0, "after child shmdt()");
 
-	tst_resm(TPASS, "pid, size, # of attaches and mode are correct "
-		 "- pass #%d", stat_time);
+	signal_children();
+	reap_children();
 }
 
-/*
- * stat_cleanup() - signal the children to clean up after themselves and
- *		    have the parent make dessert, er, um, make that remove
- *		    the shared memory that is no longer needed.
- */
-void stat_cleanup(void)
+static void verify_shmstat_inherit(void)
 {
-	int i;
+	addr = SAFE_SHMAT(shm_id, NULL, 0);
 
-	/* wake up the childern so they can detach the memory and exit */
-	for (i = 0; i < N_ATTACH; i++) {
-		SAFE_KILL(cleanup, pid_arr[i], SIGUSR1);
-	}
+	fork_children(detach_child);
+	wait_for_children();
 
-	/* remove the parent's shared memory the second time through */
-	if (stat_time == SECOND) {
-		if (shmdt(set_shared) == -1)
-			tst_resm(TINFO, "shmdt() failed");
-	}
+	check_nattch(NCHILD+1, "inherited after fork()");
 
-	for (i = 0; i < N_ATTACH; i++) {
-		SAFE_WAITPID(cleanup, pid_arr[i], NULL, 0);
-	}
+	signal_children();
+	wait_for_children();
 
-	stat_time++;
-}
+	check_nattch(1, "after child shmdt()");
 
-/*
- * set_setup() - set up for the IPC_SET command with shmctl()
- */
-void set_setup(void)
-{
-	/* set up a new mode for the shared memory segment */
-	buf.shm_perm.mode = SHM_RW | NEWMODE;
+	SAFE_SHMDT(addr);
 
-	/* sleep for one second to get a different shm_ctime value */
-	sleep(1);
+	check_nattch(0, "after parent shmdt()");
+
+	signal_children();
+	reap_children();
 }
 
-/*
- * func_set() - check the functionality of the IPC_SET command with shmctl()
- */
-void func_set(int ret)
+static void check_ds(struct shmid_ds *ds, const char *desc)
 {
-	int fail = 0;
+	pid_t pid = getpid();
 
-	/* first stat the shared memory to get the new data */
-	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
-		tst_resm(TBROK, "stat failed in func_set()");
-		return;
+	if (ds->shm_segsz != SHM_SIZE) {
+		tst_res(TFAIL, "%s: shm_segsz=%zu, expected %i",
+		        desc, ds->shm_segsz, SHM_SIZE);
+	} else {
+		tst_res(TPASS, "%s: shm_segsz=%i", desc, SHM_SIZE);
 	}
 
-	if ((buf.shm_perm.mode & MODE_MASK) !=
-			((SHM_RW | NEWMODE) & MODE_MASK)) {
-		tst_resm(TFAIL, "new mode is incorrect");
-		fail = 1;
+	if (ds->shm_cpid != pid) {
+		tst_res(TFAIL, "%s: shm_cpid=%i, expected %i",
+		        desc, ds->shm_cpid, pid);
+	} else {
+		tst_res(TPASS, "%s: shm_cpid=%i", desc, pid);
 	}
 
-	if (!fail && save_time >= buf.shm_ctime) {
-		tst_resm(TFAIL, "change time is incorrect");
-		fail = 1;
+	if (ds->shm_ctime < ctime_min || ds->shm_ctime > ctime_max) {
+		tst_res(TFAIL, "%s: shm_ctime=%li, expected <%li,%li>",
+			desc, ds->shm_ctime, ctime_min, ctime_max);
+	} else {
+		tst_res(TPASS, "%s: shm_ctime=%li in range <%li,%li>",
+			desc, ds->shm_ctime, ctime_min, ctime_max);
 	}
-
-	if (fail)
-		return;
-
-	tst_resm(TPASS, "new mode and change time are correct");
 }
 
-static void func_info(int ret)
+static void shmstat_basic_check(void)
 {
-	if (info.shmmin != 1)
-		tst_resm(TFAIL, "value of shmmin is incorrect");
-	else
-		tst_resm(TPASS, "get correct shared memory limits");
-}
+	struct shmid_ds ds;
 
-static void func_sstat(int ret)
-{
-	if (ret >= 0)
-		tst_resm(TPASS, "get correct shared memory id for index: %d",
-			shm_index);
-	else
-		tst_resm(TFAIL, "shared memory id is incorrect, index: %d",
-			shm_index);
-}
+	memset(&ds, 0, sizeof(ds));
+	SAFE_SHMCTL(shm_id, IPC_STAT, &ds);
 
-static void func_sstat_setup(void)
-{
-	struct shm_info tmp;
-	int ret;
-
-	ret = shmctl(shm_id_1, SHM_INFO, (void *)&tmp);
-	if (ret < 0)
-		tst_resm(TFAIL|TERRNO, "shmctl(SHM_INFO)");
-	else
-		shm_index = ret;
-}
+	check_ds(&ds, "IPC_STAT");
 
-static void func_lock(int ret)
-{
-	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
-		tst_resm(TBROK, "stat failed in func_lock()");
-		return;
-	}
+	memset(&ds, 0, sizeof(ds));
+	SAFE_SHMCTL(shm_idx, SHM_STAT, &ds);
 
-	if (buf.shm_perm.mode & SHM_LOCKED)
-		tst_resm(TPASS, "SHM_LOCK is set");
-	else
-		tst_resm(TFAIL, "SHM_LOCK is cleared");
+	check_ds(&ds, "SHM_STAT");
 }
 
-static void func_unlock(int ret)
-{
-	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
-		tst_resm(TBROK, "stat failed in func_unlock()");
-		return;
-	}
+static struct tcase {
+	void (*func)(void);
+	const char *desc;
+} tcases[] = {
+	{shmstat_basic_check, "Basic checks"},
+	{verify_shmstat_attach, "Children attach SHM"},
+	{verify_shmstat_inherit, "Chidlren inherit SHM"},
+};
 
-	if (buf.shm_perm.mode & SHM_LOCKED)
-		tst_resm(TFAIL, "SHM_LOCK is set");
-	else
-		tst_resm(TPASS, "SHM_LOCK is cleared");
+static void verify_shmstat(unsigned int n)
+{
+	tst_res(TINFO, "%s", tcases[n].desc);
+	tcases[n].func();
 }
 
+static void dummy_sighandler(int sig)
+{
+	(void)sig;
+}
 
-/*
- * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
- */
-void func_rmid(int ret)
+static int get_shm_idx_from_id(int shm_id)
 {
-	/* Do another shmctl() - we should get EINVAL */
-	if (shmctl(shm_id_1, IPC_STAT, &buf) != -1)
-		tst_brkm(TBROK, cleanup, "shmctl succeeded on expected fail");
+	struct shm_info dummy;
+	struct shmid_ds dummy_ds;
+	int max_idx, i;
 
-	if (errno != EINVAL)
-		tst_resm(TFAIL, "returned unexpected errno %d", errno);
-	else
-		tst_resm(TPASS, "shared memory appears to be removed");
+	max_idx = SAFE_SHMCTL(shm_id, SHM_INFO, (void*)&dummy);
 
-	shm_id_1 = -1;
-}
+	for (i = 0; i <= max_idx; i++) {
+		if (shmctl(i, SHM_STAT, &dummy_ds) == shm_id)
+			return i;
+	}
 
-/*
- * sighandler() - handle signals, in this case SIGUSR1 is the only one expected
- */
-void sighandler(int sig)
-{
-	if (sig != SIGUSR1)
-		tst_resm(TBROK, "received unexpected signal %d", sig);
+	return -1;
 }
 
-void setup(void)
+static void setup(void)
 {
-	tst_sig(FORK, sighandler, cleanup);
+	ctime_min = time(NULL);
+	shm_id = SAFE_SHMGET(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | SHM_RW);
+	ctime_max = time(NULL);
+
+	shm_idx = get_shm_idx_from_id(shm_id);
 
-	TEST_PAUSE;
+	if (shm_idx < 0)
+		tst_brk(TBROK, "Failed to get shm_id to idx mapping");
 
-	tst_tmpdir();
+	tst_res(TINFO, "shm_id=%i maps to kernel index=%i", shm_id, shm_idx);
 
-	shmkey = getipckey();
+	SAFE_SIGNAL(SIGUSR1, dummy_sighandler);
 }
 
-void cleanup(void)
+static void cleanup(void)
 {
-	rm_shm(shm_id_1);
-
-	tst_rmdir();
+	if (shm_id >= 0)
+		SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
 }
+
+static struct tst_test test = {
+	.setup = setup,
+	.cleanup = cleanup,
+	.forks_child = 1,
+	.test = verify_shmstat,
+	.tcnt = ARRAY_SIZE(tcases),
+};
-- 
2.26.2



More information about the ltp mailing list