xref: /linux/tools/testing/selftests/mount/unprivileged-remount-test.c (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
1 #define _GNU_SOURCE
2 #include <sched.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/mount.h>
8 #include <sys/wait.h>
9 #include <sys/vfs.h>
10 #include <sys/statvfs.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <grp.h>
15 #include <stdbool.h>
16 #include <stdarg.h>
17 
18 #ifndef CLONE_NEWNS
19 # define CLONE_NEWNS 0x00020000
20 #endif
21 #ifndef CLONE_NEWUTS
22 # define CLONE_NEWUTS 0x04000000
23 #endif
24 #ifndef CLONE_NEWIPC
25 # define CLONE_NEWIPC 0x08000000
26 #endif
27 #ifndef CLONE_NEWNET
28 # define CLONE_NEWNET 0x40000000
29 #endif
30 #ifndef CLONE_NEWUSER
31 # define CLONE_NEWUSER 0x10000000
32 #endif
33 #ifndef CLONE_NEWPID
34 # define CLONE_NEWPID 0x20000000
35 #endif
36 
37 #ifndef MS_REC
38 # define MS_REC 16384
39 #endif
40 #ifndef MS_RELATIME
41 # define MS_RELATIME (1 << 21)
42 #endif
43 #ifndef MS_STRICTATIME
44 # define MS_STRICTATIME (1 << 24)
45 #endif
46 
47 static void die(char *fmt, ...)
48 {
49 	va_list ap;
50 	va_start(ap, fmt);
51 	vfprintf(stderr, fmt, ap);
52 	va_end(ap);
53 	exit(EXIT_FAILURE);
54 }
55 
56 static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
57 {
58 	char buf[4096];
59 	int fd;
60 	ssize_t written;
61 	int buf_len;
62 
63 	buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
64 	if (buf_len < 0) {
65 		die("vsnprintf failed: %s\n",
66 		    strerror(errno));
67 	}
68 	if (buf_len >= sizeof(buf)) {
69 		die("vsnprintf output truncated\n");
70 	}
71 
72 	fd = open(filename, O_WRONLY);
73 	if (fd < 0) {
74 		if ((errno == ENOENT) && enoent_ok)
75 			return;
76 		die("open of %s failed: %s\n",
77 		    filename, strerror(errno));
78 	}
79 	written = write(fd, buf, buf_len);
80 	if (written != buf_len) {
81 		if (written >= 0) {
82 			die("short write to %s\n", filename);
83 		} else {
84 			die("write to %s failed: %s\n",
85 				filename, strerror(errno));
86 		}
87 	}
88 	if (close(fd) != 0) {
89 		die("close of %s failed: %s\n",
90 			filename, strerror(errno));
91 	}
92 }
93 
94 static void maybe_write_file(char *filename, char *fmt, ...)
95 {
96 	va_list ap;
97 
98 	va_start(ap, fmt);
99 	vmaybe_write_file(true, filename, fmt, ap);
100 	va_end(ap);
101 
102 }
103 
104 static void write_file(char *filename, char *fmt, ...)
105 {
106 	va_list ap;
107 
108 	va_start(ap, fmt);
109 	vmaybe_write_file(false, filename, fmt, ap);
110 	va_end(ap);
111 
112 }
113 
114 static int read_mnt_flags(const char *path)
115 {
116 	int ret;
117 	struct statvfs stat;
118 	int mnt_flags;
119 
120 	ret = statvfs(path, &stat);
121 	if (ret != 0) {
122 		die("statvfs of %s failed: %s\n",
123 			path, strerror(errno));
124 	}
125 	if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \
126 			ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \
127 			ST_SYNCHRONOUS | ST_MANDLOCK)) {
128 		die("Unrecognized mount flags\n");
129 	}
130 	mnt_flags = 0;
131 	if (stat.f_flag & ST_RDONLY)
132 		mnt_flags |= MS_RDONLY;
133 	if (stat.f_flag & ST_NOSUID)
134 		mnt_flags |= MS_NOSUID;
135 	if (stat.f_flag & ST_NODEV)
136 		mnt_flags |= MS_NODEV;
137 	if (stat.f_flag & ST_NOEXEC)
138 		mnt_flags |= MS_NOEXEC;
139 	if (stat.f_flag & ST_NOATIME)
140 		mnt_flags |= MS_NOATIME;
141 	if (stat.f_flag & ST_NODIRATIME)
142 		mnt_flags |= MS_NODIRATIME;
143 	if (stat.f_flag & ST_RELATIME)
144 		mnt_flags |= MS_RELATIME;
145 	if (stat.f_flag & ST_SYNCHRONOUS)
146 		mnt_flags |= MS_SYNCHRONOUS;
147 	if (stat.f_flag & ST_MANDLOCK)
148 		mnt_flags |= ST_MANDLOCK;
149 
150 	return mnt_flags;
151 }
152 
153 static void create_and_enter_userns(void)
154 {
155 	uid_t uid;
156 	gid_t gid;
157 
158 	uid = getuid();
159 	gid = getgid();
160 
161 	if (unshare(CLONE_NEWUSER) !=0) {
162 		die("unshare(CLONE_NEWUSER) failed: %s\n",
163 			strerror(errno));
164 	}
165 
166 	maybe_write_file("/proc/self/setgroups", "deny");
167 	write_file("/proc/self/uid_map", "0 %d 1", uid);
168 	write_file("/proc/self/gid_map", "0 %d 1", gid);
169 
170 	if (setgid(0) != 0) {
171 		die ("setgid(0) failed %s\n",
172 			strerror(errno));
173 	}
174 	if (setuid(0) != 0) {
175 		die("setuid(0) failed %s\n",
176 			strerror(errno));
177 	}
178 }
179 
180 static
181 bool test_unpriv_remount(const char *fstype, const char *mount_options,
182 			 int mount_flags, int remount_flags, int invalid_flags)
183 {
184 	pid_t child;
185 
186 	child = fork();
187 	if (child == -1) {
188 		die("fork failed: %s\n",
189 			strerror(errno));
190 	}
191 	if (child != 0) { /* parent */
192 		pid_t pid;
193 		int status;
194 		pid = waitpid(child, &status, 0);
195 		if (pid == -1) {
196 			die("waitpid failed: %s\n",
197 				strerror(errno));
198 		}
199 		if (pid != child) {
200 			die("waited for %d got %d\n",
201 				child, pid);
202 		}
203 		if (!WIFEXITED(status)) {
204 			die("child did not terminate cleanly\n");
205 		}
206 		return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
207 	}
208 
209 	create_and_enter_userns();
210 	if (unshare(CLONE_NEWNS) != 0) {
211 		die("unshare(CLONE_NEWNS) failed: %s\n",
212 			strerror(errno));
213 	}
214 
215 	if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) {
216 		die("mount of %s with options '%s' on /tmp failed: %s\n",
217 		    fstype,
218 		    mount_options? mount_options : "",
219 		    strerror(errno));
220 	}
221 
222 	create_and_enter_userns();
223 
224 	if (unshare(CLONE_NEWNS) != 0) {
225 		die("unshare(CLONE_NEWNS) failed: %s\n",
226 			strerror(errno));
227 	}
228 
229 	if (mount("/tmp", "/tmp", "none",
230 		  MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) {
231 		/* system("cat /proc/self/mounts"); */
232 		die("remount of /tmp failed: %s\n",
233 		    strerror(errno));
234 	}
235 
236 	if (mount("/tmp", "/tmp", "none",
237 		  MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) {
238 		/* system("cat /proc/self/mounts"); */
239 		die("remount of /tmp with invalid flags "
240 		    "succeeded unexpectedly\n");
241 	}
242 	exit(EXIT_SUCCESS);
243 }
244 
245 static bool test_unpriv_remount_simple(int mount_flags)
246 {
247 	return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0);
248 }
249 
250 static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags)
251 {
252 	return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags,
253 				   invalid_flags);
254 }
255 
256 static bool test_priv_mount_unpriv_remount(void)
257 {
258 	pid_t child;
259 	int ret;
260 	const char *orig_path = "/dev";
261 	const char *dest_path = "/tmp";
262 	int orig_mnt_flags, remount_mnt_flags;
263 
264 	child = fork();
265 	if (child == -1) {
266 		die("fork failed: %s\n",
267 			strerror(errno));
268 	}
269 	if (child != 0) { /* parent */
270 		pid_t pid;
271 		int status;
272 		pid = waitpid(child, &status, 0);
273 		if (pid == -1) {
274 			die("waitpid failed: %s\n",
275 				strerror(errno));
276 		}
277 		if (pid != child) {
278 			die("waited for %d got %d\n",
279 				child, pid);
280 		}
281 		if (!WIFEXITED(status)) {
282 			die("child did not terminate cleanly\n");
283 		}
284 		return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
285 	}
286 
287 	orig_mnt_flags = read_mnt_flags(orig_path);
288 
289 	create_and_enter_userns();
290 	ret = unshare(CLONE_NEWNS);
291 	if (ret != 0) {
292 		die("unshare(CLONE_NEWNS) failed: %s\n",
293 			strerror(errno));
294 	}
295 
296 	ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL);
297 	if (ret != 0) {
298 		die("recursive bind mount of %s onto %s failed: %s\n",
299 			orig_path, dest_path, strerror(errno));
300 	}
301 
302 	ret = mount(dest_path, dest_path, "none",
303 		    MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL);
304 	if (ret != 0) {
305 		/* system("cat /proc/self/mounts"); */
306 		die("remount of /tmp failed: %s\n",
307 		    strerror(errno));
308 	}
309 
310 	remount_mnt_flags = read_mnt_flags(dest_path);
311 	if (orig_mnt_flags != remount_mnt_flags) {
312 		die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n",
313 			dest_path, orig_path);
314 	}
315 	exit(EXIT_SUCCESS);
316 }
317 
318 int main(int argc, char **argv)
319 {
320 	if (!test_unpriv_remount_simple(MS_RDONLY)) {
321 		die("MS_RDONLY malfunctions\n");
322 	}
323 	if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) {
324 		die("MS_NODEV malfunctions\n");
325 	}
326 	if (!test_unpriv_remount_simple(MS_NOSUID)) {
327 		die("MS_NOSUID malfunctions\n");
328 	}
329 	if (!test_unpriv_remount_simple(MS_NOEXEC)) {
330 		die("MS_NOEXEC malfunctions\n");
331 	}
332 	if (!test_unpriv_remount_atime(MS_RELATIME,
333 				       MS_NOATIME))
334 	{
335 		die("MS_RELATIME malfunctions\n");
336 	}
337 	if (!test_unpriv_remount_atime(MS_STRICTATIME,
338 				       MS_NOATIME))
339 	{
340 		die("MS_STRICTATIME malfunctions\n");
341 	}
342 	if (!test_unpriv_remount_atime(MS_NOATIME,
343 				       MS_STRICTATIME))
344 	{
345 		die("MS_NOATIME malfunctions\n");
346 	}
347 	if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME,
348 				       MS_NOATIME))
349 	{
350 		die("MS_RELATIME|MS_NODIRATIME malfunctions\n");
351 	}
352 	if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME,
353 				       MS_NOATIME))
354 	{
355 		die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n");
356 	}
357 	if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME,
358 				       MS_STRICTATIME))
359 	{
360 		die("MS_NOATIME|MS_DIRATIME malfunctions\n");
361 	}
362 	if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME))
363 	{
364 		die("Default atime malfunctions\n");
365 	}
366 	if (!test_priv_mount_unpriv_remount()) {
367 		die("Mount flags unexpectedly changed after remount\n");
368 	}
369 	return EXIT_SUCCESS;
370 }
371