-
Notifications
You must be signed in to change notification settings - Fork 610
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
Showing
2 changed files
with
190 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,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" | ||
|
||
|
@@ -1382,6 +1384,184 @@ static int check_ipv6_freebind(void) | |
return 0; | ||
} | ||
|
||
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) { | ||
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) { | ||
Check warning on line 1404 in criu/cr-check.c GitHub Actions / build
|
||
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 statx stx, mstx; | ||
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 (statx(fd, "", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT, STATX_INO, &stx)) { | ||
pr_perror("statx"); | ||
return -1; | ||
} | ||
|
||
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, | ||
Check warning on line 1535 in criu/cr-check.c GitHub Actions / build
|
||
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); | ||
|
||
/* | ||
|
@@ -1502,6 +1682,7 @@ int cr_check(void) | |
ret |= check_openat2(); | ||
ret |= check_ptrace_get_rseq_conf(); | ||
ret |= check_ipv6_freebind(); | ||
ret |= check_overlayfs_maps(); | ||
|
||
if (kdat.lsm == LSMTYPE__APPARMOR) | ||
ret |= check_apparmor_stacking(); | ||
|
@@ -1623,6 +1804,7 @@ static struct feature_list feature_list[] = { | |
{ "openat2", check_openat2 }, | ||
{ "get_rseq_conf", check_ptrace_get_rseq_conf }, | ||
{ "ipv6_freebind", check_ipv6_freebind }, | ||
{ "overlayfs_maps", check_overlayfs_maps }, | ||
{ NULL, NULL }, | ||
}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters