Message ID | 20231214064439.1023011-2-avagin@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [1/2,v2] fs/proc: show correct device and inode numbers in /proc/pid/maps | expand |
On Thu, Dec 14, 2023 at 8:44 AM Andrei Vagin <avagin@google.com> wrote: > > 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> Reviewed-by: Amir Goldstein <amir73il@gmail.com> > --- > tools/testing/selftests/Makefile | 1 + > .../filesystems/overlayfs/.gitignore | 2 + > .../selftests/filesystems/overlayfs/Makefile | 7 + > .../filesystems/overlayfs/dev_in_maps.c | 182 ++++++++++++++++++ > .../selftests/filesystems/overlayfs/log.h | 26 +++ > 5 files changed, 218 insertions(+) > create mode 100644 tools/testing/selftests/filesystems/overlayfs/.gitignore > create mode 100644 tools/testing/selftests/filesystems/overlayfs/Makefile > create mode 100644 tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c > create mode 100644 tools/testing/selftests/filesystems/overlayfs/log.h > > diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile > index 3b2061d1c1a5..0939a40abb28 100644 > --- a/tools/testing/selftests/Makefile > +++ b/tools/testing/selftests/Makefile > @@ -26,6 +26,7 @@ TARGETS += filesystems > TARGETS += filesystems/binderfs > TARGETS += filesystems/epoll > TARGETS += filesystems/fat > +TARGETS += filesystems/overlayfs > TARGETS += firmware > TARGETS += fpu > TARGETS += ftrace > diff --git a/tools/testing/selftests/filesystems/overlayfs/.gitignore b/tools/testing/selftests/filesystems/overlayfs/.gitignore > new file mode 100644 > index 000000000000..52ae618fdd98 > --- /dev/null > +++ b/tools/testing/selftests/filesystems/overlayfs/.gitignore > @@ -0,0 +1,2 @@ > +# SPDX-License-Identifier: GPL-2.0-only > +dev_in_maps > diff --git a/tools/testing/selftests/filesystems/overlayfs/Makefile b/tools/testing/selftests/filesystems/overlayfs/Makefile > new file mode 100644 > index 000000000000..56b2b48a765b > --- /dev/null > +++ b/tools/testing/selftests/filesystems/overlayfs/Makefile > @@ -0,0 +1,7 @@ > +# SPDX-License-Identifier: GPL-2.0 > + > +TEST_GEN_PROGS := dev_in_maps > + > +CFLAGS := -Wall -Werror > + > +include ../../lib.mk > diff --git a/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c > new file mode 100644 > index 000000000000..e19ab0e85709 > --- /dev/null > +++ b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c > @@ -0,0 +1,182 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define _GNU_SOURCE > + > +#include <inttypes.h> > +#include <unistd.h> > +#include <stdio.h> > + > +#include <linux/unistd.h> > +#include <linux/types.h> > +#include <linux/mount.h> > +#include <sys/syscall.h> > +#include <sys/stat.h> > +#include <sys/mount.h> > +#include <sys/mman.h> > +#include <sched.h> > +#include <fcntl.h> > + > +#include "../../kselftest.h" > +#include "log.h" > + > +static int sys_fsopen(const char *fsname, unsigned int flags) > +{ > + return syscall(__NR_fsopen, fsname, flags); > +} > + > +static int sys_fsconfig(int fd, unsigned int cmd, const char *key, const char *value, int aux) > +{ > + return syscall(__NR_fsconfig, fd, cmd, key, value, aux); > +} > + > +static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags) > +{ > + return syscall(__NR_fsmount, fd, flags, attr_flags); > +} > + > +static int sys_move_mount(int from_dfd, const char *from_pathname, > + int to_dfd, const char *to_pathname, > + unsigned int flags) > +{ > + return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags); > +} > + > +static long get_file_dev_and_inode(void *addr, struct statx *stx) > +{ > + char buf[4096]; > + FILE *mapf; > + > + mapf = fopen("/proc/self/maps", "r"); > + if (mapf == NULL) > + return pr_perror("fopen(/proc/self/maps)"); > + > + while (fgets(buf, sizeof(buf), mapf)) { > + unsigned long start, end; > + uint32_t maj, min; > + __u64 ino; > + > + if (sscanf(buf, "%lx-%lx %*s %*s %x:%x %llu", > + &start, &end, &maj, &min, &ino) != 5) > + return pr_perror("unable to parse: %s", buf); > + if (start == (unsigned long)addr) { > + stx->stx_dev_major = maj; > + stx->stx_dev_minor = min; > + stx->stx_ino = ino; > + return 0; > + } > + } > + > + return pr_err("unable to find the mapping"); > +} > + > +static int ovl_mount(void) > +{ > + int tmpfs, fsfd, ovl; > + > + fsfd = sys_fsopen("tmpfs", 0); > + if (fsfd == -1) > + return pr_perror("fsopen(tmpfs)"); > + > + if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) > + return pr_perror("FSCONFIG_CMD_CREATE"); > + > + tmpfs = sys_fsmount(fsfd, 0, 0); > + if (tmpfs == -1) > + return pr_perror("fsmount"); > + > + close(fsfd); > + > + /* overlayfs can't be constructed on top of a detached mount. */ > + if (sys_move_mount(tmpfs, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH)) > + return pr_perror("move_mount"); > + close(tmpfs); > + > + if (mkdir("/tmp/w", 0755) == -1 || > + mkdir("/tmp/u", 0755) == -1 || > + mkdir("/tmp/l", 0755) == -1) > + return pr_perror("mkdir"); > + > + fsfd = sys_fsopen("overlay", 0); > + if (fsfd == -1) > + return pr_perror("fsopen(overlay)"); > + if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) == -1 || > + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", "/tmp/l", 0) == -1 || > + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", "/tmp/u", 0) == -1 || > + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", "/tmp/w", 0) == -1) > + return pr_perror("fsconfig"); > + if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) > + return pr_perror("fsconfig"); > + ovl = sys_fsmount(fsfd, 0, 0); > + if (ovl == -1) > + return pr_perror("fsmount"); > + > + return ovl; > +} > + > +/* > + * Check that the file device and inode shown in /proc/pid/maps match values > + * returned by stat(2). > + */ > +static int test(void) > +{ > + struct statx stx, mstx; > + int ovl, fd; > + void *addr; > + > + ovl = ovl_mount(); > + if (ovl == -1) > + return -1; > + > + fd = openat(ovl, "test", O_RDWR | O_CREAT, 0644); > + if (fd == -1) > + return pr_perror("openat"); > + > + addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); > + if (addr == MAP_FAILED) > + return pr_perror("mmap"); > + > + if (get_file_dev_and_inode(addr, &mstx)) > + return -1; > + if (statx(fd, "", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT, STATX_INO, &stx)) > + return pr_perror("statx"); > + > + if (stx.stx_dev_major != mstx.stx_dev_major || > + stx.stx_dev_minor != mstx.stx_dev_minor || > + stx.stx_ino != mstx.stx_ino) > + return pr_fail("unmatched dev:ino %x:%x:%llx (expected %x:%x:%llx)\n", > + mstx.stx_dev_major, mstx.stx_dev_minor, mstx.stx_ino, > + stx.stx_dev_major, stx.stx_dev_minor, stx.stx_ino); > + > + ksft_test_result_pass("devices are matched\n"); > + return 0; > +} > + > +int main(int argc, char **argv) > +{ > + int fsfd; > + > + fsfd = sys_fsopen("overlay", 0); > + if (fsfd == -1) { > + ksft_test_result_skip("unable to create overlay mount\n"); > + return 1; > + } > + close(fsfd); > + > + /* Create a new mount namespace to not care about cleaning test mounts. */ > + if (unshare(CLONE_NEWNS) == -1) { > + ksft_test_result_skip("unable to create a new mount namespace\n"); > + return 1; > + } > + > + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) { > + pr_perror("mount"); > + return 1; > + } > + > + ksft_set_plan(1); > + > + if (test()) > + return 1; > + > + ksft_exit_pass(); > + return 0; > +} > diff --git a/tools/testing/selftests/filesystems/overlayfs/log.h b/tools/testing/selftests/filesystems/overlayfs/log.h > new file mode 100644 > index 000000000000..db64df2a8483 > --- /dev/null > +++ b/tools/testing/selftests/filesystems/overlayfs/log.h > @@ -0,0 +1,26 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +#ifndef __SELFTEST_TIMENS_LOG_H__ > +#define __SELFTEST_TIMENS_LOG_H__ > + > +#define pr_msg(fmt, lvl, ...) \ > + ksft_print_msg("[%s] (%s:%d)\t" fmt "\n", \ > + lvl, __FILE__, __LINE__, ##__VA_ARGS__) > + > +#define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__) > + > +#define pr_err(fmt, ...) \ > + ({ \ > + ksft_test_result_error(fmt "\n", ##__VA_ARGS__); \ > + -1; \ > + }) > + > +#define pr_fail(fmt, ...) \ > + ({ \ > + ksft_test_result_fail(fmt, ##__VA_ARGS__); \ > + -1; \ > + }) > + > +#define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__) > + > +#endif > -- > 2.43.0.472.g3155946c3a-goog >
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3b2061d1c1a5..0939a40abb28 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -26,6 +26,7 @@ TARGETS += filesystems TARGETS += filesystems/binderfs TARGETS += filesystems/epoll TARGETS += filesystems/fat +TARGETS += filesystems/overlayfs TARGETS += firmware TARGETS += fpu TARGETS += ftrace diff --git a/tools/testing/selftests/filesystems/overlayfs/.gitignore b/tools/testing/selftests/filesystems/overlayfs/.gitignore new file mode 100644 index 000000000000..52ae618fdd98 --- /dev/null +++ b/tools/testing/selftests/filesystems/overlayfs/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +dev_in_maps diff --git a/tools/testing/selftests/filesystems/overlayfs/Makefile b/tools/testing/selftests/filesystems/overlayfs/Makefile new file mode 100644 index 000000000000..56b2b48a765b --- /dev/null +++ b/tools/testing/selftests/filesystems/overlayfs/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +TEST_GEN_PROGS := dev_in_maps + +CFLAGS := -Wall -Werror + +include ../../lib.mk diff --git a/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c new file mode 100644 index 000000000000..e19ab0e85709 --- /dev/null +++ b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include <inttypes.h> +#include <unistd.h> +#include <stdio.h> + +#include <linux/unistd.h> +#include <linux/types.h> +#include <linux/mount.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/mman.h> +#include <sched.h> +#include <fcntl.h> + +#include "../../kselftest.h" +#include "log.h" + +static int sys_fsopen(const char *fsname, unsigned int flags) +{ + return syscall(__NR_fsopen, fsname, flags); +} + +static int sys_fsconfig(int fd, unsigned int cmd, const char *key, const char *value, int aux) +{ + return syscall(__NR_fsconfig, fd, cmd, key, value, aux); +} + +static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags) +{ + return syscall(__NR_fsmount, fd, flags, attr_flags); +} + +static int sys_move_mount(int from_dfd, const char *from_pathname, + int to_dfd, const char *to_pathname, + unsigned int flags) +{ + return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags); +} + +static long get_file_dev_and_inode(void *addr, struct statx *stx) +{ + char buf[4096]; + FILE *mapf; + + mapf = fopen("/proc/self/maps", "r"); + if (mapf == NULL) + return pr_perror("fopen(/proc/self/maps)"); + + while (fgets(buf, sizeof(buf), mapf)) { + unsigned long start, end; + uint32_t maj, min; + __u64 ino; + + if (sscanf(buf, "%lx-%lx %*s %*s %x:%x %llu", + &start, &end, &maj, &min, &ino) != 5) + return pr_perror("unable to parse: %s", buf); + if (start == (unsigned long)addr) { + stx->stx_dev_major = maj; + stx->stx_dev_minor = min; + stx->stx_ino = ino; + return 0; + } + } + + return pr_err("unable to find the mapping"); +} + +static int ovl_mount(void) +{ + int tmpfs, fsfd, ovl; + + fsfd = sys_fsopen("tmpfs", 0); + if (fsfd == -1) + return pr_perror("fsopen(tmpfs)"); + + if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) + return pr_perror("FSCONFIG_CMD_CREATE"); + + tmpfs = sys_fsmount(fsfd, 0, 0); + if (tmpfs == -1) + return pr_perror("fsmount"); + + close(fsfd); + + /* overlayfs can't be constructed on top of a detached mount. */ + if (sys_move_mount(tmpfs, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH)) + return pr_perror("move_mount"); + close(tmpfs); + + if (mkdir("/tmp/w", 0755) == -1 || + mkdir("/tmp/u", 0755) == -1 || + mkdir("/tmp/l", 0755) == -1) + return pr_perror("mkdir"); + + fsfd = sys_fsopen("overlay", 0); + if (fsfd == -1) + return pr_perror("fsopen(overlay)"); + if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) == -1 || + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", "/tmp/l", 0) == -1 || + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", "/tmp/u", 0) == -1 || + sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", "/tmp/w", 0) == -1) + return pr_perror("fsconfig"); + if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) + return pr_perror("fsconfig"); + ovl = sys_fsmount(fsfd, 0, 0); + if (ovl == -1) + return pr_perror("fsmount"); + + return ovl; +} + +/* + * Check that the file device and inode shown in /proc/pid/maps match values + * returned by stat(2). + */ +static int test(void) +{ + struct statx stx, mstx; + int ovl, fd; + void *addr; + + ovl = ovl_mount(); + if (ovl == -1) + return -1; + + fd = openat(ovl, "test", O_RDWR | O_CREAT, 0644); + if (fd == -1) + return pr_perror("openat"); + + addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + return pr_perror("mmap"); + + if (get_file_dev_and_inode(addr, &mstx)) + return -1; + if (statx(fd, "", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT, STATX_INO, &stx)) + return pr_perror("statx"); + + if (stx.stx_dev_major != mstx.stx_dev_major || + stx.stx_dev_minor != mstx.stx_dev_minor || + stx.stx_ino != mstx.stx_ino) + return pr_fail("unmatched dev:ino %x:%x:%llx (expected %x:%x:%llx)\n", + mstx.stx_dev_major, mstx.stx_dev_minor, mstx.stx_ino, + stx.stx_dev_major, stx.stx_dev_minor, stx.stx_ino); + + ksft_test_result_pass("devices are matched\n"); + return 0; +} + +int main(int argc, char **argv) +{ + int fsfd; + + fsfd = sys_fsopen("overlay", 0); + if (fsfd == -1) { + ksft_test_result_skip("unable to create overlay mount\n"); + return 1; + } + close(fsfd); + + /* Create a new mount namespace to not care about cleaning test mounts. */ + if (unshare(CLONE_NEWNS) == -1) { + ksft_test_result_skip("unable to create a new mount namespace\n"); + return 1; + } + + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) { + pr_perror("mount"); + return 1; + } + + ksft_set_plan(1); + + if (test()) + return 1; + + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/filesystems/overlayfs/log.h b/tools/testing/selftests/filesystems/overlayfs/log.h new file mode 100644 index 000000000000..db64df2a8483 --- /dev/null +++ b/tools/testing/selftests/filesystems/overlayfs/log.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __SELFTEST_TIMENS_LOG_H__ +#define __SELFTEST_TIMENS_LOG_H__ + +#define pr_msg(fmt, lvl, ...) \ + ksft_print_msg("[%s] (%s:%d)\t" fmt "\n", \ + lvl, __FILE__, __LINE__, ##__VA_ARGS__) + +#define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__) + +#define pr_err(fmt, ...) \ + ({ \ + ksft_test_result_error(fmt "\n", ##__VA_ARGS__); \ + -1; \ + }) + +#define pr_fail(fmt, ...) \ + ({ \ + ksft_test_result_fail(fmt, ##__VA_ARGS__); \ + -1; \ + }) + +#define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__) + +#endif
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> --- tools/testing/selftests/Makefile | 1 + .../filesystems/overlayfs/.gitignore | 2 + .../selftests/filesystems/overlayfs/Makefile | 7 + .../filesystems/overlayfs/dev_in_maps.c | 182 ++++++++++++++++++ .../selftests/filesystems/overlayfs/log.h | 26 +++ 5 files changed, 218 insertions(+) create mode 100644 tools/testing/selftests/filesystems/overlayfs/.gitignore create mode 100644 tools/testing/selftests/filesystems/overlayfs/Makefile create mode 100644 tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c create mode 100644 tools/testing/selftests/filesystems/overlayfs/log.h