Skip to content

Commit

Permalink
check: verify ino and dev of overlayfs files in /proc/pid/maps
Browse files Browse the repository at this point in the history
Check that the file device and inode shown in /proc/pid/maps match
values returned by stat(2).

Signed-off-by: Andrei Vagin <[email protected]>
  • Loading branch information
avagin committed Apr 10, 2024
1 parent 9a282a5 commit 6808e77
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 4 deletions.
194 changes: 194 additions & 0 deletions criu/cr-check.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <sched.h>
#include <sys/mount.h>
#include <sys/utsname.h>
#include <sys/stat.h>

#include "../soccr/soccr.h"

Expand Down Expand Up @@ -53,6 +54,8 @@
#include "restorer.h"
#include "uffd.h"
#include "linux/aio_abi.h"
#include "syscall.h"
#include "mount-v2.h"

#include "images/inventory.pb-c.h"

Expand Down Expand Up @@ -1390,6 +1393,195 @@ static int check_pagemap_scan(void)
return 0;
}

/* musl doesn't have a statx wrapper... */
struct staty {
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 stx_ino;
};

static long get_file_dev_and_inode(void *addr, struct staty *stx)
{
char buf[4096];
FILE *mapf;

mapf = fopen("/proc/self/maps", "r");
if (mapf == NULL) {
pr_perror("fopen(/proc/self/maps)");
return -1;
}

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) {
pr_perror("Unable to parse: %s", buf);
return -1;
}
if (start == (unsigned long)addr) {
stx->stx_dev_major = maj;
stx->stx_dev_minor = min;
stx->stx_ino = ino;
return 0;
}
}

pr_err("Unable to find the mapping\n");
return -1;
}

static int ovl_mount(void)
{
int tmpfs, fsfd, ovl;

fsfd = sys_fsopen("tmpfs", 0);
if (fsfd == -1) {
pr_perror("Unable to fsopen tmpfs");
return -1;
}

if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) {
pr_perror("Unable to create tmpfs mount");
return -1;
}

tmpfs = sys_fsmount(fsfd, 0, 0);
if (tmpfs == -1) {
pr_perror("Unable to mount tmpfs");
return -1;
}

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)) {
pr_perror("Unable to attach tmpfs mount");
return -1;
}
close(tmpfs);

if (chdir("/tmp")) {
pr_perror("Unable to change working directory");
return -1;
}

if (mkdir("/tmp/w", 0755) == -1 ||
mkdir("/tmp/u", 0755) == -1 ||
mkdir("/tmp/l", 0755) == -1) {
pr_perror("mkdir");
return -1;
}

fsfd = sys_fsopen("overlay", 0);
if (fsfd == -1) {
pr_perror("Unable to fsopen overlayfs");
return -1;
}
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) {
pr_perror("Unable to configure overlayfs");
return -1;
}
if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) == -1) {
pr_perror("Unable to create overlayfs");
return -1;
}
ovl = sys_fsmount(fsfd, 0, 0);
if (ovl == -1) {
pr_perror("Unable to mount overlayfs");
return -1;
}

return ovl;
}

/*
* Check that the file device and inode shown in /proc/pid/maps match values
* returned by stat(2).
*/
static int do_check_overlayfs_maps(void)
{
struct staty stx, mstx;
struct stat st;
int ovl, fd;
void *addr;

/* Create a new mount namespace to not care about cleaning test mounts. */
if (unshare(CLONE_NEWNS) == -1) {
pr_warn("Unable to create a new mount namespace\n");
return 0;
}

if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) == -1) {
pr_perror("Unable to remount / with MS_SLAVE");
return -1;
}

ovl = ovl_mount();
if (ovl == -1)
return -1;

fd = openat(ovl, "test", O_RDWR | O_CREAT, 0644);
if (fd == -1) {
pr_perror("Unable to open a test file");
return -1;
}

addr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
pr_perror("Unable to map the test file");
return -1;
}

if (get_file_dev_and_inode(addr, &mstx))
return -1;
if (fstat(fd, &st)) {
pr_perror("stat");
return -1;
}
stx.stx_dev_major = major(st.st_dev);
stx.stx_dev_minor = minor(st.st_dev);
stx.stx_ino = st.st_ino;

if (stx.stx_dev_major != mstx.stx_dev_major ||
stx.stx_dev_minor != mstx.stx_dev_minor ||
stx.stx_ino != mstx.stx_ino) {
pr_err("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);
return -1;
}

return 0;
}

static int check_overlayfs_maps(void)
{
pid_t pid;
int status;

pid = fork();
if (pid == -1) {
pr_perror("Unable to fork a child");
return -1;
}
if (pid == 0) {
if (do_check_overlayfs_maps())
exit(1);
exit(0);
}
if (waitpid(pid, &status, 0) == -1) {
pr_perror("waitpid");
return -1;
}
return status == 0 ? 0 : -1;
}

static int (*chk_feature)(void);

/*
Expand Down Expand Up @@ -1511,6 +1703,7 @@ int cr_check(void)
ret |= check_ptrace_get_rseq_conf();
ret |= check_ipv6_freebind();
ret |= check_pagemap_scan();
ret |= check_overlayfs_maps();

if (kdat.lsm == LSMTYPE__APPARMOR)
ret |= check_apparmor_stacking();
Expand Down Expand Up @@ -1633,6 +1826,7 @@ static struct feature_list feature_list[] = {
{ "get_rseq_conf", check_ptrace_get_rseq_conf },
{ "ipv6_freebind", check_ipv6_freebind },
{ "pagemap_scan", check_pagemap_scan },
{ "overlayfs_maps", check_overlayfs_maps },
{ NULL, NULL },
};

Expand Down
14 changes: 10 additions & 4 deletions scripts/ci/run-ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,16 @@ ip net add test
# Check if cap_checkpoint_restore is supported and also if unshare -c is supported.
#
# Do not run this test in a container (see https://github.com/checkpoint-restore/criu/issues/2312).
# This is a temporary workaround until fixed in the kernel.
# The kernel currently does not show correct device and inode numbers in /proc/pid/maps
# for stackable file systems.
if capsh --supports=cap_checkpoint_restore && unshare -c /bin/true && [ ! -e /run/.containerenv ]; then
# Before v6.8-rc1~215^2~6, the kernel currently did not show correct device and
# inode numbers in /proc/pid/maps for stackable file systems.
skip=0
findmnt -no FSTYPE / | grep overlay && {
./criu/criu check --feature overlayfs_maps || skip=1
}
unshare -c /bin/true || skip=1
capsh --supports=cap_checkpoint_restore || skip=1

if [ "$skip" == 0 ]; then
make -C test/zdtm/ cleanout
rm -rf test/dump
setcap cap_checkpoint_restore,cap_sys_ptrace+eip criu/criu
Expand Down

0 comments on commit 6808e77

Please sign in to comment.