1 /*
2  * Copyright (C) 2013 Linux Test Project, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 2 of the GNU General Public
6  * License as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it
13  * is free of the rightful claim of any third person regarding
14  * infringement or the like.  Any license provided herein, whether
15  * implied or otherwise, applies only to this software file.  Patent
16  * licenses, if any, provided herein do not apply to combinations of
17  * this program with other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301, USA.
23  */
24 /*
25  * functional test for setns(2) - reassociate thread with a namespace
26  * 1. create child with CLONE_NEWUTS, set different hostname in child,
27  *    set namespace back to parent ns and check that hostname has changed
28  * 2. create child with CLONE_NEWIPC, set up shared memory in parent
29  *    and verify that child can't shmat it, then set namespace
30  *    back to parent one and verify that child is able to do shmat
31  */
32 #define _GNU_SOURCE
33 #include <sys/ipc.h>
34 #include <sys/shm.h>
35 #include <sys/stat.h>
36 #include <sys/syscall.h>
37 #include <sys/types.h>
38 #include <sys/utsname.h>
39 #include <sys/wait.h>
40 #include <errno.h>
41 #include <sched.h>
42 #include <string.h>
43 #include "config.h"
44 #include "test.h"
45 #include "lapi/syscalls.h"
46 #include "safe_macros.h"
47 
48 #define CHILD_STACK_SIZE (1024*1024)
49 #define CP "(child) "
50 char *TCID = "setns02";
51 
52 #if defined(__NR_setns) && defined(CLONE_NEWIPC) && defined(CLONE_NEWUTS)
53 #include "setns.h"
54 
55 static char *dummy_hostname = "setns_dummy_uts";
56 static int ns_ipc_fd;
57 static int ns_uts_fd;
58 static key_t ipc_key;
59 static int shmid;
60 
61 static void setup(void);
62 static void cleanup(void);
63 
do_child_newuts(void * arg)64 static int do_child_newuts(void *arg)
65 {
66 	struct utsname uts, uts_parent;
67 	int ns_flag = *(int *)arg;
68 
69 	if (uname(&uts_parent) == -1)
70 		tst_resm(TFAIL|TERRNO, CP"uname");
71 	tst_resm(TINFO, CP"hostname (inherited from parent): %s",
72 		uts_parent.nodename);
73 
74 	if (sethostname(dummy_hostname, strlen(dummy_hostname)) == -1)
75 		tst_resm(TFAIL|TERRNO, CP"sethostname");
76 	if (uname(&uts) == -1)
77 		tst_resm(TFAIL|TERRNO, CP"uname");
78 
79 	tst_resm(TINFO, CP"hostname changed to: %s", uts.nodename);
80 	if (strcmp(uts_parent.nodename, uts.nodename) == 0) {
81 		tst_resm(TFAIL, CP"expected hostname to be different");
82 		return 1;
83 	} else {
84 		tst_resm(TPASS, CP"hostname is different in parent/child");
85 	}
86 
87 	tst_resm(TINFO, CP"attempting to switch ns back to parent ns");
88 	if (syscall(__NR_setns, ns_uts_fd, ns_flag) == -1) {
89 		tst_resm(TFAIL|TERRNO, CP"setns");
90 		return 2;
91 	}
92 	if (uname(&uts) == -1)
93 		tst_resm(TFAIL|TERRNO, CP"uname");
94 
95 	tst_resm(TINFO, CP"hostname: %s", uts.nodename);
96 	if (strcmp(uts_parent.nodename, uts.nodename) != 0) {
97 		tst_resm(TFAIL, CP"expected hostname to match parent");
98 		return 3;
99 	} else {
100 		tst_resm(TPASS, CP"hostname now as expected");
101 	}
102 	return 0;
103 }
104 
do_child_newipc(void * arg)105 static int do_child_newipc(void *arg)
106 {
107 	void *p;
108 	int ns_flag = *(int *)arg;
109 
110 	p = shmat(shmid, NULL, 0);
111 	if (p == (void *) -1) {
112 		tst_resm(TPASS|TERRNO, CP"shmat failed as expected");
113 	} else {
114 		tst_resm(TFAIL, CP"shmat unexpectedly suceeded");
115 		shmdt(p);
116 		return 1;
117 	}
118 
119 	tst_resm(TINFO, CP"attempting to switch ns back to parent ns");
120 	if (syscall(__NR_setns, ns_ipc_fd, ns_flag) == -1) {
121 		tst_resm(TFAIL|TERRNO, CP"setns");
122 		return 2;
123 	}
124 
125 	p = shmat(shmid, NULL, 0);
126 	if (p == (void *) -1) {
127 		tst_resm(TFAIL|TERRNO, CP"shmat failed after setns");
128 		return 3;
129 	} else {
130 		tst_resm(TPASS, CP"shmat suceeded");
131 		shmdt(p);
132 	}
133 
134 	return 0;
135 }
136 
test_flag(int clone_flag,int ns_flag,int (* fn)(void * arg))137 static void test_flag(int clone_flag, int ns_flag, int (*fn) (void *arg))
138 {
139 	void *child_stack;
140 	int ret, status;
141 
142 	child_stack = malloc(CHILD_STACK_SIZE);
143 	if (child_stack == NULL)
144 		tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
145 
146 	tst_resm(TINFO, "creating child with clone_flag=0x%x, ns_flag=0x%x",
147 		clone_flag, ns_flag);
148 	ret = ltp_clone(SIGCHLD|clone_flag, fn, &ns_flag,
149 		CHILD_STACK_SIZE, child_stack);
150 	if (ret == -1)
151 		tst_brkm(TBROK|TERRNO, cleanup, "ltp_clone");
152 
153 	SAFE_WAITPID(cleanup, ret, &status, 0);
154 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
155 		tst_resm(TFAIL, "child returns %d", status);
156 	else
157 		tst_resm(TPASS, "child finished succesfully");
158 	free(child_stack);
159 }
160 
main(int argc,char * argv[])161 int main(int argc, char *argv[])
162 {
163 	int lc;
164 
165 	tst_parse_opts(argc, argv, NULL, NULL);
166 
167 	setup();
168 	for (lc = 0; TEST_LOOPING(lc); lc++) {
169 		if (ns_uts_fd != -1) {
170 			tst_resm(TINFO, "test_newuts");
171 			test_flag(CLONE_NEWUTS, CLONE_NEWUTS, do_child_newuts);
172 			test_flag(CLONE_NEWUTS, 0, do_child_newuts);
173 		}
174 		if (ns_ipc_fd != -1) {
175 			tst_resm(TINFO, "test_newipc");
176 			test_flag(CLONE_NEWIPC, CLONE_NEWIPC, do_child_newipc);
177 			test_flag(CLONE_NEWIPC, 0, do_child_newipc);
178 		}
179 	}
180 	cleanup();
181 	tst_exit();
182 }
183 
setup(void)184 static void setup(void)
185 {
186 	char tmp[PATH_MAX];
187 
188 	tst_require_root();
189 
190 	/* runtime check if syscall is supported */
191 	ltp_syscall(__NR_setns, -1, 0);
192 
193 	/* check if kernel has CONFIG_*_NS set and exports /proc entries */
194 	ns_ipc_fd = get_ns_fd(getpid(), "ipc");
195 	ns_uts_fd = get_ns_fd(getpid(), "uts");
196 	if (ns_ipc_fd == -1 && ns_uts_fd == -1)
197 		tst_brkm(TCONF, NULL, "your kernel has CONFIG_IPC_NS, "
198 			"CONFIG_UTS_NS or CONFIG_PROC disabled");
199 
200 	if (getcwd(tmp, PATH_MAX) == NULL)
201 		tst_brkm(TBROK|TERRNO, NULL, "getcwd");
202 	ipc_key = ftok(tmp, 65);
203 	shmid = shmget(ipc_key, getpagesize(), IPC_CREAT | 0666);
204 	if (shmid == -1)
205 		tst_brkm(TBROK|TERRNO, NULL, "shmget");
206 
207 	TEST_PAUSE;
208 }
209 
cleanup(void)210 static void cleanup(void)
211 {
212 	if (ns_ipc_fd != -1)
213 		close(ns_ipc_fd);
214 	if (ns_uts_fd != -1)
215 		close(ns_uts_fd);
216 
217 	shmctl(shmid, IPC_RMID, NULL);
218 }
219 #else
main(int argc,char * argv[])220 int main(int argc, char *argv[])
221 {
222 	tst_brkm(TCONF, NULL, "__NR_setns, CLONE_NEWIPC or CLONE_NEWUTS "
223 		" is not defined on your system.");
224 }
225 #endif
226