From: Greg Kroah-Hartman Date: Tue, 15 Jul 2025 15:43:28 +0000 (+0200) Subject: 5.10-stable patches X-Git-Tag: v5.4.296~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e29c4b6c311f572c5a0ce322cd03d183ad7f8369;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: rseq-fix-segfault-on-registration-when-rseq_cs-is-non-zero.patch --- diff --git a/queue-5.10/rseq-fix-segfault-on-registration-when-rseq_cs-is-non-zero.patch b/queue-5.10/rseq-fix-segfault-on-registration-when-rseq_cs-is-non-zero.patch new file mode 100644 index 0000000000..05231267ad --- /dev/null +++ b/queue-5.10/rseq-fix-segfault-on-registration-when-rseq_cs-is-non-zero.patch @@ -0,0 +1,156 @@ +From fd881d0a085fc54354414aed990ccf05f282ba53 Mon Sep 17 00:00:00 2001 +From: Michael Jeanson +Date: Thu, 6 Mar 2025 16:12:21 -0500 +Subject: rseq: Fix segfault on registration when rseq_cs is non-zero + +From: Michael Jeanson + +commit fd881d0a085fc54354414aed990ccf05f282ba53 upstream. + +The rseq_cs field is documented as being set to 0 by user-space prior to +registration, however this is not currently enforced by the kernel. This +can result in a segfault on return to user-space if the value stored in +the rseq_cs field doesn't point to a valid struct rseq_cs. + +The correct solution to this would be to fail the rseq registration when +the rseq_cs field is non-zero. However, some older versions of glibc +will reuse the rseq area of previous threads without clearing the +rseq_cs field and will also terminate the process if the rseq +registration fails in a secondary thread. This wasn't caught in testing +because in this case the leftover rseq_cs does point to a valid struct +rseq_cs. + +What we can do is clear the rseq_cs field on registration when it's +non-zero which will prevent segfaults on registration and won't break +the glibc versions that reuse rseq areas on thread creation. + +Signed-off-by: Michael Jeanson +Signed-off-by: Ingo Molnar +Reviewed-by: Mathieu Desnoyers +Cc: Peter Zijlstra +Cc: Linus Torvalds +Link: https://lore.kernel.org/r/20250306211223.109455-1-mjeanson@efficios.com +Signed-off-by: Greg Kroah-Hartman +--- + kernel/rseq.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 48 insertions(+), 12 deletions(-) + +--- a/kernel/rseq.c ++++ b/kernel/rseq.c +@@ -112,6 +112,29 @@ static int rseq_reset_rseq_cpu_id(struct + return 0; + } + ++/* ++ * Get the user-space pointer value stored in the 'rseq_cs' field. ++ */ ++static int rseq_get_rseq_cs_ptr_val(struct rseq __user *rseq, u64 *rseq_cs) ++{ ++ if (!rseq_cs) ++ return -EFAULT; ++ ++#ifdef CONFIG_64BIT ++ if (get_user(*rseq_cs, &rseq->rseq_cs)) ++ return -EFAULT; ++#else ++ if (copy_from_user(rseq_cs, &rseq->rseq_cs, sizeof(*rseq_cs))) ++ return -EFAULT; ++#endif ++ ++ return 0; ++} ++ ++/* ++ * If the rseq_cs field of 'struct rseq' contains a valid pointer to ++ * user-space, copy 'struct rseq_cs' from user-space and validate its fields. ++ */ + static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs) + { + struct rseq_cs __user *urseq_cs; +@@ -120,17 +143,16 @@ static int rseq_get_rseq_cs(struct task_ + u32 sig; + int ret; + +-#ifdef CONFIG_64BIT +- if (get_user(ptr, &t->rseq->rseq_cs)) +- return -EFAULT; +-#else +- if (copy_from_user(&ptr, &t->rseq->rseq_cs, sizeof(ptr))) +- return -EFAULT; +-#endif ++ ret = rseq_get_rseq_cs_ptr_val(t->rseq, &ptr); ++ if (ret) ++ return ret; ++ ++ /* If the rseq_cs pointer is NULL, return a cleared struct rseq_cs. */ + if (!ptr) { + memset(rseq_cs, 0, sizeof(*rseq_cs)); + return 0; + } ++ /* Check that the pointer value fits in the user-space process space. */ + if (ptr >= TASK_SIZE) + return -EINVAL; + urseq_cs = (struct rseq_cs __user *)(unsigned long)ptr; +@@ -199,7 +221,7 @@ static int rseq_need_restart(struct task + return !!(event_mask & ~flags); + } + +-static int clear_rseq_cs(struct task_struct *t) ++static int clear_rseq_cs(struct rseq __user *rseq) + { + /* + * The rseq_cs field is set to NULL on preemption or signal +@@ -210,9 +232,9 @@ static int clear_rseq_cs(struct task_str + * Set rseq_cs to NULL. + */ + #ifdef CONFIG_64BIT +- return put_user(0UL, &t->rseq->rseq_cs); ++ return put_user(0UL, &rseq->rseq_cs); + #else +- if (clear_user(&t->rseq->rseq_cs, sizeof(t->rseq->rseq_cs))) ++ if (clear_user(&rseq->rseq_cs, sizeof(rseq->rseq_cs))) + return -EFAULT; + return 0; + #endif +@@ -244,11 +266,11 @@ static int rseq_ip_fixup(struct pt_regs + * Clear the rseq_cs pointer and return. + */ + if (!in_rseq_cs(ip, &rseq_cs)) +- return clear_rseq_cs(t); ++ return clear_rseq_cs(t->rseq); + ret = rseq_need_restart(t, rseq_cs.flags); + if (ret <= 0) + return ret; +- ret = clear_rseq_cs(t); ++ ret = clear_rseq_cs(t->rseq); + if (ret) + return ret; + trace_rseq_ip_fixup(ip, rseq_cs.start_ip, rseq_cs.post_commit_offset, +@@ -324,6 +346,7 @@ SYSCALL_DEFINE4(rseq, struct rseq __user + int, flags, u32, sig) + { + int ret; ++ u64 rseq_cs; + + if (flags & RSEQ_FLAG_UNREGISTER) { + if (flags & ~RSEQ_FLAG_UNREGISTER) +@@ -369,6 +392,19 @@ SYSCALL_DEFINE4(rseq, struct rseq __user + return -EINVAL; + if (!access_ok(rseq, rseq_len)) + return -EFAULT; ++ ++ /* ++ * If the rseq_cs pointer is non-NULL on registration, clear it to ++ * avoid a potential segfault on return to user-space. The proper thing ++ * to do would have been to fail the registration but this would break ++ * older libcs that reuse the rseq area for new threads without ++ * clearing the fields. ++ */ ++ if (rseq_get_rseq_cs_ptr_val(rseq, &rseq_cs)) ++ return -EFAULT; ++ if (rseq_cs && clear_rseq_cs(rseq)) ++ return -EFAULT; ++ + current->rseq = rseq; + current->rseq_sig = sig; + /* diff --git a/queue-5.10/series b/queue-5.10/series index e41cab6d59..608e591353 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -206,3 +206,4 @@ x86-bugs-add-a-transient-scheduler-attacks-mitigation.patch kvm-x86-add-support-for-cpuid-leaf-0x80000021.patch kvm-svm-advertise-tsa-cpuid-bits-to-guests.patch x86-process-move-the-buffer-clearing-before-monitor.patch +rseq-fix-segfault-on-registration-when-rseq_cs-is-non-zero.patch