From a3badeba1d92149070bb1153601130f4bf5bbcf7 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Wed, 10 Jul 2024 18:34:50 +0200 Subject: [PATCH] Adjust to glibc __rseq_size semantic change In commit 2e456ccf0c34a056e3ccafac4a0c7effef14d918 ("Linux: Make __rseq_size useful for feature detection (bug 31965)") glibc 2.40 changed the meaning of __rseq_size slightly: it is now the size of the active/feature area (20 bytes initially), and not the size of the entire initially defined struct (32 bytes including padding). The reason for the change is that the size including padding does not allow detection of newly added features while previously unused padding is consumed. The prep_libc_rseq_info change in criu/cr-restore.c is not necessary on kernels which have full ptrace support for obtaining rseq information because the code is not used. On older kernels, it is a correctness fix because with size 20 (the new value), rseq registeration would fail. The two other changes are required to make rseq unregistration work in tests. Signed-off-by: Andrei Vagin --- criu/cr-restore.c | 8 ++++++++ test/zdtm/static/rseq00.c | 8 ++++++-- test/zdtm/transition/rseq01.c | 5 ++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/criu/cr-restore.c b/criu/cr-restore.c index 4db2f4ecfc..b95d4f134b 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -2618,7 +2618,15 @@ static void prep_libc_rseq_info(struct rst_rseq_param *rseq) if (!kdat.has_ptrace_get_rseq_conf) { #if defined(__GLIBC__) && defined(RSEQ_SIG) rseq->rseq_abi_pointer = encode_pointer(__criu_thread_pointer() + __rseq_offset); + /* + * Current glibc reports the feature/active size in + * __rseq_size, not the size passed to the kernel. + * This could be 20, but older kernels expect 32 for + * the size argument even if only 20 bytes are used. + */ rseq->rseq_abi_size = __rseq_size; + if (rseq->rseq_abi_size < 32) + rseq->rseq_abi_size = 32; rseq->signature = RSEQ_SIG; #else rseq->rseq_abi_pointer = 0; diff --git a/test/zdtm/static/rseq00.c b/test/zdtm/static/rseq00.c index 471ad6a43f..fd3386cdca 100644 --- a/test/zdtm/static/rseq00.c +++ b/test/zdtm/static/rseq00.c @@ -46,12 +46,16 @@ static inline void *__criu_thread_pointer(void) static inline void unregister_glibc_rseq(void) { struct rseq *rseq = (struct rseq *)((char *)__criu_thread_pointer() + __rseq_offset); + unsigned int size; /* hack: mark glibc rseq structure as failed to register */ rseq->cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED; /* unregister rseq */ - syscall(__NR_rseq, (void *)rseq, __rseq_size, 1, RSEQ_SIG); + size = __rseq_size; + if (__rseq_size < 32) + size = 32; + syscall(__NR_rseq, (void *)rseq, size, 1, RSEQ_SIG); } #else static inline void unregister_glibc_rseq(void) @@ -140,4 +144,4 @@ int main(int argc, char *argv[]) return 0; } -#endif /* #if defined(__x86_64__) */ \ No newline at end of file +#endif /* #if defined(__x86_64__) */ diff --git a/test/zdtm/transition/rseq01.c b/test/zdtm/transition/rseq01.c index 0fbcc2dca0..08a7a8e1a6 100644 --- a/test/zdtm/transition/rseq01.c +++ b/test/zdtm/transition/rseq01.c @@ -33,7 +33,10 @@ static inline void *thread_pointer(void) static inline void unregister_old_rseq(void) { /* unregister rseq */ - syscall(__NR_rseq, (void *)((char *)thread_pointer() + __rseq_offset), __rseq_size, 1, RSEQ_SIG); + unsigned int size = __rseq_size; + if (__rseq_size < 32) + size = 32; + syscall(__NR_rseq, (void *)((char *)thread_pointer() + __rseq_offset), size, 1, RSEQ_SIG); } #else static inline void unregister_old_rseq(void)