Generic compat_sys_rt_sigqueueinfo

Kyle McMartin
2006-10-30T15:59:13Z

Every 32-on-64 arch (except parisc) was implementing effectively the
same sys32_rt_sigqueueinfo. Add a generic implementation taken from
x86_64 and add it to kernel/compat.c, which required the addition of
copy_siginfo_from_user32 on mips and powerpc. sparc64 had a sufficient
implementation for this purpose, so crib it for both those platforms.

This yields a nice clean up and is one step towards cleaning up some
of the compat signals mess parisc has been carrying out of tree for a few
years.

Incorporated Arnd's suggestion of not bothering to call sys_rt_sigqueueinfo
and just doing the full syscall in compat_sys_rt_sigqueueinfo.

Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
--
[N.B. the modification to powerpc is probably wrong, since it seems
to require a wrapper for handling the signed ints like sparc64 and s390
have. -- Kyle]
--
arch/ia64/ia32/ia32_entry.S | 2 +-
arch/ia64/ia32/ia32_signal.c | 15 ---------------
arch/mips/kernel/scall64-n32.S | 2 +-
arch/mips/kernel/scall64-o32.S | 2 +-
arch/mips/kernel/signal32.c | 31 ++++++++++++++++---------------
arch/parisc/kernel/syscall_table.S | 2 +-
arch/powerpc/kernel/signal_32.c | 33 +++++++++++++--------------------
arch/s390/kernel/compat_linux.c | 15 ---------------
arch/s390/kernel/compat_wrapper.S | 2 +-
arch/sparc64/kernel/sys_sparc32.c | 16 ----------------
arch/x86_64/ia32/ia32entry.S | 2 +-
arch/x86_64/ia32/sys_ia32.c | 15 ---------------
kernel/compat.c | 18 ++++++++++++++++++
13 files changed, 53 insertions(+), 102 deletions(-)

diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S
index a32cd59..8d41c2c 100644
--- a/arch/ia64/ia32/ia32_entry.S
+++ b/arch/ia64/ia32/ia32_entry.S
@@ -388,7 +388,7 @@ ia32_syscall_table:
data8 sys32_rt_sigprocmask /* 175 */
data8 sys_rt_sigpending
data8 compat_sys_rt_sigtimedwait
- data8 sys32_rt_sigqueueinfo
+ data8 compat_sys_rt_sigqueueinfo
data8 sys32_rt_sigsuspend
data8 sys32_pread /* 180 */
data8 sys32_pwrite
diff --git a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c
index b3355a9..00341ff 100644
--- a/arch/ia64/ia32/ia32_signal.c
+++ b/arch/ia64/ia32/ia32_signal.c
@@ -588,21 +588,6 @@ sys32_rt_sigprocmask (int how, compat_si
}

asmlinkage long
-sys32_rt_sigqueueinfo (int pid, int sig, compat_siginfo_t __user *uinfo)
-{
- mm_segment_t old_fs = get_fs();
- siginfo_t info;
- int ret;
-
- if (copy_siginfo_from_user32(&info, uinfo))
- return -EFAULT;
- set_fs(KERNEL_DS);
- ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info);
- set_fs(old_fs);
- return ret;
-}
-
-asmlinkage long
sys32_sigaction (int sig, struct old_sigaction32 __user *act, struct old_sigaction32 __user *oact)
{
struct k_sigaction new_ka, old_ka;
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 67b92a1..013efe9 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -247,7 +247,7 @@ EXPORT(sysn32_call_table)
PTR sys_capset
PTR sys32_rt_sigpending /* 6125 */
PTR compat_sys_rt_sigtimedwait
- PTR sys32_rt_sigqueueinfo
+ PTR compat_sys_rt_sigqueueinfo
PTR sysn32_rt_sigsuspend
PTR sys32_sigaltstack
PTR compat_sys_utime /* 6130 */
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 2875c4a..e9bc5a6 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -403,7 +403,7 @@ sys_call_table:
PTR sys32_rt_sigprocmask /* 4195 */
PTR sys32_rt_sigpending
PTR compat_sys_rt_sigtimedwait
- PTR sys32_rt_sigqueueinfo
+ PTR compat_sys_rt_sigqueueinfo
PTR sys32_rt_sigsuspend
PTR sys32_pread /* 4200 */
PTR sys32_pwrite
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c
index c86a5dd..b680e7d 100644
--- a/arch/mips/kernel/signal32.c
+++ b/arch/mips/kernel/signal32.c
@@ -463,6 +463,22 @@ int copy_siginfo_to_user32(compat_siginf
return err;
}

+/* CAUTION: This is just a very minimalist implementation for the
+ * sake of compat_sys_rt_sigqueueinfo(), cribbed from sparc64.
+ */
+int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
+{
+ if (!access_ok(VERIFY_WRITE, from, sizeof(compat_siginfo_t)))
+ return -EFAULT;
+
+ if (copy_from_user(to, from, 3*sizeof(int)) ||
+ copy_from_user(to->_sifields._pad, from->_sifields._pad,
+ SI_PAD_SIZE))
+ return -EFAULT;
+
+ return 0;
+}
+
save_static_function(sys32_sigreturn);
__attribute_used__ noinline static void
_sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
@@ -950,21 +966,6 @@ asmlinkage int sys32_rt_sigpending(compa
return ret;
}

-asmlinkage int sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo)
-{
- siginfo_t info;
- int ret;
- mm_segment_t old_fs = get_fs();
-
- if (copy_from_user (&info, uinfo, 3*sizeof(int)) ||
- copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE))
- return -EFAULT;
- set_fs (KERNEL_DS);
- ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *)&info);
- set_fs (old_fs);
- return ret;
-}
-
asmlinkage long
sys32_waitid(int which, compat_pid_t pid,
compat_siginfo_t __user *uinfo, int options,
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index 701d66a..c91605b 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -282,7 +282,7 @@ #endif
* to worry about faulting trying to copy in a larger 64-bit
* struct from a 32-bit user-space app.
*/
- ENTRY_SAME(rt_sigqueueinfo)
+ ENTRY_COMP(rt_sigqueueinfo)
ENTRY_SAME(rt_sigsuspend_wrapper) /* not really SAME -- see the code */
ENTRY_SAME(chown) /* 180 */
/* setsockopt() used by iptables: SO_SET_REPLACE/SO_SET_ADD_COUNTERS */
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 320353f..6bdc1a4 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -573,7 +573,6 @@ long compat_sys_rt_sigpending(compat_sig
return ret;
}

-
int copy_siginfo_to_user32(struct compat_siginfo __user *d, siginfo_t *s)
{
int err;
@@ -629,30 +628,24 @@ int copy_siginfo_to_user32(struct compat
return err;
}

-#define copy_siginfo_to_user copy_siginfo_to_user32
-
-/*
- * Note: it is necessary to treat pid and sig as unsigned ints, with the
- * corresponding cast to a signed int to insure that the proper conversion
- * (sign extension) between the register representation of a signed int
- * (msr in 32-bit mode) and the register representation of a signed int
- * (msr in 64-bit mode) is performed.
+/* CAUTION: This is just a very minimalist implementation for the
+ * sake of compat_sys_rt_sigqueueinfo(), cribbed from sparc64.
*/
-long compat_sys_rt_sigqueueinfo(u32 pid, u32 sig, compat_siginfo_t __user *uinfo)
+int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
{
- siginfo_t info;
- int ret;
- mm_segment_t old_fs = get_fs();
+ if (!access_ok(VERIFY_WRITE, from, sizeof(compat_siginfo_t)))
+ return -EFAULT;

- if (copy_from_user (&info, uinfo, 3*sizeof(int)) ||
- copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE32))
+ if (copy_from_user(to, from, 3*sizeof(int)) ||
+ copy_from_user(to->_sifields._pad, from->_sifields._pad,
+ SI_PAD_SIZE))
return -EFAULT;
- set_fs (KERNEL_DS);
- /* The __user pointer cast is valid becasuse of the set_fs() */
- ret = sys_rt_sigqueueinfo((int)pid, (int)sig, (siginfo_t __user *) &info);
- set_fs (old_fs);
- return ret;
+
+ return 0;
}
+
+#define copy_siginfo_to_user copy_siginfo_to_user32
+
/*
* Start Alternate signal stack support
*
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 5b33f82..295d2bb 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -514,21 +514,6 @@ asmlinkage long sys32_rt_sigpending(comp
return ret;
}

-asmlinkage long
-sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo)
-{
- siginfo_t info;
- int ret;
- mm_segment_t old_fs = get_fs();
-
- if (copy_siginfo_from_user32(&info, uinfo))
- return -EFAULT;
- set_fs (KERNEL_DS);
- ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info);
- set_fs (old_fs);
- return ret;
-}
-
/*
* sys32_execve() executes a new program after the asm stub has set
* things up for us. This should basically do what I want it to.
diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S
index 71e54ef..71c1127 100644
--- a/arch/s390/kernel/compat_wrapper.S
+++ b/arch/s390/kernel/compat_wrapper.S
@@ -857,7 +857,7 @@ sys32_rt_sigqueueinfo_wrapper:
lgfr %r2,%r2 # int
lgfr %r3,%r3 # int
llgtr %r4,%r4 # siginfo_emu31_t *
- jg sys32_rt_sigqueueinfo # branch to system call
+ jg compat_sys_rt_sigqueueinfo # branch to system call

.globl compat_sys_rt_sigsuspend_wrapper
compat_sys_rt_sigsuspend_wrapper:
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index e27cb71..16ff6ff 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -601,22 +601,6 @@ asmlinkage long sys32_rt_sigpending(comp
return ret;
}

-asmlinkage long compat_sys_rt_sigqueueinfo(int pid, int sig,
- struct compat_siginfo __user *uinfo)
-{
- siginfo_t info;
- int ret;
- mm_segment_t old_fs = get_fs();
-
- if (copy_siginfo_from_user32(&info, uinfo))
- return -EFAULT;
-
- set_fs (KERNEL_DS);
- ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info);
- set_fs (old_fs);
- return ret;
-}
-
asmlinkage long compat_sys_sigaction(int sig, struct old_sigaction32 __user *act,
struct old_sigaction32 __user *oact)
{
diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S
index b4aa875..9fa991a 100644
--- a/arch/x86_64/ia32/ia32entry.S
+++ b/arch/x86_64/ia32/ia32entry.S
@@ -577,7 +577,7 @@ #endif
.quad sys32_rt_sigprocmask /* 175 */
.quad sys32_rt_sigpending
.quad compat_sys_rt_sigtimedwait
- .quad sys32_rt_sigqueueinfo
+ .quad compat_sys_rt_sigqueueinfo
.quad stub32_rt_sigsuspend
.quad sys32_pread /* 180 */
.quad sys32_pwrite
diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c
index c9bac3a..db10734 100644
--- a/arch/x86_64/ia32/sys_ia32.c
+++ b/arch/x86_64/ia32/sys_ia32.c
@@ -628,21 +628,6 @@ sys32_rt_sigpending(compat_sigset_t __us
return ret;
}

-asmlinkage long
-sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo)
-{
- siginfo_t info;
- int ret;
- mm_segment_t old_fs = get_fs();
-
- if (copy_siginfo_from_user32(&info, uinfo))
- return -EFAULT;
- set_fs (KERNEL_DS);
- ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *)&info);
- set_fs (old_fs);
- return ret;
-}
-
/* These are here just in case some old ia32 binary calls it. */
asmlinkage long
sys32_pause(void)
diff --git a/kernel/compat.c b/kernel/compat.c
index d4898aa..022821a 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -837,6 +837,24 @@ compat_sys_rt_sigtimedwait (compat_sigse

}

+asmlinkage long compat_sys_rt_sigqueueinfo(int pid, int sig,
+ struct compat_siginfo __user *uinfo)
+{
+ siginfo_t info;
+
+ if (copy_siginfo_from_user32(&info, uinfo))
+ return -EFAULT;
+
+ /* Not even root can pretend to send signals from the kernel.
+ Nor can they impersonate a kill(), which adds source info. */
+ if (info.si_code >= 0)
+ return -EPERM;
+ info.si_signo = sig;
+
+ /* POSIX.1b doesn't mention process groups. */
+ return kill_proc_info(sig, &info, pid);
+}
+
#ifdef __ARCH_WANT_COMPAT_SYS_TIME

/* compat_time_t is a 32 bit "long" and needs to get converted. */