mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 01:51:51 +00:00
check: verify ino and dev of overlayfs files in /proc/pid/maps
Check that the file device and inode shown in /proc/pid/maps match values returned by stat(2). Signed-off-by: Andrei Vagin <avagin@gmail.com>
This commit is contained in:
parent
e07ffa04b0
commit
1c2a3d7faa
194
criu/cr-check.c
194
criu/cr-check.c
@ -22,6 +22,7 @@
|
||||
#include <sched.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../soccr/soccr.h"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
/*
|
||||
@ -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();
|
||||
@ -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 },
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user