From f0b0a64d1a2339a14de621c6c42516a03a2e95bc Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Wed, 20 Jul 2022 14:36:28 +0300 Subject: [PATCH] cr-restore: rseq: dynamically handle *libc with rseq Before this patch we assumed that CRIU is compiled against the same GLibc as it runs with. But as we see from real world examples like #1935 it's not always true. The idea of this patch is to detect rseq configuration for the main CRIU process and use it to unregister rseq for all further child processes. It's correct, because we restore pstree using clone*() syscalls, don't use exec*() (!) syscalls, so rseq gets inherited in the kernel and rseq configuration remains the same for all children processes. This will prevent issues like this: https://github.com/checkpoint-restore/criu/issues/1935 Suggested-by: Florian Weimer Signed-off-by: Alexander Mikhalitsyn --- criu/cr-restore.c | 16 ++++++++-------- criu/include/kerndat.h | 2 ++ criu/kerndat.c | 25 +++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/criu/cr-restore.c b/criu/cr-restore.c index d11d28173a..5b5b41dfc8 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -3103,14 +3103,14 @@ static void prep_libc_rseq_info(struct rst_rseq_param *rseq) #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; + if (!kdat.has_rseq || !kdat.has_ptrace_get_rseq_conf) { + rseq->rseq_abi_pointer = 0; + return; + } + + rseq->rseq_abi_pointer = kdat.libc_rseq_conf.rseq_abi_pointer; + rseq->rseq_abi_size = kdat.libc_rseq_conf.rseq_abi_size; + rseq->signature = kdat.libc_rseq_conf.signature; } #endif diff --git a/criu/include/kerndat.h b/criu/include/kerndat.h index 83d867e75b..a3959c9926 100644 --- a/criu/include/kerndat.h +++ b/criu/include/kerndat.h @@ -7,6 +7,7 @@ #include "asm/kerndat.h" #include "util-vdso.h" #include "hugetlb.h" +#include struct stat; @@ -82,6 +83,7 @@ struct kerndat_s { bool has_openat2; bool has_rseq; bool has_ptrace_get_rseq_conf; + struct __ptrace_rseq_configuration libc_rseq_conf; }; extern struct kerndat_s kdat; diff --git a/criu/kerndat.c b/criu/kerndat.c index bc5dccab18..0f7d5fc8fb 100644 --- a/criu/kerndat.c +++ b/criu/kerndat.c @@ -923,6 +923,7 @@ static int kerndat_has_ptrace_get_rseq_conf(void) pid_t pid; int len; struct __ptrace_rseq_configuration rseq; + int ret = 0; pid = fork_and_ptrace_attach(NULL); if (pid < 0) @@ -930,6 +931,9 @@ static int kerndat_has_ptrace_get_rseq_conf(void) len = ptrace(PTRACE_GET_RSEQ_CONFIGURATION, pid, sizeof(rseq), &rseq); if (len != sizeof(rseq)) { + if (kdat.has_ptrace_get_rseq_conf) + ret = 1; /* we should update kdat */ + kdat.has_ptrace_get_rseq_conf = false; pr_info("ptrace(PTRACE_GET_RSEQ_CONFIGURATION) is not supported\n"); goto out; @@ -940,16 +944,27 @@ static int kerndat_has_ptrace_get_rseq_conf(void) * we need to pay attention to that and, possibly, make changes on the CRIU side. */ if (rseq.flags != 0) { + if (kdat.has_ptrace_get_rseq_conf) + ret = 1; /* we should update kdat */ + kdat.has_ptrace_get_rseq_conf = false; pr_err("ptrace(PTRACE_GET_RSEQ_CONFIGURATION): rseq.flags != 0\n"); } else { + if (!kdat.has_ptrace_get_rseq_conf) + ret = 1; /* we should update kdat */ + kdat.has_ptrace_get_rseq_conf = true; + + if (memcmp(&kdat.libc_rseq_conf, &rseq, sizeof(rseq))) + ret = 1; /* we should update kdat */ + + kdat.libc_rseq_conf = rseq; } out: kill(pid, SIGKILL); waitpid(pid, NULL, 0); - return 0; + return ret; } int kerndat_sockopt_buf_lock(void) @@ -1472,6 +1487,12 @@ int kerndat_try_load_new(void) if (ret < 0) return ret; + ret = kerndat_has_ptrace_get_rseq_conf(); + if (ret < 0) { + pr_err("kerndat_has_ptrace_get_rseq_conf failed when initializing kerndat.\n"); + return ret; + } + /* New information is found, we need to save to the cache */ if (ret) kerndat_save_cache(); @@ -1657,7 +1678,7 @@ int kerndat_init(void) pr_err("kerndat_has_rseq failed when initializing kerndat.\n"); ret = -1; } - if (!ret && kerndat_has_ptrace_get_rseq_conf()) { + if (!ret && (kerndat_has_ptrace_get_rseq_conf() < 0)) { pr_err("kerndat_has_ptrace_get_rseq_conf failed when initializing kerndat.\n"); ret = -1; }