From cacddf19dad339f963b0b01f7174091b90c49e5d Mon Sep 17 00:00:00 2001
From: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
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 <fweimer@redhat.com>
Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
---
 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 d11d28173a63..5b5b41dfc8bd 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 83d867e75bab..a3959c99260d 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 <compel/ptrace.h>
 
 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 bc5dccab1804..0f7d5fc8fb1d 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;
 	}
-- 
2.45.2

