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