linux/tools/testing/selftests/mount/unprivileged-remount-test.c
<<
>>
Prefs
   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
  48static 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
  57static 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
  95static 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
 105static 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
 115static 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
 154static 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
 181static
 182bool 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
 246static bool test_unpriv_remount_simple(int mount_flags)
 247{
 248        return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0);
 249}
 250
 251static 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
 257static 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
 319int 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}
 372