Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[firefly-linux-kernel-4.4.55.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 <stdlib.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <grp.h>
13 #include <stdbool.h>
14 #include <stdarg.h>
15
16 #ifndef CLONE_NEWNS
17 # define CLONE_NEWNS 0x00020000
18 #endif
19 #ifndef CLONE_NEWUTS
20 # define CLONE_NEWUTS 0x04000000
21 #endif
22 #ifndef CLONE_NEWIPC
23 # define CLONE_NEWIPC 0x08000000
24 #endif
25 #ifndef CLONE_NEWNET
26 # define CLONE_NEWNET 0x40000000
27 #endif
28 #ifndef CLONE_NEWUSER
29 # define CLONE_NEWUSER 0x10000000
30 #endif
31 #ifndef CLONE_NEWPID
32 # define CLONE_NEWPID 0x20000000
33 #endif
34
35 #ifndef MS_RELATIME
36 #define MS_RELATIME (1 << 21)
37 #endif
38 #ifndef MS_STRICTATIME
39 #define MS_STRICTATIME (1 << 24)
40 #endif
41
42 static void die(char *fmt, ...)
43 {
44         va_list ap;
45         va_start(ap, fmt);
46         vfprintf(stderr, fmt, ap);
47         va_end(ap);
48         exit(EXIT_FAILURE);
49 }
50
51 static void write_file(char *filename, char *fmt, ...)
52 {
53         char buf[4096];
54         int fd;
55         ssize_t written;
56         int buf_len;
57         va_list ap;
58
59         va_start(ap, fmt);
60         buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
61         va_end(ap);
62         if (buf_len < 0) {
63                 die("vsnprintf failed: %s\n",
64                     strerror(errno));
65         }
66         if (buf_len >= sizeof(buf)) {
67                 die("vsnprintf output truncated\n");
68         }
69
70         fd = open(filename, O_WRONLY);
71         if (fd < 0) {
72                 die("open of %s failed: %s\n",
73                     filename, strerror(errno));
74         }
75         written = write(fd, buf, buf_len);
76         if (written != buf_len) {
77                 if (written >= 0) {
78                         die("short write to %s\n", filename);
79                 } else {
80                         die("write to %s failed: %s\n",
81                                 filename, strerror(errno));
82                 }
83         }
84         if (close(fd) != 0) {
85                 die("close of %s failed: %s\n",
86                         filename, strerror(errno));
87         }
88 }
89
90 static void create_and_enter_userns(void)
91 {
92         uid_t uid;
93         gid_t gid;
94
95         uid = getuid();
96         gid = getgid();
97
98         if (unshare(CLONE_NEWUSER) !=0) {
99                 die("unshare(CLONE_NEWUSER) failed: %s\n",
100                         strerror(errno));
101         }
102
103         write_file("/proc/self/uid_map", "0 %d 1", uid);
104         write_file("/proc/self/gid_map", "0 %d 1", gid);
105
106         if (setgroups(0, NULL) != 0) {
107                 die("setgroups failed: %s\n",
108                         strerror(errno));
109         }
110         if (setgid(0) != 0) {
111                 die ("setgid(0) failed %s\n",
112                         strerror(errno));
113         }
114         if (setuid(0) != 0) {
115                 die("setuid(0) failed %s\n",
116                         strerror(errno));
117         }
118 }
119
120 static
121 bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags)
122 {
123         pid_t child;
124
125         child = fork();
126         if (child == -1) {
127                 die("fork failed: %s\n",
128                         strerror(errno));
129         }
130         if (child != 0) { /* parent */
131                 pid_t pid;
132                 int status;
133                 pid = waitpid(child, &status, 0);
134                 if (pid == -1) {
135                         die("waitpid failed: %s\n",
136                                 strerror(errno));
137                 }
138                 if (pid != child) {
139                         die("waited for %d got %d\n",
140                                 child, pid);
141                 }
142                 if (!WIFEXITED(status)) {
143                         die("child did not terminate cleanly\n");
144                 }
145                 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
146         }
147
148         create_and_enter_userns();
149         if (unshare(CLONE_NEWNS) != 0) {
150                 die("unshare(CLONE_NEWNS) failed: %s\n",
151                         strerror(errno));
152         }
153
154         if (mount("testing", "/tmp", "ramfs", mount_flags, NULL) != 0) {
155                 die("mount of /tmp failed: %s\n",
156                         strerror(errno));
157         }
158
159         create_and_enter_userns();
160
161         if (unshare(CLONE_NEWNS) != 0) {
162                 die("unshare(CLONE_NEWNS) failed: %s\n",
163                         strerror(errno));
164         }
165
166         if (mount("/tmp", "/tmp", "none",
167                   MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) {
168                 /* system("cat /proc/self/mounts"); */
169                 die("remount of /tmp failed: %s\n",
170                     strerror(errno));
171         }
172
173         if (mount("/tmp", "/tmp", "none",
174                   MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) {
175                 /* system("cat /proc/self/mounts"); */
176                 die("remount of /tmp with invalid flags "
177                     "succeeded unexpectedly\n");
178         }
179         exit(EXIT_SUCCESS);
180 }
181
182 static bool test_unpriv_remount_simple(int mount_flags)
183 {
184         return test_unpriv_remount(mount_flags, mount_flags, 0);
185 }
186
187 static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags)
188 {
189         return test_unpriv_remount(mount_flags, mount_flags, invalid_flags);
190 }
191
192 int main(int argc, char **argv)
193 {
194         if (!test_unpriv_remount_simple(MS_RDONLY|MS_NODEV)) {
195                 die("MS_RDONLY malfunctions\n");
196         }
197         if (!test_unpriv_remount_simple(MS_NODEV)) {
198                 die("MS_NODEV malfunctions\n");
199         }
200         if (!test_unpriv_remount_simple(MS_NOSUID|MS_NODEV)) {
201                 die("MS_NOSUID malfunctions\n");
202         }
203         if (!test_unpriv_remount_simple(MS_NOEXEC|MS_NODEV)) {
204                 die("MS_NOEXEC malfunctions\n");
205         }
206         if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODEV,
207                                        MS_NOATIME|MS_NODEV))
208         {
209                 die("MS_RELATIME malfunctions\n");
210         }
211         if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODEV,
212                                        MS_NOATIME|MS_NODEV))
213         {
214                 die("MS_STRICTATIME malfunctions\n");
215         }
216         if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODEV,
217                                        MS_STRICTATIME|MS_NODEV))
218         {
219                 die("MS_RELATIME malfunctions\n");
220         }
221         if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME|MS_NODEV,
222                                        MS_NOATIME|MS_NODEV))
223         {
224                 die("MS_RELATIME malfunctions\n");
225         }
226         if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME|MS_NODEV,
227                                        MS_NOATIME|MS_NODEV))
228         {
229                 die("MS_RELATIME malfunctions\n");
230         }
231         if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME|MS_NODEV,
232                                        MS_STRICTATIME|MS_NODEV))
233         {
234                 die("MS_RELATIME malfunctions\n");
235         }
236         if (!test_unpriv_remount(MS_STRICTATIME|MS_NODEV, MS_NODEV,
237                                  MS_NOATIME|MS_NODEV))
238         {
239                 die("Default atime malfunctions\n");
240         }
241         return EXIT_SUCCESS;
242 }