lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon,  3 Jan 2022 10:19:49 -0800
From:   Walt Drummond <walt@...mmond.us>
To:     Alexander Viro <viro@...iv.linux.org.uk>,
        Oleg Nesterov <oleg@...hat.com>,
        Paolo Bonzini <pbonzini@...hat.com>
Cc:     linux-kernel@...r.kernel.org, Walt Drummond <walt@...mmond.us>,
        linux-fsdevel@...r.kernel.org, kvm@...r.kernel.org
Subject: [RFC PATCH 1/8] signals: Make the real-time signal system calls accept different sized sigset_t from user space.

The real-time signals API provides a mechanism for user space to tell
the kernel how many bytes is has in sigset_t. Make these system calls
use that mechanism and accept differently sized sigset_t.

Add a value to the auxvec to inform user space of the maximum size
sigset_t the kernel can accept.

Signed-off-by: Walt Drummond <walt@...mmond.us>
---
 fs/binfmt_elf.c             |   1 +
 fs/binfmt_elf_fdpic.c       |   1 +
 fs/signalfd.c               |  24 +++---
 include/linux/compat.h      |  98 +++++++++++++++++++++---
 include/linux/signal.h      |  62 +++++++++++++++
 include/uapi/linux/auxvec.h |   1 +
 kernel/compat.c             |  24 ------
 kernel/ptrace.c             |  16 ++--
 kernel/signal.c             | 147 +++++++++++++++++++-----------------
 virt/kvm/kvm_main.c         |  16 ++--
 10 files changed, 257 insertions(+), 133 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index a813b70f594e..7133515fd386 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -274,6 +274,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
 #ifdef ELF_HWCAP2
 	NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2);
 #endif
+	NEW_AUX_ENT(AT_SIGSET_SZ, SIGSETSIZE_MAX);
 	NEW_AUX_ENT(AT_EXECFN, bprm->exec);
 	if (k_platform) {
 		NEW_AUX_ENT(AT_PLATFORM,
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 6d8fd6030cbb..09249dc4364b 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -659,6 +659,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
 	NEW_AUX_ENT(AT_EGID,	(elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid));
 	NEW_AUX_ENT(AT_SECURE,	bprm->secureexec);
 	NEW_AUX_ENT(AT_EXECFN,	bprm->exec);
+	NEW_AUX_ENT(AT_SIGSET_SZ, SIGSETSIZE_MAX);
 
 #ifdef ARCH_DLINFO
 	nr = 0;
diff --git a/fs/signalfd.c b/fs/signalfd.c
index 040e1cf90528..12fdc282e299 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -311,24 +311,24 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags)
 SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
 		size_t, sizemask, int, flags)
 {
+	int ret;
 	sigset_t mask;
 
-	if (sizemask != sizeof(sigset_t))
-		return -EINVAL;
-	if (copy_from_user(&mask, user_mask, sizeof(mask)))
-		return -EFAULT;
+	ret = copy_sigset_from_user(&mask, user_mask, sizemask);
+	if (ret)
+		return ret;
 	return do_signalfd4(ufd, &mask, flags);
 }
 
 SYSCALL_DEFINE3(signalfd, int, ufd, sigset_t __user *, user_mask,
 		size_t, sizemask)
 {
+	int ret;
 	sigset_t mask;
 
-	if (sizemask != sizeof(sigset_t))
-		return -EINVAL;
-	if (copy_from_user(&mask, user_mask, sizeof(mask)))
-		return -EFAULT;
+	ret = copy_sigset_from_user(&mask, user_mask, sizemask);
+	if (ret)
+		return ret;
 	return do_signalfd4(ufd, &mask, 0);
 }
 
@@ -338,11 +338,11 @@ static long do_compat_signalfd4(int ufd,
 			compat_size_t sigsetsize, int flags)
 {
 	sigset_t mask;
+	int ret;
 
-	if (sigsetsize != sizeof(compat_sigset_t))
-		return -EINVAL;
-	if (get_compat_sigset(&mask, user_mask))
-		return -EFAULT;
+	ret = copy_compat_sigset_from_user(&mask, user_mask, sigsetsize);
+	if (ret)
+		return ret;
 	return do_signalfd4(ufd, &mask, flags);
 }
 
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 1c758b0e0359..ecdbff1d2218 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -407,33 +407,109 @@ int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
 int get_compat_sigevent(struct sigevent *event,
 		const struct compat_sigevent __user *u_event);
 
-extern int get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat);
-
 /*
  * Defined inline such that size can be compile time constant, which avoids
  * CONFIG_HARDENED_USERCOPY complaining about copies from task_struct
  */
 static inline int
-put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set,
-		  unsigned int size)
+copy_compat_sigset_to_user(compat_sigset_t __user *compat, const sigset_t *set,
+			   size_t sigsetsize)
 {
-	/* size <= sizeof(compat_sigset_t) <= sizeof(sigset_t) */
+	size_t copybytes;
 #if defined(__BIG_ENDIAN) && defined(CONFIG_64BIT)
 	compat_sigset_t v;
+	int i;
+#endif
+
+	if (!valid_sigsetsize(sigsetsize))
+		return -EINVAL;
+
+	copybytes = min(sizeof(compat_sigset_t), sigsetsize);
+
+#if defined(__BIG_ENDIAN) && defined(CONFIG_64BIT)
+	switch (_NSIG_WORDS) {
+	default:
+		for (i = 0; i < _NSIG_WORDS; i++) {
+			v.sig[(i * 2)]     = set->sig[i];
+			v.sig[(i * 2) + 1] = set->sig[i] >> 32;
+		}
+		break;
+	case 4:
+		v.sig[7] = (set->sig[3] >> 32);
+		v.sig[6] =  set->sig[3];
+		fallthrough;
+	case 3:
+		v.sig[5] = (set->sig[2] >> 32);
+		v.sig[4] =  set->sig[2];
+		fallthrough;
+	case 2:
+		v.sig[3] = (set->sig[1] >> 32);
+		v.sig[2] =  set->sig[1];
+		fallthrough;
+	case 1:
+		v.sig[1] = (set->sig[0] >> 32);
+		v.sig[0] =  set->sig[0];
+	}
+	if (copy_to_user(compat, &v, copybytes))
+		return -EFAULT;
+#else
+	if (copy_to_user(compat, set, copybytes))
+		return -EFAULT;
+#endif
+	/* Zero any unused part of mask */
+	if (sigsetsize > sizeof(compat_sigset_t)) {
+		if (clear_user((char *)compat + copybytes,
+			       sigsetsize - sizeof(compat_sigset_t)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+#define put_compat_sigset(set, compat, size)		\
+	copy_compat_sigset_to_user((set), (compat), (size))
+
+static inline int
+copy_compat_sigset_from_user(sigset_t *set,
+			     const compat_sigset_t __user *compat, size_t size)
+{
+#if defined(__BIG_ENDIAN) && defined(CONFIG_64BIT)
+	compat_sigset_t v;
+	int i;
+#endif
+
+	if (!valid_sigsetsize(size))
+		return -EINVAL;
+
+#if defined(__BIG_ENDIAN) && defined(CONFIG_64BIT)
+	if (copy_from_user(&v, compat, min(sizeof(compat_sigset_t), size)))
+		return -EFAULT;
 	switch (_NSIG_WORDS) {
-	case 4: v.sig[7] = (set->sig[3] >> 32); v.sig[6] = set->sig[3];
+	default:
+		for (i = 0; i < _NSIG_WORDS; i++) {
+			set->sig[i] =    v.sig[(i * 2)] |
+				(((long) v.sig[(i * 2) + 1]) << 32);
+		}
+		break;
+	case 4:
+		set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32);
 		fallthrough;
-	case 3: v.sig[5] = (set->sig[2] >> 32); v.sig[4] = set->sig[2];
+	case 3:
+		set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32);
 		fallthrough;
-	case 2: v.sig[3] = (set->sig[1] >> 32); v.sig[2] = set->sig[1];
+	case 2:
+		set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32);
 		fallthrough;
-	case 1: v.sig[1] = (set->sig[0] >> 32); v.sig[0] = set->sig[0];
+	case 1:
+		set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32);
 	}
-	return copy_to_user(compat, &v, size) ? -EFAULT : 0;
 #else
-	return copy_to_user(compat, set, size) ? -EFAULT : 0;
+	if (copy_from_user(set, compat, min(sizeof(compat_sigset_t), size)))
+		return -EFAULT;
 #endif
+	return 0;
 }
+#define get_compat_sigset(set, compat)					\
+	copy_compat_sigset_from_user((set), (compat), sizeof(compat_sigset_t))
 
 #ifdef CONFIG_CPU_BIG_ENDIAN
 #define unsafe_put_compat_sigset(compat, set, label) do {		\
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 3f96a6374e4f..c66d4f520228 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -5,6 +5,7 @@
 #include <linux/bug.h>
 #include <linux/signal_types.h>
 #include <linux/string.h>
+#include <linux/uaccess.h>
 
 struct task_struct;
 
@@ -260,6 +261,67 @@ static inline void siginitsetinv(sigset_t *set, unsigned long mask)
 
 #endif /* __HAVE_ARCH_SIG_SETOPS */
 
+/* Safely copy a sigset_t from user space handling any differences in
+ * size between user space and kernel sigset_t.  We don't use
+ * copy_struct_from_user() here as we can't ensure that in the case
+ * where sigisetsize > sizeof(sigset_t), the unused bytes are zeroed.
+ *
+ * SIGSETSIZE_MIN *must* be 8 bytes and cannot change.
+ *
+ * SIGSETSIZE_MAX shouldn't be too small, nor should it be too large.
+ * We've somewhat randomly picked 128 bytes to keep this sync'ed with
+ * glibc and musl; this can be changed as needed.
+ */
+
+#define SIGSETSIZE_MIN 8
+#define SIGSETSIZE_MAX 128
+
+static inline int valid_sigsetsize(size_t sigsetsize)
+{
+	return  sigsetsize >= SIGSETSIZE_MIN &&
+		sigsetsize <= SIGSETSIZE_MAX;
+}
+
+static inline int copy_sigset_from_user(sigset_t *kmask,
+					const sigset_t __user *umask,
+					size_t sigsetsize)
+{
+	if (!valid_sigsetsize(sigsetsize))
+		return -EINVAL;
+
+	if (kmask == NULL)
+		return -EFAULT;
+
+	sigemptyset(kmask);
+
+	if (copy_from_user(kmask, umask, min(sizeof(sigset_t), sigsetsize)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static inline int copy_sigset_to_user(sigset_t __user *umask,
+				      sigset_t *kmask,
+				      size_t sigsetsize)
+{
+	size_t copybytes;
+
+	if (!valid_sigsetsize(sigsetsize))
+		return -EINVAL;
+
+	copybytes = min(sizeof(sigset_t), sigsetsize);
+	if (copy_to_user(umask, kmask, copybytes))
+		return -EFAULT;
+
+	/* Zero unused parts of umask */
+	if (sigsetsize > copybytes) {
+		if (clear_user((char *)umask + copybytes,
+			       sigsetsize - copybytes))
+			return -EFAULT;
+	}
+	return 0;
+}
+
 static inline void init_sigpending(struct sigpending *sig)
 {
 	sigemptyset(&sig->signal);
diff --git a/include/uapi/linux/auxvec.h b/include/uapi/linux/auxvec.h
index c7e502bf5a6f..752184abf620 100644
--- a/include/uapi/linux/auxvec.h
+++ b/include/uapi/linux/auxvec.h
@@ -30,6 +30,7 @@
 				 * differ from AT_PLATFORM. */
 #define AT_RANDOM 25	/* address of 16 random bytes */
 #define AT_HWCAP2 26	/* extension of AT_HWCAP */
+#define AT_SIGSET_SZ 27	/* sizeof(sigset_t) */
 
 #define AT_EXECFN  31	/* filename of program */
 
diff --git a/kernel/compat.c b/kernel/compat.c
index 55551989d9da..cc2438f4070c 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -245,27 +245,3 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
 	user_write_access_end();
 	return -EFAULT;
 }
-
-int
-get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat)
-{
-#ifdef __BIG_ENDIAN
-	compat_sigset_t v;
-	if (copy_from_user(&v, compat, sizeof(compat_sigset_t)))
-		return -EFAULT;
-	switch (_NSIG_WORDS) {
-	case 4: set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32 );
-		fallthrough;
-	case 3: set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32 );
-		fallthrough;
-	case 2: set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32 );
-		fallthrough;
-	case 1: set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32 );
-	}
-#else
-	if (copy_from_user(set, compat, sizeof(compat_sigset_t)))
-		return -EFAULT;
-#endif
-	return 0;
-}
-EXPORT_SYMBOL_GPL(get_compat_sigset);
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index f8589bf8d7dc..2f7ee345a629 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -1074,8 +1074,9 @@ int ptrace_request(struct task_struct *child, long request,
 
 	case PTRACE_GETSIGMASK: {
 		sigset_t *mask;
+		size_t sigsetsize = (size_t) addr;
 
-		if (addr != sizeof(sigset_t)) {
+		if (!valid_sigsetsize(sigsetsize) == 0) {
 			ret = -EINVAL;
 			break;
 		}
@@ -1085,7 +1086,7 @@ int ptrace_request(struct task_struct *child, long request,
 		else
 			mask = &child->blocked;
 
-		if (copy_to_user(datavp, mask, sizeof(sigset_t)))
+		if (copy_sigset_to_user(datavp, mask, sigsetsize))
 			ret = -EFAULT;
 		else
 			ret = 0;
@@ -1095,16 +1096,11 @@ int ptrace_request(struct task_struct *child, long request,
 
 	case PTRACE_SETSIGMASK: {
 		sigset_t new_set;
+		size_t sigsetsize = (size_t) addr;
 
-		if (addr != sizeof(sigset_t)) {
-			ret = -EINVAL;
-			break;
-		}
-
-		if (copy_from_user(&new_set, datavp, sizeof(sigset_t))) {
-			ret = -EFAULT;
+		ret = copy_sigset_from_user(&new_set, datavp, sigsetsize);
+		if (ret)
 			break;
-		}
 
 		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
 
diff --git a/kernel/signal.c b/kernel/signal.c
index 487bf4f5dadf..94b1828ae973 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -3091,13 +3091,14 @@ EXPORT_SYMBOL(sigprocmask);
 int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
 {
 	sigset_t kmask;
+	int ret;
 
 	if (!umask)
 		return 0;
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-	if (copy_from_user(&kmask, umask, sizeof(sigset_t)))
-		return -EFAULT;
+
+	ret = copy_sigset_from_user(&kmask, umask, sigsetsize);
+	if (ret)
+		return ret;
 
 	set_restore_sigmask();
 	current->saved_sigmask = current->blocked;
@@ -3111,13 +3112,14 @@ int set_compat_user_sigmask(const compat_sigset_t __user *umask,
 			    size_t sigsetsize)
 {
 	sigset_t kmask;
+	int ret;
 
 	if (!umask)
 		return 0;
-	if (sigsetsize != sizeof(compat_sigset_t))
-		return -EINVAL;
-	if (get_compat_sigset(&kmask, umask))
-		return -EFAULT;
+
+	ret = copy_compat_sigset_from_user(&kmask, umask, sigsetsize);
+	if (ret)
+		return ret;
 
 	set_restore_sigmask();
 	current->saved_sigmask = current->blocked;
@@ -3140,14 +3142,13 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,
 	sigset_t old_set, new_set;
 	int error;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
+	if (!valid_sigsetsize(sigsetsize))
 		return -EINVAL;
 
 	old_set = current->blocked;
 
 	if (nset) {
-		if (copy_from_user(&new_set, nset, sizeof(sigset_t)))
+		if (copy_sigset_from_user(&new_set, nset, sigsetsize))
 			return -EFAULT;
 		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
 
@@ -3157,7 +3158,7 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,
 	}
 
 	if (oset) {
-		if (copy_to_user(oset, &old_set, sizeof(sigset_t)))
+		if (copy_sigset_to_user(oset, &old_set, sigsetsize))
 			return -EFAULT;
 	}
 
@@ -3168,16 +3169,16 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,
 COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset,
 		compat_sigset_t __user *, oset, compat_size_t, sigsetsize)
 {
-	sigset_t old_set = current->blocked;
+	sigset_t old_set, new_set;
+	int error;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
+	if (!valid_sigsetsize(sigsetsize))
 		return -EINVAL;
 
+	old_set = current->blocked;
+
 	if (nset) {
-		sigset_t new_set;
-		int error;
-		if (get_compat_sigset(&new_set, nset))
+		if (copy_compat_sigset_from_user(&new_set, nset, sigsetsize))
 			return -EFAULT;
 		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
 
@@ -3185,7 +3186,12 @@ COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset,
 		if (error)
 			return error;
 	}
-	return oset ? put_compat_sigset(oset, &old_set, sizeof(*oset)) : 0;
+	if (oset) {
+		if (copy_compat_sigset_to_user(oset, &old_set, sigsetsize))
+			return -EFAULT;
+	}
+
+	return 0;
 }
 #endif
 
@@ -3210,12 +3216,12 @@ SYSCALL_DEFINE2(rt_sigpending, sigset_t __user *, uset, size_t, sigsetsize)
 {
 	sigset_t set;
 
-	if (sigsetsize > sizeof(*uset))
+	if (!valid_sigsetsize(sigsetsize))
 		return -EINVAL;
 
 	do_sigpending(&set);
 
-	if (copy_to_user(uset, &set, sigsetsize))
+	if (copy_sigset_to_user(uset, &set, sigsetsize))
 		return -EFAULT;
 
 	return 0;
@@ -3227,12 +3233,15 @@ COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset,
 {
 	sigset_t set;
 
-	if (sigsetsize > sizeof(*uset))
+	if (!valid_sigsetsize(sigsetsize))
 		return -EINVAL;
 
 	do_sigpending(&set);
 
-	return put_compat_sigset(uset, &set, sigsetsize);
+	if (copy_compat_sigset_to_user(uset, &set, sigsetsize))
+		return -EFAULT;
+
+	return 0;
 }
 #endif
 
@@ -3627,12 +3636,9 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese,
 	kernel_siginfo_t info;
 	int ret;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (copy_from_user(&these, uthese, sizeof(these)))
-		return -EFAULT;
+	ret = copy_sigset_from_user(&these, uthese, sigsetsize);
+	if (ret)
+		return ret;
 
 	if (uts) {
 		if (get_timespec64(&ts, uts))
@@ -3660,11 +3666,9 @@ SYSCALL_DEFINE4(rt_sigtimedwait_time32, const sigset_t __user *, uthese,
 	kernel_siginfo_t info;
 	int ret;
 
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (copy_from_user(&these, uthese, sizeof(these)))
-		return -EFAULT;
+	ret = copy_sigset_from_user(&these, uthese, sigsetsize);
+	if (ret)
+		return ret;
 
 	if (uts) {
 		if (get_old_timespec32(&ts, uts))
@@ -3692,11 +3696,9 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait_time64, compat_sigset_t __user *, uthese,
 	kernel_siginfo_t info;
 	long ret;
 
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (get_compat_sigset(&s, uthese))
-		return -EFAULT;
+	ret = copy_compat_sigset_from_user(&s, uthese, sigsetsize);
+	if (ret)
+		return ret;
 
 	if (uts) {
 		if (get_timespec64(&t, uts))
@@ -3723,11 +3725,9 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait_time32, compat_sigset_t __user *, uthese,
 	kernel_siginfo_t info;
 	long ret;
 
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (get_compat_sigset(&s, uthese))
-		return -EFAULT;
+	ret = copy_compat_sigset_from_user(&s, uthese, sigsetsize);
+	if (ret)
+		return ret;
 
 	if (uts) {
 		if (get_old_timespec32(&t, uts))
@@ -4370,21 +4370,36 @@ SYSCALL_DEFINE4(rt_sigaction, int, sig,
 		size_t, sigsetsize)
 {
 	struct k_sigaction new_sa, old_sa;
+	size_t sa_len = sizeof(struct sigaction) - sizeof(sigset_t);
 	int ret;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
+	/* struct sigaction contains a sigset_t; handle cases where
+	 * user and kernel sizes of sigset_t differ.
+	 */
 
-	if (act && copy_from_user(&new_sa.sa, act, sizeof(new_sa.sa)))
-		return -EFAULT;
+	memset(&new_sa.sa, 0, sizeof(struct sigaction));
+
+	if (act) {
+		if (copy_from_user(&new_sa.sa, act, sa_len))
+			return -EFAULT;
+		ret = copy_sigset_from_user(&new_sa.sa.sa_mask, &act->sa_mask,
+					    sigsetsize);
+		if (ret)
+			return ret;
+	}
 
 	ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL);
 	if (ret)
 		return ret;
 
-	if (oact && copy_to_user(oact, &old_sa.sa, sizeof(old_sa.sa)))
-		return -EFAULT;
+	if (oact) {
+		if (copy_to_user(oact, &old_sa.sa, sa_len))
+			return -EFAULT;
+		ret = copy_sigset_to_user(&oact->sa_mask, &old_sa.sa.sa_mask,
+					  sigsetsize);
+		if (ret)
+			return ret;
+	}
 
 	return 0;
 }
@@ -4400,8 +4415,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig,
 #endif
 	int ret;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(compat_sigset_t))
+	if (!valid_sigsetsize(sigsetsize))
 		return -EINVAL;
 
 	if (act) {
@@ -4412,7 +4426,8 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig,
 		ret |= get_user(restorer, &act->sa_restorer);
 		new_ka.sa.sa_restorer = compat_ptr(restorer);
 #endif
-		ret |= get_compat_sigset(&new_ka.sa.sa_mask, &act->sa_mask);
+		ret |= copy_compat_sigset_from_user(&new_ka.sa.sa_mask,
+						    &act->sa_mask, sigsetsize);
 		ret |= get_user(new_ka.sa.sa_flags, &act->sa_flags);
 		if (ret)
 			return -EFAULT;
@@ -4422,8 +4437,8 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig,
 	if (!ret && oact) {
 		ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), 
 			       &oact->sa_handler);
-		ret |= put_compat_sigset(&oact->sa_mask, &old_ka.sa.sa_mask,
-					 sizeof(oact->sa_mask));
+		ret |= copy_compat_sigset_to_user(&oact->sa_mask,
+						  &old_ka.sa.sa_mask, sigsetsize);
 		ret |= put_user(old_ka.sa.sa_flags, &oact->sa_flags);
 #ifdef __ARCH_HAS_SA_RESTORER
 		ret |= put_user(ptr_to_compat(old_ka.sa.sa_restorer),
@@ -4590,13 +4605,11 @@ static int sigsuspend(sigset_t *set)
 SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)
 {
 	sigset_t newset;
+	int ret;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (copy_from_user(&newset, unewset, sizeof(newset)))
-		return -EFAULT;
+	ret = copy_sigset_from_user(&newset, unewset, sigsetsize);
+	if (ret)
+		return ret;
 	return sigsuspend(&newset);
 }
  
@@ -4604,13 +4617,11 @@ SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)
 COMPAT_SYSCALL_DEFINE2(rt_sigsuspend, compat_sigset_t __user *, unewset, compat_size_t, sigsetsize)
 {
 	sigset_t newset;
+	int ret;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (get_compat_sigset(&newset, unewset))
-		return -EFAULT;
+	ret = copy_compat_sigset_from_user(&newset, unewset, sigsetsize);
+	if (ret)
+		return ret;
 	return sigsuspend(&newset);
 }
 #endif
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 7851f3a1b5f7..c8b3645c9a7d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3891,8 +3891,10 @@ static long kvm_vcpu_ioctl(struct file *filp,
 			if (copy_from_user(&kvm_sigmask, argp,
 					   sizeof(kvm_sigmask)))
 				goto out;
-			r = -EINVAL;
-			if (kvm_sigmask.len != sizeof(sigset))
+			r = copy_sigset_from_user(&sigset,
+				       (sigset_t __user *) &sigmask_arg->sigset,
+				       kvm_sigmask.len);
+			if (r)
 				goto out;
 			r = -EFAULT;
 			if (copy_from_user(&sigset, sigmask_arg->sigset,
@@ -3963,12 +3965,10 @@ static long kvm_vcpu_compat_ioctl(struct file *filp,
 			if (copy_from_user(&kvm_sigmask, argp,
 					   sizeof(kvm_sigmask)))
 				goto out;
-			r = -EINVAL;
-			if (kvm_sigmask.len != sizeof(compat_sigset_t))
-				goto out;
-			r = -EFAULT;
-			if (get_compat_sigset(&sigset,
-					      (compat_sigset_t __user *)sigmask_arg->sigset))
+			r = copy_compat_sigset_from_user(&sigset,
+				(compat_sigset_t __user *) &sigmask_arg->sigset,
+				kvm_sigmask.len);
+			if (r)
 				goto out;
 			r = kvm_vcpu_ioctl_set_sigmask(vcpu, &sigset);
 		} else
-- 
2.30.2

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ