From: Will Deacon Date: Wed, 5 Sep 2018 14:34:42 +0000 (+0100) Subject: signal: Introduce COMPAT_SIGMINSTKSZ for use in compat_sys_sigaltstack X-Git-Tag: v4.9.147~61 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1e7066a4540b39d475782122db60cf93633dca48;p=thirdparty%2Fkernel%2Fstable.git signal: Introduce COMPAT_SIGMINSTKSZ for use in compat_sys_sigaltstack commit 22839869f21ab3850fbbac9b425ccc4c0023926f upstream. The sigaltstack(2) system call fails with -ENOMEM if the new alternative signal stack is found to be smaller than SIGMINSTKSZ. On architectures such as arm64, where the native value for SIGMINSTKSZ is larger than the compat value, this can result in an unexpected error being reported to a compat task. See, for example: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=904385 This patch fixes the problem by extending do_sigaltstack to take the minimum signal stack size as an additional parameter, allowing the native and compat system call entry code to pass in their respective values. COMPAT_SIGMINSTKSZ is just defined as SIGMINSTKSZ if it has not been defined by the architecture. Cc: Arnd Bergmann Cc: Dominik Brodowski Cc: "Eric W. Biederman" Cc: Andrew Morton Cc: Al Viro Cc: Oleg Nesterov Reported-by: Steve McIntyre Tested-by: Steve McIntyre <93sam@debian.org> Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas [signal: Fix up cherry-pick conflicts for 22839869f21a] Signed-off-by: Steve McIntyre <93sam@debian.org> Signed-off-by: Greg Kroah-Hartman --- diff --git a/include/linux/compat.h b/include/linux/compat.h index d8535a430cafc..fab35daf87596 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -67,6 +67,9 @@ typedef struct compat_sigaltstack { compat_size_t ss_size; } compat_stack_t; #endif +#ifndef COMPAT_MINSIGSTKSZ +#define COMPAT_MINSIGSTKSZ MINSIGSTKSZ +#endif #define compat_jiffies_to_clock_t(x) \ (((unsigned long)(x) * COMPAT_USER_HZ) / HZ) diff --git a/kernel/signal.c b/kernel/signal.c index 424306163edc4..049929a5f4ce0 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -3116,7 +3116,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) } static int -do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp) +do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp, + size_t min_ss_size) { stack_t oss; int error; @@ -3155,9 +3156,8 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s ss_size = 0; ss_sp = NULL; } else { - error = -ENOMEM; - if (ss_size < MINSIGSTKSZ) - goto out; + if (unlikely(ss_size < min_ss_size)) + return -ENOMEM; } current->sas_ss_sp = (unsigned long) ss_sp; @@ -3180,12 +3180,14 @@ out: } SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss) { - return do_sigaltstack(uss, uoss, current_user_stack_pointer()); + return do_sigaltstack(uss, uoss, current_user_stack_pointer(), + MINSIGSTKSZ); } int restore_altstack(const stack_t __user *uss) { - int err = do_sigaltstack(uss, NULL, current_user_stack_pointer()); + int err = do_sigaltstack(uss, NULL, current_user_stack_pointer(), + MINSIGSTKSZ); /* squash all but EFAULT for now */ return err == -EFAULT ? err : 0; } @@ -3226,7 +3228,8 @@ COMPAT_SYSCALL_DEFINE2(sigaltstack, set_fs(KERNEL_DS); ret = do_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL), (stack_t __force __user *) &uoss, - compat_user_stack_pointer()); + compat_user_stack_pointer(), + COMPAT_MINSIGSTKSZ); set_fs(seg); if (ret >= 0 && uoss_ptr) { if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(compat_stack_t)) ||