Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/overlayfs: verify device and inode numbers in /proc/pid/maps

When mapping a file on overlayfs, the file stored in ->vm_file is a
backing file whose f_inode is on the underlying filesystem. We need to
verify that /proc/pid/maps contains numbers of the overlayfs file, but
not its backing file.

Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Alexander Mikhalitsyn <alexander@mihalicyn.com>
Signed-off-by: Andrei Vagin <avagin@google.com>
Link: https://lore.kernel.org/r/20231214064439.1023011-2-avagin@google.com
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Andrei Vagin and committed by
Christian Brauner
b5a78c71 3efdc78f

+218
+1
tools/testing/selftests/Makefile
··· 26 26 TARGETS += filesystems/binderfs 27 27 TARGETS += filesystems/epoll 28 28 TARGETS += filesystems/fat 29 + TARGETS += filesystems/overlayfs 29 30 TARGETS += firmware 30 31 TARGETS += fpu 31 32 TARGETS += ftrace
+2
tools/testing/selftests/filesystems/overlayfs/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + dev_in_maps
+7
tools/testing/selftests/filesystems/overlayfs/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + TEST_GEN_PROGS := dev_in_maps 4 + 5 + CFLAGS := -Wall -Werror 6 + 7 + include ../../lib.mk
+182
tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + 4 + #include <inttypes.h> 5 + #include <unistd.h> 6 + #include <stdio.h> 7 + 8 + #include <linux/unistd.h> 9 + #include <linux/types.h> 10 + #include <linux/mount.h> 11 + #include <sys/syscall.h> 12 + #include <sys/stat.h> 13 + #include <sys/mount.h> 14 + #include <sys/mman.h> 15 + #include <sched.h> 16 + #include <fcntl.h> 17 + 18 + #include "../../kselftest.h" 19 + #include "log.h" 20 + 21 + static int sys_fsopen(const char *fsname, unsigned int flags) 22 + { 23 + return syscall(__NR_fsopen, fsname, flags); 24 + } 25 + 26 + static int sys_fsconfig(int fd, unsigned int cmd, const char *key, const char *value, int aux) 27 + { 28 + return syscall(__NR_fsconfig, fd, cmd, key, value, aux); 29 + } 30 + 31 + static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags) 32 + { 33 + return syscall(__NR_fsmount, fd, flags, attr_flags); 34 + } 35 + 36 + static int sys_move_mount(int from_dfd, const char *from_pathname, 37 + int to_dfd, const char *to_pathname, 38 + unsigned int flags) 39 + { 40 + return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags); 41 + } 42 + 43 + static long get_file_dev_and_inode(void *addr, struct statx *stx) 44 + { 45 + char buf[4096]; 46 + FILE *mapf; 47 + 48 + mapf = fopen("/proc/self/maps", "r"); 49 + if (mapf == NULL) 50 + return pr_perror("fopen(/proc/self/maps)"); 51 + 52 + while (fgets(buf, sizeof(buf), mapf)) { 53 + unsigned long start, end; 54 + uint32_t maj, min; 55 + __u64 ino; 56 + 57 + if (sscanf(buf, "%lx-%lx %*s %*s %x:%x %llu", 58 + &start, &end, &maj, &min, &ino) != 5) 59 + return pr_perror("unable to parse: %s", buf); 60 + if (start == (unsigned long)addr) { 61 + stx->stx_dev_major = maj; 62 + stx->stx_dev_minor = min; 63 + stx->stx_ino = ino; 64 + return 0; 65 + } 66 + } 67 + 68 + return pr_err("unable to find the mapping"); 69 + } 70 + 71 + static int ovl_mount(void) 72 + { 73 + int tmpfs, fsfd, ovl; 74 + 75 + fsfd = sys_fsopen("tmpfs", 0); 76 + if (fsfd == -1) 77 + return pr_perror("fsopen(tmpfs)"); 78 + 79 + if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) 80 + return pr_perror("FSCONFIG_CMD_CREATE"); 81 + 82 + tmpfs = sys_fsmount(fsfd, 0, 0); 83 + if (tmpfs == -1) 84 + return pr_perror("fsmount"); 85 + 86 + close(fsfd); 87 + 88 + /* overlayfs can't be constructed on top of a detached mount. */ 89 + if (sys_move_mount(tmpfs, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH)) 90 + return pr_perror("move_mount"); 91 + close(tmpfs); 92 + 93 + if (mkdir("/tmp/w", 0755) == -1 || 94 + mkdir("/tmp/u", 0755) == -1 || 95 + mkdir("/tmp/l", 0755) == -1) 96 + return pr_perror("mkdir"); 97 + 98 + fsfd = sys_fsopen("overlay", 0); 99 + if (fsfd == -1) 100 + return pr_perror("fsopen(overlay)"); 101 + if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) == -1 || 102 + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", "/tmp/l", 0) == -1 || 103 + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", "/tmp/u", 0) == -1 || 104 + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", "/tmp/w", 0) == -1) 105 + return pr_perror("fsconfig"); 106 + if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) 107 + return pr_perror("fsconfig"); 108 + ovl = sys_fsmount(fsfd, 0, 0); 109 + if (ovl == -1) 110 + return pr_perror("fsmount"); 111 + 112 + return ovl; 113 + } 114 + 115 + /* 116 + * Check that the file device and inode shown in /proc/pid/maps match values 117 + * returned by stat(2). 118 + */ 119 + static int test(void) 120 + { 121 + struct statx stx, mstx; 122 + int ovl, fd; 123 + void *addr; 124 + 125 + ovl = ovl_mount(); 126 + if (ovl == -1) 127 + return -1; 128 + 129 + fd = openat(ovl, "test", O_RDWR | O_CREAT, 0644); 130 + if (fd == -1) 131 + return pr_perror("openat"); 132 + 133 + addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); 134 + if (addr == MAP_FAILED) 135 + return pr_perror("mmap"); 136 + 137 + if (get_file_dev_and_inode(addr, &mstx)) 138 + return -1; 139 + if (statx(fd, "", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT, STATX_INO, &stx)) 140 + return pr_perror("statx"); 141 + 142 + if (stx.stx_dev_major != mstx.stx_dev_major || 143 + stx.stx_dev_minor != mstx.stx_dev_minor || 144 + stx.stx_ino != mstx.stx_ino) 145 + return pr_fail("unmatched dev:ino %x:%x:%llx (expected %x:%x:%llx)\n", 146 + mstx.stx_dev_major, mstx.stx_dev_minor, mstx.stx_ino, 147 + stx.stx_dev_major, stx.stx_dev_minor, stx.stx_ino); 148 + 149 + ksft_test_result_pass("devices are matched\n"); 150 + return 0; 151 + } 152 + 153 + int main(int argc, char **argv) 154 + { 155 + int fsfd; 156 + 157 + fsfd = sys_fsopen("overlay", 0); 158 + if (fsfd == -1) { 159 + ksft_test_result_skip("unable to create overlay mount\n"); 160 + return 1; 161 + } 162 + close(fsfd); 163 + 164 + /* Create a new mount namespace to not care about cleaning test mounts. */ 165 + if (unshare(CLONE_NEWNS) == -1) { 166 + ksft_test_result_skip("unable to create a new mount namespace\n"); 167 + return 1; 168 + } 169 + 170 + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) { 171 + pr_perror("mount"); 172 + return 1; 173 + } 174 + 175 + ksft_set_plan(1); 176 + 177 + if (test()) 178 + return 1; 179 + 180 + ksft_exit_pass(); 181 + return 0; 182 + }
+26
tools/testing/selftests/filesystems/overlayfs/log.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __SELFTEST_TIMENS_LOG_H__ 4 + #define __SELFTEST_TIMENS_LOG_H__ 5 + 6 + #define pr_msg(fmt, lvl, ...) \ 7 + ksft_print_msg("[%s] (%s:%d)\t" fmt "\n", \ 8 + lvl, __FILE__, __LINE__, ##__VA_ARGS__) 9 + 10 + #define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__) 11 + 12 + #define pr_err(fmt, ...) \ 13 + ({ \ 14 + ksft_test_result_error(fmt "\n", ##__VA_ARGS__); \ 15 + -1; \ 16 + }) 17 + 18 + #define pr_fail(fmt, ...) \ 19 + ({ \ 20 + ksft_test_result_fail(fmt, ##__VA_ARGS__); \ 21 + -1; \ 22 + }) 23 + 24 + #define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__) 25 + 26 + #endif