Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/checkpoint-restore/criu.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>2021-12-24 18:57:54 +0300
committerAndrei Vagin <avagin@gmail.com>2022-04-29 03:53:52 +0300
commitf70ddab24eeb99fcbab0015c8338b673d002d5e6 (patch)
tree4d9e45d273361f01c305b9114a3d0f9cec31ad6f
parente1799e530584e9d47acb4700ef7a7a44c4af1443 (diff)
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>
-rw-r--r--criu/cr-restore.c29
-rw-r--r--criu/include/linux/rseq.h9
-rw-r--r--criu/include/restorer.h6
-rw-r--r--criu/pie/restorer.c18
4 files changed, 62 insertions, 0 deletions
diff --git a/criu/cr-restore.c b/criu/cr-restore.c
index ed576fc55..0751c5b8d 100644
--- a/criu/cr-restore.c
+++ b/criu/cr-restore.c
@@ -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.
*/
diff --git a/criu/include/linux/rseq.h b/criu/include/linux/rseq.h
index b227aefdf..a47876e66 100644
--- a/criu/include/linux/rseq.h
+++ b/criu/include/linux/rseq.h
@@ -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
diff --git a/criu/include/restorer.h b/criu/include/restorer.h
index 2e21da522..325804e44 100644
--- a/criu/include/restorer.h
+++ b/criu/include/restorer.h
@@ -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);
/*
diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c
index 376a5025d..f80b68359 100644
--- a/criu/pie/restorer.c
+++ b/criu/pie/restorer.c
@@ -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;