[LTP] [PATCH v4 4/6] Refactor mqns_04 using new LTP API

Andrea Cervesato andrea.cervesato@suse.com
Wed Mar 1 15:03:51 CET 2023


Hi,

On 2/28/23 16:11, Richard Palethorpe wrote:
> Hello,
>
> Andrea Cervesato via ltp <ltp@lists.linux.it> writes:
>
>> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
>> ---
>>   runtest/containers                         |   4 +-
>>   testcases/kernel/containers/mqns/mqns_04.c | 273 +++++++++------------
>>   2 files changed, 122 insertions(+), 155 deletions(-)
>>
>> diff --git a/runtest/containers b/runtest/containers
>> index 1cc6c838d..318113e19 100644
>> --- a/runtest/containers
>> +++ b/runtest/containers
>> @@ -24,8 +24,8 @@ mqns_02_unshare mqns_02 -m unshare
>>   mqns_03 mqns_03
>>   mqns_03_clone mqns_03 -m clone
>>   mqns_03_unshare mqns_03 -m unshare
>> -mqns_04 mqns_04
>> -mqns_04_clone mqns_04 -clone
>> +mqns_04_clone mqns_04 -m clone
>> +mqns_04_unshare mqns_04 -m unshare
>>   
>>   netns_netlink netns_netlink
>>   netns_breakns_ip_ipv4_netlink netns_breakns.sh
>> diff --git a/testcases/kernel/containers/mqns/mqns_04.c b/testcases/kernel/containers/mqns/mqns_04.c
>> index d07a85c04..93df61ec4 100644
>> --- a/testcases/kernel/containers/mqns/mqns_04.c
>> +++ b/testcases/kernel/containers/mqns/mqns_04.c
>> @@ -1,187 +1,154 @@
>> +// SPDX-License-Identifier: GPL-2.0
>>   /*
>> -* Copyright (c) International Business Machines Corp., 2009
>> -* 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
>> -*
>> -* Author: Serge Hallyn <serue@us.ibm.com>
>> -*
>> -* Check mqueuefs lifetime
>> -* . parent creates /dev/mqueue2
>> -* . child mounts mqueue there
>> -* . child does mq_open("/ab")
>> -* . parent checks for /dev/mqueue2
>> -* . child exits
>> -* . parent checks for /dev/mqueue2
>> -* . parent tries 'touch /dev/mqueue2/dd' -> should fail
>> -* . parent umounts /dev/mqueue2
>> -
>> -***************************************************************************/
>> -
>> -#ifndef _GNU_SOURCE
>> + * Copyright (c) International Business Machines Corp., 2009
>> + * Copyright (c) Serge Hallyn <serue@us.ibm.com>
>> + * Copyright (C) 2023 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
>> + */
>> +
>> +/*\
>> + * [Description]
>> + *
>> + * Test mqueuefs manipulation from child/parent namespaces.
>> + *
>> + * [Algorithm]
>> + *
>> + * - parent creates mqueue folder in <tmpdir>
>> + * - child mounts mqueue there
>> + * - child creates /MQ1 mqueue
>> + * - parent checks for <tmpdir>/mqueue/MQ1 existence
>> + * - child exits
>> + * - parent checks for <tmpdir>/mqueue/MQ1 existence
>> + * - parent tries 'touch <tmpdir>/mqueue/MQ2' -> should fail
>> + * - parent umount mqueuefs
>> + */
>>   #define _GNU_SOURCE
>> -#endif
>> -#include <sys/types.h>
>> -#include <sys/stat.h>
>> +
>>   #include <sys/wait.h>
>> -#include <assert.h>
>> -#include <stdio.h>
>> -#include <stdlib.h>
>> -#include <unistd.h>
>> -#include <string.h>
>> -#include <errno.h>
>> -#include "mqns.h"
>> -#include "mqns_helper.h"
>> +#include "tst_test.h"
>> +#include "lapi/sched.h"
>> +#include "tst_safe_posix_ipc.h"
>> +#include "tst_safe_stdio.h"
>>   
>> -char *TCID = "posixmq_namespace_04";
>> -int TST_TOTAL = 1;
>> +#define CHECK_MQ_OPEN_RET(x) ((x) >= 0 || ((x) == -1 && errno != EMFILE))
>>   
>> -int p1[2];
>> -int p2[2];
>> +#define MQNAME1 "/MQ1"
>> +#define MQNAME2 "/MQ2"
> /MQ2 is never created

We try to create it by hand from parent, touching a queue file after 
mounting mqueue folder, but since it's a unshared container, we obtain 
error. Check:

     TST_EXP_FAIL(creat(mqueue2, 0755), EACCES);

>>   
>> -#define FNAM1 DEV_MQUEUE2 SLASH_MQ1
>> -#define FNAM2 DEV_MQUEUE2 SLASH_MQ2
>> +static char *str_op;
>> +static char *devdir;
>> +static char *mqueue1;
>> +static char *mqueue2;
>> +static int *mq_freed;
>>   
>> -int check_mqueue(void *vtest)
>> +static void check_mqueue(void)
>>   {
>> -	char buf[30];
>>   	mqd_t mqd;
>> -	int rc;
>>   
>> -	(void) vtest;
>> +	mqd = TST_RETRY_FUNC(
>> +		mq_open(MQNAME1, O_RDWR | O_CREAT | O_EXCL, 0755, NULL),
>> +		CHECK_MQ_OPEN_RET);
>> +	if (mqd == -1)
>> +		tst_brk(TBROK | TERRNO, "mq_open failed");
>>   
>> -	close(p1[1]);
>> -	close(p2[0]);
>> +	SAFE_MQ_CLOSE(mqd);
>>   
>> -	read(p1[0], buf, 3);	/* go */
>> +	SAFE_MOUNT("mqueue", devdir, "mqueue", 0, NULL);
>>   
>> -	mqd = tst_syscall(__NR_mq_open, NOSLASH_MQ1, O_RDWR | O_CREAT | O_EXCL,
>> -		0755, NULL);
>> -	if (mqd == -1) {
>> -		write(p2[1], "mqfail", 7);
>> -		tst_exit();
>> -	}
>> +	TST_CHECKPOINT_WAKE_AND_WAIT(0);
>> +}
>> +
>> +static void run(void)
>> +{
>> +	const struct tst_clone_args clone_args = { CLONE_NEWIPC, SIGCHLD };
>> +	int status;
>> +	struct stat statbuf;
>> +
>> +	tst_res(TINFO, "Checking mqueue filesystem lifetime");
>> +
>> +	if (str_op && !strcmp(str_op, "clone")) {
>> +		tst_res(TINFO, "Spawning isolated process");
>>   
>> -	mq_close(mqd);
>> +		if (!SAFE_CLONE(&clone_args)) {
>> +			check_mqueue();
>> +			return;
>> +		}
>> +	} else if (str_op && !strcmp(str_op, "unshare")) {
>> +		tst_res(TINFO, "Spawning unshared process");
>>   
>> -	rc = mount("mqueue", DEV_MQUEUE2, "mqueue", 0, NULL);
> Possibly this is a mistake in the original test. Do we really need to
> remount mqueuefs? Isn't it just the same FS in a different location?
>
> This thing that really makes a difference is that the child is in a new
> namespace, so the parent and child should not see each others queues?
>
>> -	if (rc == -1) {
>> -		perror("mount");
>> -		write(p2[1], "mount", 6);
>> -		tst_exit();
>> +		if (!SAFE_FORK()) {
>> +			SAFE_UNSHARE(CLONE_NEWIPC);
>> +			check_mqueue();
>> +			return;
>> +		}
>>   	}
>>   
>> -	write(p2[1], "go", 3);
>> -	read(p1[0], buf, 3);
>> +	TST_CHECKPOINT_WAIT(0);
>>   
>> -	tst_exit();
>> -}
>> +	SAFE_STAT(mqueue1, &statbuf);
>> +	tst_res(TPASS, "%s child's mqueue can be accessed from parent", mqueue1);
>>   
>> -static void setup(void)
>> -{
>> -	tst_require_root();
>> -	check_mqns();
>> -}
>> +	TST_CHECKPOINT_WAKE(0);
>>   
>> -int main(int argc, char *argv[])
>> -{
>> -	int rc;
>> -	int status;
>> -	char buf[30];
>> -	struct stat statbuf;
>> -	int use_clone = T_UNSHARE;
>> +	tst_res(TINFO, "Waiting child to exit");
>>   
>> -	setup();
>> +	SAFE_WAIT(&status);
> Again we can just use tst_reap_children() which does a thorough check of
> the child status.
>
>>   
>> -	if (argc == 2 && strcmp(argv[1], "-clone") == 0) {
>> -		tst_resm(TINFO,
>> -			 "Testing posix mq namespaces through clone(2).");
>> -		use_clone = T_CLONE;
>> -	} else
>> -		tst_resm(TINFO,
>> -			 "Testing posix mq namespaces through unshare(2).");
>> +	if (!WIFEXITED(status))
>> +		tst_brk(TBROK, "Child did not exit normally: %s", tst_strstatus(status));
>>   
>> -	if (pipe(p1) == -1) {
>> -		perror("pipe");
>> -		exit(EXIT_FAILURE);
>> -	}
>> -	if (pipe(p2) == -1) {
>> -		perror("pipe");
>> -		exit(EXIT_FAILURE);
>> -	}
>> +	SAFE_STAT(mqueue1, &statbuf);
>> +	tst_res(TPASS, "%s child's mqueue can be accessed from parent after child's dead", mqueue1);
>>   
>> -	mkdir(DEV_MQUEUE2, 0755);
>> +	TST_EXP_FAIL(creat(mqueue2, 0755), EACCES);
>>   
>> -	tst_resm(TINFO, "Checking mqueue filesystem lifetime");
>> +	SAFE_UMOUNT(devdir);
>>   
>> -	/* fire off the test */
>> -	rc = do_clone_unshare_test(use_clone, CLONE_NEWIPC, check_mqueue, NULL);
>> -	if (rc < 0) {
>> -		tst_resm(TFAIL, "failed clone/unshare");
>> -		goto fail;
>> -	}
>> +	*mq_freed = 1;
> This doesn't appear to be near the point that the MQ is unlinked.
>
>> +}
>>   
>> -	close(p1[0]);
>> -	close(p2[1]);
>> -	write(p1[1], "go", 3);
>> -
>> -	read(p2[0], buf, 7);
>> -	if (!strcmp(buf, "mqfail")) {
>> -		tst_resm(TFAIL, "child process could not create mqueue");
>> -		goto fail;
>> -	} else if (!strcmp(buf, "mount")) {
>> -		tst_resm(TFAIL, "child process could not mount mqueue");
>> -		goto fail;
>> -	}
>> +static void setup(void)
>> +{
>> +	char *tmpdir;
>>   
>> -	rc = stat(FNAM1, &statbuf);
>> -	if (rc == -1) {
>> -		perror("stat");
>> -		write(p1[1], "go", 3);
>> -		tst_resm(TFAIL, "parent could not see child's created mq");
>> -		goto fail;
>> -	}
>> -	write(p1[1], "go", 3);
>> +	if (!str_op)
>> +		tst_brk(TCONF, "Please use clone|unshare execution
>> mode");
> When this happens the test segfaults because devdir and mq_freed have
> not been allocated.
>
> This is also a problem in previous tests as well.
>
>>   
>> -	rc = wait(&status);
>> -	if (rc == -1) {
>> -		perror("wait");
>> -		tst_resm(TFAIL, "error while parent waited on child to exit");
>> -		goto fail;
>> -	}
>> -	if (!WIFEXITED(status)) {
>> -		tst_resm(TFAIL, "Child did not exit normally (status %d)",
>> -			 status);
>> -		goto fail;
>> -	}
>> -	rc = stat(FNAM1, &statbuf);
>> -	if (rc == -1) {
>> -		tst_resm(TFAIL,
>> -			 "parent's view of child's mq died with child");
>> -		goto fail;
>> -	}
>> +	tmpdir = tst_get_tmpdir();
>>   
>> -	rc = creat(FNAM2, 0755);
>> -	if (rc != -1) {
>> -		tst_resm(TFAIL,
>> -			 "parent was able to create a file in dead child's mqfs");
>> -		goto fail;
>> -	}
>> +	SAFE_ASPRINTF(&devdir, "%s/mqueue", tmpdir);
>> +	SAFE_MKDIR(devdir, 0755);
>> +
>> +	SAFE_ASPRINTF(&mqueue1, "%s" MQNAME1, devdir);
>> +	SAFE_ASPRINTF(&mqueue2, "%s" MQNAME2, devdir);
>>   
>> -	tst_resm(TPASS, "Child mqueue fs still visible for parent");
>> +	mq_freed = SAFE_MMAP(NULL,
>> +		sizeof(int),
>> +		PROT_READ | PROT_WRITE,
>> +		MAP_SHARED | MAP_ANONYMOUS,
>> +		-1, 0);
> This starts as 0, but we have not created any MQs yet so we could not
> free them in cleanup (also in mqns03).
I think in both mqns0[34] files I will access() mqueue files and remove 
them, instead of handling mq_freed variable.
> Try running with -i0
>
>> +}
>>   
>> -fail:
>> -	umount(DEV_MQUEUE2);
>> -	rmdir(DEV_MQUEUE2);
>> +static void cleanup(void)
>> +{
>> +	if (tst_is_mounted(devdir))
>> +		SAFE_UMOUNT(devdir);
>>   
>> -	tst_exit();
>> +	if (!*mq_freed) {
>> +		SAFE_MQ_UNLINK(MQNAME1);
>> +		SAFE_MQ_UNLINK(MQNAME2);
>> +	}
>>   }
>> +
>> +static struct tst_test test = {
>> +	.test_all = run,
>> +	.setup = setup,
>> +	.cleanup = cleanup,
>> +	.needs_root = 1,
>> +	.forks_child = 1,
>> +	.needs_tmpdir = 1,
>> +	.needs_checkpoints = 1,
>> +	.options = (struct tst_option[]) {
>> +		{ "m:", &str_op, "Test execution mode <clone|unshare>" },
>> +		{},
>> +	},
>> +};
>> -- 
>> 2.35.3
>
Andrea



More information about the ltp mailing list