Commit 22839869 authored by Will Deacon's avatar Will Deacon Committed by Catalin Marinas

signal: Introduce COMPAT_SIGMINSTKSZ for use in compat_sys_sigaltstack

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 <arnd@arndb.de>
Cc: Dominik Brodowski <linux@dominikbrodowski.net>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Oleg Nesterov <oleg@redhat.com>
Reported-by: default avatarSteve McIntyre <steve.mcintyre@arm.com>
Tested-by: default avatarSteve McIntyre <93sam@debian.org>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 03630b3b
...@@ -103,6 +103,9 @@ typedef struct compat_sigaltstack { ...@@ -103,6 +103,9 @@ typedef struct compat_sigaltstack {
compat_size_t ss_size; compat_size_t ss_size;
} compat_stack_t; } compat_stack_t;
#endif #endif
#ifndef COMPAT_MINSIGSTKSZ
#define COMPAT_MINSIGSTKSZ MINSIGSTKSZ
#endif
#define compat_jiffies_to_clock_t(x) \ #define compat_jiffies_to_clock_t(x) \
(((unsigned long)(x) * COMPAT_USER_HZ) / HZ) (((unsigned long)(x) * COMPAT_USER_HZ) / HZ)
......
...@@ -3460,7 +3460,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) ...@@ -3460,7 +3460,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
} }
static int static int
do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp) do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp,
size_t min_ss_size)
{ {
struct task_struct *t = current; struct task_struct *t = current;
...@@ -3490,7 +3491,7 @@ do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp) ...@@ -3490,7 +3491,7 @@ do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp)
ss_size = 0; ss_size = 0;
ss_sp = NULL; ss_sp = NULL;
} else { } else {
if (unlikely(ss_size < MINSIGSTKSZ)) if (unlikely(ss_size < min_ss_size))
return -ENOMEM; return -ENOMEM;
} }
...@@ -3508,7 +3509,8 @@ SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss) ...@@ -3508,7 +3509,8 @@ SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)
if (uss && copy_from_user(&new, uss, sizeof(stack_t))) if (uss && copy_from_user(&new, uss, sizeof(stack_t)))
return -EFAULT; return -EFAULT;
err = do_sigaltstack(uss ? &new : NULL, uoss ? &old : NULL, err = do_sigaltstack(uss ? &new : NULL, uoss ? &old : NULL,
current_user_stack_pointer()); current_user_stack_pointer(),
MINSIGSTKSZ);
if (!err && uoss && copy_to_user(uoss, &old, sizeof(stack_t))) if (!err && uoss && copy_to_user(uoss, &old, sizeof(stack_t)))
err = -EFAULT; err = -EFAULT;
return err; return err;
...@@ -3519,7 +3521,8 @@ int restore_altstack(const stack_t __user *uss) ...@@ -3519,7 +3521,8 @@ int restore_altstack(const stack_t __user *uss)
stack_t new; stack_t new;
if (copy_from_user(&new, uss, sizeof(stack_t))) if (copy_from_user(&new, uss, sizeof(stack_t)))
return -EFAULT; return -EFAULT;
(void)do_sigaltstack(&new, NULL, current_user_stack_pointer()); (void)do_sigaltstack(&new, NULL, current_user_stack_pointer(),
MINSIGSTKSZ);
/* squash all but EFAULT for now */ /* squash all but EFAULT for now */
return 0; return 0;
} }
...@@ -3553,7 +3556,8 @@ static int do_compat_sigaltstack(const compat_stack_t __user *uss_ptr, ...@@ -3553,7 +3556,8 @@ static int do_compat_sigaltstack(const compat_stack_t __user *uss_ptr,
uss.ss_size = uss32.ss_size; uss.ss_size = uss32.ss_size;
} }
ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss,
compat_user_stack_pointer()); compat_user_stack_pointer(),
COMPAT_MINSIGSTKSZ);
if (ret >= 0 && uoss_ptr) { if (ret >= 0 && uoss_ptr) {
compat_stack_t old; compat_stack_t old;
memset(&old, 0, sizeof(old)); memset(&old, 0, sizeof(old));
......
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