mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 09:58:09 +00:00
compel: fix the stack test
The stack test incorrectly assumed the page immediately following the stack pointer could never be changed. This doesn't work, because this page can be a part of another mapping. This commit introduces a dedicated "stack redzone," a small guard region directly after the stack. The stack test is modified to specifically check for corruption within this redzone. Signed-off-by: Andrei Vagin <avagin@gmail.com>
This commit is contained in:
parent
4249c11213
commit
24ea8befcc
@ -13,6 +13,15 @@
|
|||||||
|
|
||||||
#define PARASITE_START_AREA_MIN (4096)
|
#define PARASITE_START_AREA_MIN (4096)
|
||||||
|
|
||||||
|
#define PARASITE_STACK_SIZE (16 << 10)
|
||||||
|
/*
|
||||||
|
* A stack redzone is a small, protected region of memory located immediately
|
||||||
|
* after a parasite stack. It is intended to remain unchanged. While it can be
|
||||||
|
* implemented as a guard page, we want to avoid the overhead of additional
|
||||||
|
* remote system calls.
|
||||||
|
*/
|
||||||
|
#define PARASITE_STACK_REDZONE 128
|
||||||
|
|
||||||
extern int __must_check compel_interrupt_task(int pid);
|
extern int __must_check compel_interrupt_task(int pid);
|
||||||
|
|
||||||
struct seize_task_status {
|
struct seize_task_status {
|
||||||
|
@ -38,8 +38,6 @@
|
|||||||
#define UNIX_PATH_MAX (sizeof(struct sockaddr_un) - (size_t)((struct sockaddr_un *)0)->sun_path)
|
#define UNIX_PATH_MAX (sizeof(struct sockaddr_un) - (size_t)((struct sockaddr_un *)0)->sun_path)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PARASITE_STACK_SIZE (16 << 10)
|
|
||||||
|
|
||||||
#ifndef SECCOMP_MODE_DISABLED
|
#ifndef SECCOMP_MODE_DISABLED
|
||||||
#define SECCOMP_MODE_DISABLED 0
|
#define SECCOMP_MODE_DISABLED 0
|
||||||
#endif
|
#endif
|
||||||
@ -1064,7 +1062,7 @@ int compel_infect_no_daemon(struct parasite_ctl *ctl, unsigned long nr_threads,
|
|||||||
|
|
||||||
p += RESTORE_STACK_SIGFRAME;
|
p += RESTORE_STACK_SIGFRAME;
|
||||||
p += PARASITE_STACK_SIZE;
|
p += PARASITE_STACK_SIZE;
|
||||||
ctl->rstack = ctl->remote_map + p;
|
ctl->rstack = ctl->remote_map + p - PARASITE_STACK_REDZONE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* x86-64 ABI requires a 16 bytes aligned stack.
|
* x86-64 ABI requires a 16 bytes aligned stack.
|
||||||
@ -1078,7 +1076,7 @@ int compel_infect_no_daemon(struct parasite_ctl *ctl, unsigned long nr_threads,
|
|||||||
|
|
||||||
if (nr_threads > 1) {
|
if (nr_threads > 1) {
|
||||||
p += PARASITE_STACK_SIZE;
|
p += PARASITE_STACK_SIZE;
|
||||||
ctl->r_thread_stack = ctl->remote_map + p;
|
ctl->r_thread_stack = ctl->remote_map + p - PARASITE_STACK_REDZONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = arch_fetch_sas(ctl, ctl->rsigframe);
|
ret = arch_fetch_sas(ctl, ctl->rsigframe);
|
||||||
|
@ -50,70 +50,6 @@ static void *get_parasite_rstack_start(struct parasite_ctl *ctl)
|
|||||||
return rstack_start;
|
return rstack_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int page_writable(struct parasite_ctl *ctl, int pid, void *page)
|
|
||||||
{
|
|
||||||
FILE *maps;
|
|
||||||
size_t maps_line_len = 0;
|
|
||||||
char *maps_line = NULL;
|
|
||||||
char victim_maps_path[6 + 11 + 5 + 1];
|
|
||||||
int written;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (((uintptr_t)page & (page_size() - 1)) != 0) {
|
|
||||||
fprintf(stderr, "Page address not aligned\n");
|
|
||||||
ret = -1;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
written = snprintf(victim_maps_path, sizeof(victim_maps_path), "/proc/%d/maps", pid);
|
|
||||||
if (written < 0 || written >= sizeof(victim_maps_path)) {
|
|
||||||
fprintf(stderr, "Failed to create path string to victim's /proc/%d/maps file\n", pid);
|
|
||||||
ret = -1;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
maps = fopen(victim_maps_path, "r");
|
|
||||||
if (maps == NULL) {
|
|
||||||
perror("Can't open victim's /proc/$pid/maps");
|
|
||||||
ret = -1;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (getline(&maps_line, &maps_line_len, maps) != -1) {
|
|
||||||
unsigned long vmstart, vmend;
|
|
||||||
char r, w;
|
|
||||||
|
|
||||||
if (sscanf(maps_line, "%lx-%lx %c%c", &vmstart, &vmend, &r, &w) < 4) {
|
|
||||||
fprintf(stderr, "Can't parse victim's /proc/%d/maps; line: %s\n", pid, maps_line);
|
|
||||||
ret = -1;
|
|
||||||
goto free_linebuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page >= (void *)vmstart && page < (void *)vmend) {
|
|
||||||
if (w == 'w') {
|
|
||||||
if (r != 'r') {
|
|
||||||
fprintf(stderr, "Expecting writable memory to also be readable");
|
|
||||||
ret = -1;
|
|
||||||
goto free_linebuf;
|
|
||||||
}
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errno) {
|
|
||||||
perror("Can't read victim's /proc/$pid/maps");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free_linebuf:
|
|
||||||
free(maps_line);
|
|
||||||
fclose(maps);
|
|
||||||
done:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *read_proc_mem(int pid, void *offset, size_t len)
|
static void *read_proc_mem(int pid, void *offset, size_t len)
|
||||||
{
|
{
|
||||||
char victim_mem_path[6 + 11 + 4 + 1];
|
char victim_mem_path[6 + 11 + 4 + 1];
|
||||||
@ -153,51 +89,6 @@ freebuf:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int save_data_near_stack(struct parasite_ctl *ctl, int pid, void *stack, void **saved_data,
|
|
||||||
size_t *saved_data_size)
|
|
||||||
{
|
|
||||||
size_t page_mask = page_size() - 1;
|
|
||||||
size_t saved_size = 0;
|
|
||||||
size_t stack_size_last_page = (uintptr_t)stack & page_mask;
|
|
||||||
void *next_page = stack;
|
|
||||||
|
|
||||||
if (stack_size_last_page != 0) {
|
|
||||||
size_t empty_space_last_page = page_size() - stack_size_last_page;
|
|
||||||
saved_size = min(empty_space_last_page, (size_t)SAVED_DATA_MAX);
|
|
||||||
next_page += page_size() - stack_size_last_page;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (saved_size < SAVED_DATA_MAX && next_page != NULL) {
|
|
||||||
switch (page_writable(ctl, pid, next_page)) {
|
|
||||||
case 1:
|
|
||||||
saved_size = min((size_t)(saved_size + page_size()), (size_t)SAVED_DATA_MAX);
|
|
||||||
next_page += page_size();
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
next_page = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (saved_size > 0) {
|
|
||||||
void *sd;
|
|
||||||
|
|
||||||
sd = read_proc_mem(pid, stack, saved_size);
|
|
||||||
if (sd == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*saved_data = sd;
|
|
||||||
} else {
|
|
||||||
*saved_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*saved_data_size = saved_size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_saved_data(struct parasite_ctl *ctl, int pid, void *stack, void *saved_data, size_t saved_data_size)
|
static int check_saved_data(struct parasite_ctl *ctl, int pid, void *stack, void *saved_data, size_t saved_data_size)
|
||||||
{
|
{
|
||||||
if (saved_data != NULL) {
|
if (saved_data != NULL) {
|
||||||
@ -221,7 +112,7 @@ static int do_infection(int pid)
|
|||||||
struct infect_ctx *ictx;
|
struct infect_ctx *ictx;
|
||||||
int *arg;
|
int *arg;
|
||||||
void *stack;
|
void *stack;
|
||||||
size_t saved_data_size;
|
size_t saved_data_size = PARASITE_STACK_REDZONE;
|
||||||
int saved_data_check;
|
int saved_data_check;
|
||||||
|
|
||||||
compel_log_init(print_vmsg, COMPEL_LOG_DEBUG);
|
compel_log_init(print_vmsg, COMPEL_LOG_DEBUG);
|
||||||
@ -257,8 +148,6 @@ static int do_infection(int pid)
|
|||||||
err_and_ret("Can't register cleanup function with atexit\n");
|
err_and_ret("Can't register cleanup function with atexit\n");
|
||||||
|
|
||||||
stack = get_parasite_rstack_start(ctl);
|
stack = get_parasite_rstack_start(ctl);
|
||||||
if (save_data_near_stack(ctl, pid, stack, &saved_data, &saved_data_size))
|
|
||||||
err_and_ret("Can't save data above stack\n");
|
|
||||||
|
|
||||||
if (compel_start_daemon(ctl))
|
if (compel_start_daemon(ctl))
|
||||||
err_and_ret("Can't start daemon in victim\n");
|
err_and_ret("Can't start daemon in victim\n");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user