root/tools/testing/selftests/mount/unprivileged-remount-test.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. die
  2. vmaybe_write_file
  3. maybe_write_file
  4. write_file
  5. read_mnt_flags
  6. create_and_enter_userns
  7. test_unpriv_remount
  8. test_unpriv_remount_simple
  9. test_unpriv_remount_atime
  10. test_priv_mount_unpriv_remount
  11. main

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

/* [<][>][^][v][top][bottom][index][help] */