~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/mount/unprivileged-remount-test.c

Version: ~ [ linux-5.11 ] ~ [ linux-5.10.17 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.99 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.176 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.221 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.257 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.257 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp