From: James Morse james.morse@arm.com
maillist inclusion category: feature bugzilla: 46790 CVE: NA
Reference: https://github.com/norov/linux/commits/ilp32-5.2
--------------------------------
compat_ptrace_request() lacks handlers for PTRACE_{G,S}ETSIGMASK, instead using those in ptrace_request(). The compat variant should read a compat_sigset_t from userspace instead of ptrace_request()s sigset_t.
While compat_sigset_t is the same size as sigset_t, it is defined as 2xu32, instead of a single u64. On a big-endian CPU this means that compat_sigset_t is passed to user-space using middle-endianness, where the least-significant u32 is written most significant byte first.
If ptrace_request()s code is used userspace will read the most significant u32 where it expected the least significant.
Instead of duplicating ptrace_request()s code as a special case in the arch code, handle it here.
Fixes: 29000caecbe87 ("ptrace: add ability to get/set signal-blocked mask") CC: Andrey Vagin avagin@openvz.org Signed-off-by: James Morse james.morse@arm.com
Yury: Replace sigset_{to,from}_compat() with new {get,put}_compat_sigset() Signed-off-by: Yury Norov ynorov@caviumnetworks.com Signed-off-by: Yury Norov ynorov@marvell.com Signed-off-by: Xiongfeng Wang wangxiongfeng2@huawei.com Acked-by: Xie XiuQi xiexiuqi@huawei.com Signed-off-by: Chen Jun chenjun102@huawei.com Patchwork Links: http://patchwork.huawei.com/project/hulk5.10/list/?series=12937 Signed-off-by: Chen Jiahao chenjiahao16@huawei.com --- kernel/ptrace.c | 52 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 443057bee87c..a8682842c840 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -1028,6 +1028,24 @@ ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size, } #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */
+static int ptrace_setsigmask(struct task_struct *child, sigset_t *new_set) +{ + sigdelsetmask(new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + /* + * Every thread does recalc_sigpending() after resume, so + * retarget_shared_pending() and recalc_sigpending() are not + * called here. + */ + spin_lock_irq(&child->sighand->siglock); + child->blocked = *new_set; + spin_unlock_irq(&child->sighand->siglock); + + clear_tsk_restore_sigmask(child); + + return 0; +} + int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -1106,20 +1124,7 @@ int ptrace_request(struct task_struct *child, long request, break; }
- sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); - - /* - * Every thread does recalc_sigpending() after resume, so - * retarget_shared_pending() and recalc_sigpending() are not - * called here. - */ - spin_lock_irq(&child->sighand->siglock); - child->blocked = new_set; - spin_unlock_irq(&child->sighand->siglock); - - clear_tsk_restore_sigmask(child); - - ret = 0; + ret = ptrace_setsigmask(child, &new_set); break; }
@@ -1341,6 +1346,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, { compat_ulong_t __user *datap = compat_ptr(data); compat_ulong_t word; + sigset_t new_set; kernel_siginfo_t siginfo; int ret;
@@ -1380,6 +1386,24 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, if (!ret) ret = ptrace_setsiginfo(child, &siginfo); break; + case PTRACE_GETSIGMASK: + if (addr != sizeof(compat_sigset_t)) + return -EINVAL; + + ret = put_compat_sigset((compat_sigset_t __user *) datap, + &child->blocked, sizeof(compat_sigset_t)); + break; + case PTRACE_SETSIGMASK: + if (addr != sizeof(compat_sigset_t)) + return -EINVAL; + + ret = get_compat_sigset(&new_set, + (compat_sigset_t __user *) datap); + if (ret) + break; + + ret = ptrace_setsigmask(child, &new_set); + break; #ifdef CONFIG_HAVE_ARCH_TRACEHOOK case PTRACE_GETREGSET: case PTRACE_SETREGSET: