Commit 5499802b authored by Christophe Leroy's avatar Christophe Leroy Committed by Michael Ellerman

powerpc/signal32: Fix sigset_t copy

The conversion from __copy_from_user() to __get_user() by
commit d3ccc978 ("powerpc/signal: Use __get_user() to copy
sigset_t") introduced a regression in __get_user_sigset() for
powerpc/32. The bug was subsequently moved into
unsafe_get_user_sigset().

The bug is due to the copied 64 bit value being truncated to
32 bits while being assigned to dst->sig[0]

The regression was reported by users of the Xorg packages distributed in
Debian/powerpc --

    "The symptoms are that the fb screen goes blank, with the backlight
    remaining on and no errors logged in /var/log; wdm (or startx) run
    with no effect (I tried logging in in the blind, with no effect).
    And they are hard to kill, requiring 'kill -KILL ...'"

Fix the regression by copying each word of the sigset, not only the
first one.

__get_user_sigset() was tentatively optimised to copy 64 bits at once
in order to minimise KUAP unlock/lock impact, but the unsafe variant
doesn't suffer that, so it can just copy words.

Fixes: 887f3ceb ("powerpc/signal32: Convert do_setcontext[_tm]() to user access block")
Cc: stable@vger.kernel.org # v5.13+
Reported-by: default avatarFinn Thain <fthain@linux-m68k.org>
Reported-and-tested-by: default avatarStan Johnson <userm57@yahoo.com>
Signed-off-by: default avatarChristophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/99ef38d61c0eb3f79c68942deb0c35995a93a777.1636966353.git.christophe.leroy@csgroup.eu
parent 5b548609
...@@ -25,8 +25,14 @@ static inline int __get_user_sigset(sigset_t *dst, const sigset_t __user *src) ...@@ -25,8 +25,14 @@ static inline int __get_user_sigset(sigset_t *dst, const sigset_t __user *src)
return __get_user(dst->sig[0], (u64 __user *)&src->sig[0]); return __get_user(dst->sig[0], (u64 __user *)&src->sig[0]);
} }
#define unsafe_get_user_sigset(dst, src, label) \ #define unsafe_get_user_sigset(dst, src, label) do { \
unsafe_get_user((dst)->sig[0], (u64 __user *)&(src)->sig[0], label) sigset_t *__dst = dst; \
const sigset_t __user *__src = src; \
int i; \
\
for (i = 0; i < _NSIG_WORDS; i++) \
unsafe_get_user(__dst->sig[i], &__src->sig[i], label); \
} while (0)
#ifdef CONFIG_VSX #ifdef CONFIG_VSX
extern unsigned long copy_vsx_to_user(void __user *to, extern unsigned long copy_vsx_to_user(void __user *to,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment