linux/tools/testing/selftests/mount/unprivileged-remount-test.c
<<
>>
Prefs
   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
  47static 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
  56static 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
  94static 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
 104static 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
 114static 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
 153static 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
 180static
 181bool 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
 245static bool test_unpriv_remount_simple(int mount_flags)
 246{
 247        return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0);
 248}
 249
 250static 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
 256static 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
 318int 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