mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
pie/restorer: unregister (g)libc rseq before memory restoration
Fresh glibc does rseq registration by default during start_thread(). [ see https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=95e114a0919d844d8fe07839cb6538b7f5ee920e ] This cause process crashes during memory restore procedure, because memory which corresponds to the struct rseq will be unmapped and overriden in __export_restore_task. Let's perform rseq unregistration just before unmap_old_vmas(). To achieve that we need to determine (struct rseq) address at first while we are in Glibc (we do that in prep_libc_rseq_info using Glibc exported symbols). See also ("nptl: Add public rseq symbols and <sys/rseq.h>") https://sourceware.org/git?p=glibc.git;a=commit;h=c901c3e764d7c7079f006b4e21e877d5036eb4f5 ("nptl: Add <thread_pointer.h> for defining __thread_pointer") https://sourceware.org/git?p=glibc.git;a=commit;h=8dbeb0561eeb876f557ac9eef5721912ec074ea5 TODO: do the same for musl-libc if it will start to register rseq by default Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
This commit is contained in:
parent
e1799e5305
commit
f70ddab24e
4 changed files with 62 additions and 0 deletions
|
|
@ -23,6 +23,7 @@
|
|||
#include "common/compiler.h"
|
||||
|
||||
#include "linux/mount.h"
|
||||
#include "linux/rseq.h"
|
||||
|
||||
#include "clone-noasan.h"
|
||||
#include "cr_options.h"
|
||||
|
|
@ -3012,6 +3013,32 @@ static int prep_rseq(struct rst_rseq_param *rseq, ThreadCoreEntry *tc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) && defined(RSEQ_SIG)
|
||||
static void prep_libc_rseq_info(struct rst_rseq_param *rseq)
|
||||
{
|
||||
if (!kdat.has_rseq) {
|
||||
rseq->rseq_abi_pointer = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
rseq->rseq_abi_pointer = encode_pointer(__criu_thread_pointer() + __rseq_offset);
|
||||
rseq->rseq_abi_size = __rseq_size;
|
||||
rseq->signature = RSEQ_SIG;
|
||||
}
|
||||
#else
|
||||
static void prep_libc_rseq_info(struct rst_rseq_param *rseq)
|
||||
{
|
||||
/*
|
||||
* TODO: handle built-in rseq on other libc'ies like musl
|
||||
* We can do that using get_rseq_conf kernel feature.
|
||||
*
|
||||
* For now we just assume that other libc libraries are
|
||||
* not registering rseq by default.
|
||||
*/
|
||||
rseq->rseq_abi_pointer = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static rlim_t decode_rlim(rlim_t ival)
|
||||
{
|
||||
return ival == -1 ? RLIM_INFINITY : ival;
|
||||
|
|
@ -3665,6 +3692,8 @@ static int sigreturn_restore(pid_t pid, struct task_restore_args *task_args, uns
|
|||
strncpy(task_args->comm, core->tc->comm, TASK_COMM_LEN - 1);
|
||||
task_args->comm[TASK_COMM_LEN - 1] = 0;
|
||||
|
||||
prep_libc_rseq_info(&task_args->libc_rseq);
|
||||
|
||||
/*
|
||||
* Fill up per-thread data.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
#ifndef _UAPI_LINUX_RSEQ_H
|
||||
#define _UAPI_LINUX_RSEQ_H
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include("sys/rseq.h")
|
||||
#include <sys/rseq.h>
|
||||
#include "asm/thread_pointer.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __GLIBC_HAVE_KERNEL_RSEQ
|
||||
/*
|
||||
* linux/rseq.h
|
||||
*
|
||||
|
|
@ -49,6 +57,7 @@ struct rseq_cs {
|
|||
__u64 post_commit_offset;
|
||||
__u64 abort_ip;
|
||||
} __attribute__((aligned(4 * sizeof(__u64))));
|
||||
#endif /* __GLIBC_HAVE_KERNEL_RSEQ */
|
||||
|
||||
/*
|
||||
* We have to have our own copy of struct rseq definition because
|
||||
|
|
|
|||
|
|
@ -229,6 +229,12 @@ struct task_restore_args {
|
|||
int lsm_type;
|
||||
int child_subreaper;
|
||||
bool has_clone3_set_tid;
|
||||
|
||||
/*
|
||||
* info about rseq from libc used to
|
||||
* unregister it before memory restoration procedure
|
||||
*/
|
||||
struct rst_rseq_param libc_rseq;
|
||||
} __aligned(64);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1122,6 +1122,15 @@ void __export_unmap(void)
|
|||
sys_munmap(bootstrap_start, bootstrap_len - vdso_rt_size);
|
||||
}
|
||||
|
||||
static void unregister_libc_rseq(struct rst_rseq_param *rseq)
|
||||
{
|
||||
if (!rseq->rseq_abi_pointer)
|
||||
return;
|
||||
|
||||
/* can't fail if rseq is registered */
|
||||
sys_rseq(decode_pointer(rseq->rseq_abi_pointer), rseq->rseq_abi_size, 1, rseq->signature);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function unmaps all VMAs, which don't belong to
|
||||
* the restored process or the restorer.
|
||||
|
|
@ -1461,6 +1470,15 @@ long __export_restore_task(struct task_restore_args *args)
|
|||
goto core_restore_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* We may have rseq registered already if CRIU compiled against
|
||||
* a fresh Glibc with rseq support. Anyway, we need to unregister it
|
||||
* before doing unmap_old_vmas or we will get SIGSEGV from the kernel,
|
||||
* for instance once the kernel will want to update (struct rseq).cpu_id field:
|
||||
* https://github.com/torvalds/linux/blob/ce522ba9ef7e/kernel/rseq.c#L89
|
||||
*/
|
||||
unregister_libc_rseq(&args->libc_rseq);
|
||||
|
||||
if (unmap_old_vmas((void *)args->premmapped_addr, args->premmapped_len, bootstrap_start, bootstrap_len,
|
||||
args->task_size))
|
||||
goto core_restore_end;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue