[parisc-linux-cvs] Please review. Generic changes required for
siginfo_t compat on 64-bit arches.
Carlos O'Donell
carlos at baldric.uwo.ca
Fri Apr 30 14:13:18 MDT 2004
On Fri, Apr 30, 2004 at 02:07:04PM -0600, Carlos O'Donell wrote:
> ----------------------------------------
> Linux Kernel - siginfo_t compat changes
> ----------------------------------------
>
> The Linuxthreads implementation of posix-threads uses
> rt_sigqueueinfo. This is broken for 64-bit kernels running
> 32-bit userspace. It requires that kernel check for a compat
> task and properly copy the siginfo_t out of userspace.
>
> This change adds the requires compat checks in generic
> places, and uses the previously implemented is_compat_task
> macro to remove this code when there is no possibility of a
> compat task.
>
> ----------------------------------------
> include/linux/compat_siginfo.h
>
> Cleanup and make more consistent with compat_* types.
> Make __ARCH_SI_UID_T the correct compat type.
>
> kernel/compat_signal.c
>
> (compat_copy_siginfo_to_user)
>
> Always copy the first three words, the kernel uses
> these and we might need them later. Only copy the
> _sifields._pad when the structure is from userspace.
>
> (compat_copy_siginfo_from_user)
>
> A brand new function that is going to be used by
> ptrace_getsiginfo and sys_rt_sigqueueinfo, since both
> functions read siginfo_t from userspace. This function
> copies up a siginfo_t to the proper kernel size.
>
> kernel/signal.c
>
> (copy_siginfo_from_user)
>
> If the task is compat use the compat version e.g.
> compat_copy_siginfo_from_user, else fall back to
> using copy_from_user.
>
> (copy_siginfo_to_user)
>
> Move the compat check to the start of the function, that way
> we don't duplicate the access check.
>
> (sys_rt_sigqueueinfo)
>
> Call copy_siginfo_from_user to load a siginfo_t from
> userspace.
>
> kernel/ptrace.c
>
> (ptrace_getsiginfo)
>
> Call copy_siginfo_from_user to load a siginfo_t from
> userspace.
The log message says it all. We have working posix-timers with 64-bit
kernels, by virtue of a working rt_sigqueueinfo. I wonder if this fixes
more things on 64-bit boxes.
Index: include/linux/compat_siginfo.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/linux/compat_siginfo.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -p -r1.3 -r1.4
--- a/include/linux/compat_siginfo.h 3 Feb 2004 21:51:13 -0000 1.3
+++ b/include/linux/compat_siginfo.h 30 Apr 2004 20:07:02 -0000 1.4
@@ -33,12 +33,13 @@ typedef union compat_sigval {
* struct siginfo that is before the union.
*/
#ifndef __ARCH_SI_COMPAT_PREAMBLE_SIZE
-#define __ARCH_SI_COMPAT_PREAMBLE_SIZE (3 * sizeof(int))
+#define __ARCH_SI_COMPAT_PREAMBLE_SIZE (3 * sizeof(compat_int_t))
#endif
#define SI_COMPAT_MAX_SIZE 128
#ifndef SI_COMPAT_PAD_SIZE
-#define SI_COMPAT_PAD_SIZE ((SI_COMPAT_MAX_SIZE - __ARCH_SI_COMPAT_PREAMBLE_SIZE) / sizeof(int))
+#define SI_COMPAT_PAD_SIZE \
+ ((SI_COMPAT_MAX_SIZE - __ARCH_SI_COMPAT_PREAMBLE_SIZE) / sizeof(compat_int_t))
#endif
/* 32-bit view of si.uid_t */
@@ -72,7 +73,7 @@ typedef struct compat_siginfo {
struct {
compat_timer_t _tid; /* timer id */
compat_int_t _overrun; /* overrun count */
- char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
+ char _pad[sizeof(__ARCH_SI_COMPAT_UID_T) - sizeof(compat_int_t)];
compat_sigval_t _sigval; /* same as below */
compat_int_t _sys_private; /* not to be passed to user */
} _timer;
@@ -164,6 +165,7 @@ static inline void compat_copy_siginfo(s
#endif /* !HAVE_ARCH_COMPAT_COPY_SIGINFO */
extern int compat_copy_siginfo_to_user(compat_siginfo_t __user *to, struct siginfo *from);
+extern int compat_copy_siginfo_from_user(struct siginfo *to, compat_siginfo_t __user *from);
#endif /* CONFIG_COMPAT */
#endif /* _ASM_GENERIC_COMPAT_SIGINFO_H */
Index: kernel/compat_signal.c
===================================================================
RCS file: /var/cvs/linux-2.6/kernel/compat_signal.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -p -r1.3 -r1.4
--- a/kernel/compat_signal.c 26 Apr 2004 04:46:48 -0000 1.3
+++ b/kernel/compat_signal.c 30 Apr 2004 20:07:02 -0000 1.4
@@ -48,17 +48,25 @@ int compat_copy_siginfo_to_user(compat_s
/* Convert structure, don't leak anything in the copy */
memset(&compat_from,'\0',sizeof(compat_siginfo_t));
+
+ /* Always copy si_signo, si_errno, and si_code */
compat_from.si_signo = (compat_int_t)(from->si_signo);
compat_from.si_errno = (compat_int_t)(from->si_errno);
compat_from.si_code = (compat_int_t)(from->si_code);
-
- if (from->si_code < 0)
- return __copy_to_user(to, &compat_from, sizeof(compat_siginfo_t))
- ? -EFAULT : 0;
-
+
err = __put_user(compat_from.si_signo, &to->si_signo);
err |= __put_user(compat_from.si_errno, &to->si_errno);
err |= __put_user(compat_from.si_code, &to->si_code);
+
+ /* siginfo_t came from userspace, so it is the right
+ * size, no need for conversion
+ */
+ if (from->si_code < 0) {
+ return __copy_to_user(&to->_sifields._pad,
+ &from->_sifields._pad,
+ SI_COMPAT_PAD_SIZE)
+ ? -EFAULT : 0;
+ }
switch (from->si_code & __SI_MASK) {
case __SI_KILL:
@@ -117,6 +125,79 @@ int compat_copy_siginfo_to_user(compat_s
compat_from.si_uid = (__ARCH_SI_COMPAT_UID_T)(from->si_uid);
err |= __put_user(compat_from.si_pid, &to->si_pid);
err |= __put_user(compat_from.si_uid, &to->si_uid);
+ break;
+ }
+ return err;
+}
+
+int compat_copy_siginfo_from_user(siginfo_t *to, compat_siginfo_t __user *from)
+{
+ int err;
+ u64 scratch;
+
+ if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t)))
+ return -EFAULT;
+
+ /*
+ * If you change compat_siginfo_t structure *or* siginfo_t,
+ * please be sure this code is fixed accordingly.
+ */
+
+ /* Always copy si_signo, si_errno, and si_code */
+ err = __get_user(to->si_signo, &from->si_signo);
+ err |= __get_user(to->si_errno, &from->si_errno);
+ err |= __get_user(to->si_code, &from->si_code);
+
+ /* siginfo_t came from userspace, so it is the right
+ * size, no need for conversion
+ */
+ if (to->si_code < 0) {
+ return __copy_from_user(&to->_sifields._pad,
+ &from->_sifields._pad,
+ SI_COMPAT_PAD_SIZE)
+ ? -EFAULT : 0;
+ }
+
+ switch (to->si_code & __SI_MASK) {
+ case __SI_KILL:
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ break;
+ case __SI_TIMER:
+ err |= __get_user(to->si_tid, &from->si_tid);
+ err |= __get_user(to->si_overrun, &from->si_overrun);
+ err |= __get_user(scratch, &from->si_ptr);
+ to->si_ptr = (u64*)scratch;
+ break;
+ case __SI_POLL:
+ err |= __get_user(to->si_band, &from->si_band);
+ err |= __get_user(to->si_fd, &from->si_fd);
+ break;
+ case __SI_FAULT:
+ err |= __get_user(scratch, &from->si_addr);
+ to->si_addr = (u64*)scratch;
+#ifdef __ARCH_SI_COMPAT_TRAPNO
+ err |= __get_user(to->si_trapno, &from->si_trapno);
+#endif
+ break;
+ case __SI_CHLD:
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ err |= __get_user(to->si_status, &from->si_status);
+ err |= __get_user(to->si_utime, &from->si_utime);
+ err |= __get_user(to->si_stime, &from->si_stime);
+ break;
+ case __SI_RT: /* This is not generated by the kernel as of now. */
+ case __SI_MESGQ: /* But this is */
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ err |= __get_user(to->si_int, &from->si_int);
+ err |= __get_user(scratch, &from->si_ptr);
+ to->si_ptr = (u64*)scratch;
+ break;
+ default: /* this is just in case for now ... */
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
break;
}
return err;
Index: kernel/ptrace.c
===================================================================
RCS file: /var/cvs/linux-2.6/kernel/ptrace.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -p -r1.4 -r1.5
--- a/kernel/ptrace.c 30 Mar 2004 12:42:47 -0000 1.4
+++ b/kernel/ptrace.c 30 Apr 2004 20:07:02 -0000 1.5
@@ -290,7 +290,7 @@ static int ptrace_setsiginfo(struct task
{
if (child->last_siginfo == NULL)
return -EINVAL;
- if (copy_from_user(child->last_siginfo, data, sizeof (siginfo_t)) != 0)
+ if (copy_siginfo_from_user(child->last_siginfo, data) != 0)
return -EFAULT;
return 0;
}
Index: kernel/signal.c
===================================================================
RCS file: /var/cvs/linux-2.6/kernel/signal.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -p -r1.18 -r1.19
--- a/kernel/signal.c 28 Apr 2004 19:13:08 -0000 1.18
+++ b/kernel/signal.c 30 Apr 2004 20:07:02 -0000 1.19
@@ -2018,11 +2018,28 @@ sys_rt_sigpending(sigset_t __user *set,
return do_sigpending(set, sigsetsize);
}
+#ifndef HAVE_ARCH_COPY_SIGINFO_FROM_USER
+
+int copy_siginfo_from_user(siginfo_t *to, siginfo_t __user *from)
+{
+ if(is_compat_task(current))
+ return compat_copy_siginfo_from_user(to,(compat_siginfo_t __user *)from);
+
+ return copy_from_user(&to, from, sizeof(siginfo_t));
+}
+
+#endif
+
#ifndef HAVE_ARCH_COPY_SIGINFO_TO_USER
int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)
{
int err;
+
+ /* Use compat_siginfo_t with 32-bit signals */
+ if(is_compat_task(current)){
+ return compat_copy_siginfo_to_user((compat_siginfo_t __user *)to,from);
+ }
if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t)))
return -EFAULT;
@@ -2030,11 +2047,6 @@ int copy_siginfo_to_user(siginfo_t __use
return __copy_to_user(to, from, sizeof(siginfo_t))
? -EFAULT : 0;
- /* Use compat_siginfo_t with 32-bit signals */
- if(is_compat_task(current)){
- return compat_copy_siginfo_to_user((compat_siginfo_t __user *)to,from);
- }
-
/*
* If you change siginfo_t structure, please be sure
* this code is fixed accordingly.
@@ -2271,7 +2283,7 @@ sys_rt_sigqueueinfo(int pid, int sig, si
{
siginfo_t info;
- if (copy_from_user(&info, uinfo, sizeof(siginfo_t)))
+ if (copy_siginfo_from_user(&info, uinfo))
return -EFAULT;
/* Not even root can pretend to send signals from the kernel.
More information about the parisc-linux-cvs
mailing list