This patch fixes incorrect data returned from waitid() for mips64el N32. It uses compat code to translate siginfo_t for N32, and factors out common O32 and N32 compat code for siginfo_t, to compat32.c. Tested on glibc suite and LTP.
From: Peter Watkins <treestem@xxxxxxxxx> Date: Thu, 27 Jul 2006 19:13:09 -0400 Subject: Use compat code to translate siginfo_t for N32. Use compat code to translate siginfo_t for N32. Factor out common O32 and N32 compat code for siginfo_t, to compat32.c. --- arch/mips/kernel/Makefile | 4 +- arch/mips/kernel/compat32.c | 88 +++++++++++++++++++++++++++++++++++++++++ arch/mips/kernel/compat32.h | 76 +++++++++++++++++++++++++++++++++++ arch/mips/kernel/linux32.c | 28 ------------- arch/mips/kernel/signal32.c | 52 ------------------------ arch/mips/kernel/signal_n32.c | 37 ++++++++++++++++- 6 files changed, 200 insertions(+), 85 deletions(-) diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 881c467..108171a 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -56,8 +56,8 @@ obj-$(CONFIG_32BIT) += scall32-o32.o obj-$(CONFIG_64BIT) += scall64-64.o obj-$(CONFIG_BINFMT_IRIX) += binfmt_irix.o obj-$(CONFIG_MIPS32_COMPAT) += linux32.o signal32.o -obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o -obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o +obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o compat32.o +obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o compat32.o obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/arch/mips/kernel/compat32.c b/arch/mips/kernel/compat32.c new file mode 100644 index 0000000..858f7db --- /dev/null +++ b/arch/mips/kernel/compat32.c @@ -0,0 +1,88 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/cache.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/syscalls.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/compat.h> +#include <linux/suspend.h> +#include <linux/compiler.h> + +#include <asm/abi.h> +#include <asm/asm.h> +#include <linux/bitops.h> +#include <asm/sim.h> +#include <asm/uaccess.h> +#include <asm/ucontext.h> +#include <asm/system.h> +#include <asm/fpu.h> +#include <asm/war.h> + +#include "compat32.h" + + +int copy_siginfo_to_user32(compat_siginfo_t *to, siginfo_t *from) +{ + int err; + + if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) + return -EFAULT; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. + This routine must convert siginfo from 64bit to 32bit as well + at the same time. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + if (from->si_code < 0) + err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); + else { + switch (from->si_code >> 16) { + case __SI_TIMER >> 16: + err |= __put_user(from->si_tid, &to->si_tid); + err |= __put_user(from->si_overrun, &to->si_overrun); + err |= __put_user(from->si_int, &to->si_int); + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + case __SI_FAULT >> 16: + err |= __put_user((long)from->si_addr, &to->si_addr); + break; + case __SI_POLL >> 16: + err |= __put_user(from->si_band, &to->si_band); + err |= __put_user(from->si_fd, &to->si_fd); + break; + case __SI_RT >> 16: /* This is not generated by the kernel as of now. */ + case __SI_MESGQ >> 16: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_int, &to->si_int); + break; + } + } + return err; +} diff --git a/arch/mips/kernel/compat32.h b/arch/mips/kernel/compat32.h new file mode 100644 index 0000000..e2bb23b --- /dev/null +++ b/arch/mips/kernel/compat32.h @@ -0,0 +1,76 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ + +#define SI_PAD_SIZE32 ((SI_MAX_SIZE/sizeof(int)) - 3) + +typedef struct compat_siginfo { + int si_signo; + int si_code; + int si_errno; + + union { + int _pad[SI_PAD_SIZE32]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + compat_uid_t _uid; /* sender's uid */ + } _kill; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + compat_uid_t _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* IRIX SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + compat_clock_t _utime; + int _status; /* exit code */ + compat_clock_t _stime; + } _irix_sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + s32 _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL, SIGXFSZ (To do ...) */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + + /* POSIX.1b timers */ + struct { + timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval;/* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + compat_uid_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + } _sifields; +} compat_siginfo_t; + + + +int copy_siginfo_to_user32(compat_siginfo_t *to, siginfo_t *from); + diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 450ac59..dd71096 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -163,34 +163,6 @@ out: return error; } -asmlinkage long -sysn32_waitid(int which, compat_pid_t pid, - siginfo_t __user *uinfo, int options, - struct compat_rusage __user *uru) -{ - struct rusage ru; - long ret; - mm_segment_t old_fs = get_fs(); - int si_signo; - - if (!access_ok(VERIFY_WRITE, uinfo, sizeof(*uinfo))) - return -EFAULT; - - set_fs (KERNEL_DS); - ret = sys_waitid(which, pid, uinfo, options, - uru ? (struct rusage __user *) &ru : NULL); - set_fs (old_fs); - - if (__get_user(si_signo, &uinfo->si_signo)) - return -EFAULT; - if (ret < 0 || si_signo == 0) - return ret; - - if (uru) - ret = put_compat_rusage(&ru, uru); - return ret; -} - struct sysinfo32 { s32 uptime; u32 loads[3]; diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index f32a229..2ba8c53 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c @@ -411,58 +411,6 @@ #if ICACHE_REFILLS_WORKAROUND_WAR #endif }; -int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) -{ - int err; - - if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) - return -EFAULT; - - /* If you change siginfo_t structure, please be sure - this code is fixed accordingly. - It should never copy any pad contained in the structure - to avoid security leaks, but must copy the generic - 3 ints plus the relevant union member. - This routine must convert siginfo from 64bit to 32bit as well - at the same time. */ - err = __put_user(from->si_signo, &to->si_signo); - err |= __put_user(from->si_errno, &to->si_errno); - err |= __put_user((short)from->si_code, &to->si_code); - if (from->si_code < 0) - err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); - else { - switch (from->si_code >> 16) { - case __SI_TIMER >> 16: - err |= __put_user(from->si_tid, &to->si_tid); - err |= __put_user(from->si_overrun, &to->si_overrun); - err |= __put_user(from->si_int, &to->si_int); - break; - case __SI_CHLD >> 16: - err |= __put_user(from->si_utime, &to->si_utime); - err |= __put_user(from->si_stime, &to->si_stime); - err |= __put_user(from->si_status, &to->si_status); - default: - err |= __put_user(from->si_pid, &to->si_pid); - err |= __put_user(from->si_uid, &to->si_uid); - break; - case __SI_FAULT >> 16: - err |= __put_user((unsigned long)from->si_addr, &to->si_addr); - break; - case __SI_POLL >> 16: - err |= __put_user(from->si_band, &to->si_band); - err |= __put_user(from->si_fd, &to->si_fd); - break; - case __SI_RT >> 16: /* This is not generated by the kernel as of now. */ - case __SI_MESGQ >> 16: - err |= __put_user(from->si_pid, &to->si_pid); - err |= __put_user(from->si_uid, &to->si_uid); - err |= __put_user(from->si_int, &to->si_int); - break; - } - } - return err; -} - save_static_function(sys32_sigreturn); __attribute_used__ noinline static void _sys32_sigreturn(nabi_no_regargs struct pt_regs regs) diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c index 477c533..f805bea 100644 --- a/arch/mips/kernel/signal_n32.c +++ b/arch/mips/kernel/signal_n32.c @@ -23,6 +23,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/kernel.h> #include <linux/signal.h> +#include <linux/syscalls.h> #include <linux/errno.h> #include <linux/wait.h> #include <linux/ptrace.h> @@ -41,6 +42,7 @@ #include <asm/cpu-features.h> #include <asm/war.h> #include "signal-common.h" +#include "compat32.h" /* * Including <asm/unistd.h> would give use the 64-bit syscall numbers ... @@ -74,7 +76,7 @@ #if ICACHE_REFILLS_WORKAROUND_WAR #else u32 rs_code[2]; /* signal trampoline */ #endif - struct siginfo rs_info; + compat_siginfo_t rs_info; struct ucontextn32 rs_uc; #if ICACHE_REFILLS_WORKAROUND_WAR u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */ @@ -180,7 +182,7 @@ int setup_rt_frame_n32(struct k_sigactio install_sigtramp(frame->rs_code, __NR_N32_rt_sigreturn); /* Create siginfo. */ - err |= copy_siginfo_to_user(&frame->rs_info, info); + err |= copy_siginfo_to_user32(&frame->rs_info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->rs_uc.uc_flags); @@ -215,7 +217,7 @@ int setup_rt_frame_n32(struct k_sigactio regs->regs[31] = (unsigned long) frame->rs_code; regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; -#if DEBUG_SIG +#ifdef DEBUG_SIG printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", current->comm, current->pid, frame, regs->cp0_epc, regs->regs[31]); @@ -226,3 +228,32 @@ give_sigsegv: force_sigsegv(signr, current); return -EFAULT; } + + + +asmlinkage long +sysn32_waitid(int which, compat_pid_t pid, + compat_siginfo_t __user *uinfo, int options, + struct compat_rusage __user *uru) +{ + siginfo_t info; + struct rusage ru; + long ret; + mm_segment_t old_fs = get_fs(); + + info.si_signo = 0; + set_fs (KERNEL_DS); + ret = sys_waitid(which, pid, (siginfo_t __user *) &info, options, + uru ? (struct rusage __user *) &ru : NULL); + set_fs (old_fs); + + if (ret < 0 || info.si_signo == 0) + return ret; + + if (uru && (ret = put_compat_rusage(&ru, uru))) + return ret; + + BUG_ON(info.si_code & __SI_MASK); + info.si_code |= __SI_CHLD; + return copy_siginfo_to_user32(uinfo, &info); +} -- 1.4.1