mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-29 05:18:00 +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 <sched.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "../soccr/soccr.h"
|
#include "../soccr/soccr.h"
|
||||||
|
|
||||||
@ -53,6 +54,8 @@
|
|||||||
#include "restorer.h"
|
#include "restorer.h"
|
||||||
#include "uffd.h"
|
#include "uffd.h"
|
||||||
#include "linux/aio_abi.h"
|
#include "linux/aio_abi.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
#include "mount-v2.h"
|
||||||
|
|
||||||
#include "images/inventory.pb-c.h"
|
#include "images/inventory.pb-c.h"
|
||||||
|
|
||||||
@ -1390,6 +1393,195 @@ static int check_pagemap_scan(void)
|
|||||||
return 0;
|
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);
|
static int (*chk_feature)(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1511,6 +1703,7 @@ int cr_check(void)
|
|||||||
ret |= check_ptrace_get_rseq_conf();
|
ret |= check_ptrace_get_rseq_conf();
|
||||||
ret |= check_ipv6_freebind();
|
ret |= check_ipv6_freebind();
|
||||||
ret |= check_pagemap_scan();
|
ret |= check_pagemap_scan();
|
||||||
|
ret |= check_overlayfs_maps();
|
||||||
|
|
||||||
if (kdat.lsm == LSMTYPE__APPARMOR)
|
if (kdat.lsm == LSMTYPE__APPARMOR)
|
||||||
ret |= check_apparmor_stacking();
|
ret |= check_apparmor_stacking();
|
||||||
@ -1633,6 +1826,7 @@ static struct feature_list feature_list[] = {
|
|||||||
{ "get_rseq_conf", check_ptrace_get_rseq_conf },
|
{ "get_rseq_conf", check_ptrace_get_rseq_conf },
|
||||||
{ "ipv6_freebind", check_ipv6_freebind },
|
{ "ipv6_freebind", check_ipv6_freebind },
|
||||||
{ "pagemap_scan", check_pagemap_scan },
|
{ "pagemap_scan", check_pagemap_scan },
|
||||||
|
{ "overlayfs_maps", check_overlayfs_maps },
|
||||||
{ NULL, NULL },
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -290,10 +290,16 @@ ip net add test
|
|||||||
# Check if cap_checkpoint_restore is supported and also if unshare -c is supported.
|
# 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).
|
# 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.
|
# Before v6.8-rc1~215^2~6, the kernel currently did not show correct device and
|
||||||
# The kernel currently does not show correct device and inode numbers in /proc/pid/maps
|
# inode numbers in /proc/pid/maps for stackable file systems.
|
||||||
# for stackable file systems.
|
skip=0
|
||||||
if capsh --supports=cap_checkpoint_restore && unshare -c /bin/true && [ ! -e /run/.containerenv ]; then
|
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
|
make -C test/zdtm/ cleanout
|
||||||
rm -rf test/dump
|
rm -rf test/dump
|
||||||
setcap cap_checkpoint_restore,cap_sys_ptrace+eip criu/criu
|
setcap cap_checkpoint_restore,cap_sys_ptrace+eip criu/criu
|
||||||
|
Loading…
x
Reference in New Issue
Block a user