These routines try to match the behavior of native 32-bit kernels as closely as possible. They will replace architecture-specific versions that are missing support for some fields.
The only problematic situation is when sending a si_ptr from a 32-bit process to a 64-bit process or vice-versa, but this has never worked correctly in the past anyways. Signed-off-by: Amanieu d'Antras <aman...@gmail.com> --- arch/arm64/include/asm/compat.h | 2 + arch/mips/include/asm/compat.h | 2 + arch/parisc/include/asm/compat.h | 2 + arch/powerpc/include/asm/compat.h | 2 + arch/s390/include/asm/compat.h | 2 + arch/sparc/include/asm/compat.h | 2 + arch/tile/include/asm/compat.h | 2 + arch/x86/include/asm/compat.h | 2 + kernel/compat.c | 204 ++++++++++++++++++++++++++++++++++++++ kernel/signal.c | 12 ++- 10 files changed, 227 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index ff4e294..5eae749 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -156,6 +156,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 typedef struct compat_siginfo { int si_signo; diff --git a/arch/mips/include/asm/compat.h b/arch/mips/include/asm/compat.h index 5f1f816..1e5ba38 100644 --- a/arch/mips/include/asm/compat.h +++ b/arch/mips/include/asm/compat.h @@ -131,6 +131,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 #define SI_PAD_SIZE32 (128/sizeof(int) - 3) typedef struct compat_siginfo { diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h index e0be05f..46a0a8a 100644 --- a/arch/parisc/include/asm/compat.h +++ b/arch/parisc/include/asm/compat.h @@ -135,6 +135,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 typedef struct compat_siginfo { int si_signo; diff --git a/arch/powerpc/include/asm/compat.h b/arch/powerpc/include/asm/compat.h index 75b25ff..cdc8638 100644 --- a/arch/powerpc/include/asm/compat.h +++ b/arch/powerpc/include/asm/compat.h @@ -125,6 +125,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 #define SI_PAD_SIZE32 (128/sizeof(int) - 3) typedef struct compat_siginfo { diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index ac73ac7..497af62 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -193,6 +193,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 typedef struct compat_siginfo { int si_signo; diff --git a/arch/sparc/include/asm/compat.h b/arch/sparc/include/asm/compat.h index 0c80f59..9357014 100644 --- a/arch/sparc/include/asm/compat.h +++ b/arch/sparc/include/asm/compat.h @@ -154,6 +154,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 #define SI_PAD_SIZE32 (128/sizeof(int) - 3) typedef struct compat_siginfo { diff --git a/arch/tile/include/asm/compat.h b/arch/tile/include/asm/compat.h index f9bba8d..e0c61da 100644 --- a/arch/tile/include/asm/compat.h +++ b/arch/tile/include/asm/compat.h @@ -116,6 +116,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 #define COMPAT_SI_PAD_SIZE (128/sizeof(int) - 3) typedef struct compat_siginfo { diff --git a/arch/x86/include/asm/compat.h b/arch/x86/include/asm/compat.h index 69176b4..c6b58b1 100644 --- a/arch/x86/include/asm/compat.h +++ b/arch/x86/include/asm/compat.h @@ -131,6 +131,8 @@ typedef union compat_sigval { } compat_sigval_t; #define HAVE_ARCH_COMPAT_SIGINFO_T +#define HAVE_ARCH_COPY_SIGINFO_TO_USER32 +#define HAVE_ARCH_COPY_SIGINFO_FROM_USER32 typedef struct compat_siginfo { int si_signo; diff --git a/kernel/compat.c b/kernel/compat.c index 333d364..7af6d52 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -1174,3 +1174,207 @@ void __user *compat_alloc_user_space(unsigned long len) return ptr; } EXPORT_SYMBOL_GPL(compat_alloc_user_space); + +#ifndef HAVE_ARCH_COPY_SIGINFO_TO_USER32 +int copy_siginfo_to_user32(struct compat_siginfo __user *to, const siginfo_t *from) +{ + int err, si_code; + + if (!access_ok(VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + + /* + * Get the user-visible si_code by hiding the top 16 bits if this is a + * kernel-generated signal. + */ + si_code = from->si_code < 0 ? from->si_code : (short)from->si_code; + + /* + * If you change siginfo_t structure, please be sure that + * all these functions are fixed accordingly: + * copy_siginfo_to_user + * copy_siginfo_to_user32 + * copy_siginfo_from_user32 + * signalfd_copyinfo + * They 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. + */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user(si_code, &to->si_code); + if (from->si_code < 0) { + err |= __copy_to_user(to->_sifields._pad, from->_sifields._pad, SI_PAD_SIZE * sizeof(int)) + ? -EFAULT : 0; + return err; + } + switch (from->si_code & __SI_MASK) { + case __SI_KILL: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + case __SI_TIMER: + err |= __put_user(from->si_tid, &to->si_tid); + err |= __put_user(from->si_overrun, &to->si_overrun); + /* + * Get the sigval from si_int, which matches the convention + * used in get_compat_sigevent. + */ + err |= __put_user(from->si_int, &to->si_int); + break; + case __SI_POLL: + err |= __put_user(from->si_band, &to->si_band); + err |= __put_user(from->si_fd, &to->si_fd); + break; + case __SI_FAULT: + err |= __put_user(ptr_to_compat(from->si_addr), &to->si_addr); +#ifdef __ARCH_SI_TRAPNO + err |= __put_user(from->si_trapno, &to->si_trapno); +#endif +#ifdef BUS_MCEERR_AO + /* + * Other callers might not initialize the si_lsb field, + * so check explicitly for the right codes here. + */ + if (from->si_signo == SIGBUS && + (from->si_code == BUS_MCEERR_AR || from->si_code == BUS_MCEERR_AO)) + err |= __put_user(from->si_addr_lsb, &to->si_addr_lsb); +#endif +#ifdef SEGV_BNDERR + if (from->si_signo == SIGSEGV && from->si_code == SEGV_BNDERR) { + err |= __put_user(ptr_to_compat(from->si_lower), &to->si_lower); + err |= __put_user(ptr_to_compat(from->si_upper), &to->si_upper); + } +#endif + break; + case __SI_CHLD: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_status, &to->si_status); + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + break; + case __SI_RT: /* This is not generated by the kernel as of now. */ + case __SI_MESGQ: /* But this is */ + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + /* + * Get the sigval from si_int, which matches the convention + * used in get_compat_sigevent. + */ + err |= __put_user(from->si_int, &to->si_int); + break; +#ifdef __ARCH_SIGSYS + case __SI_SYS: + err |= __put_user(ptr_to_compat(from->si_call_addr), &to->si_call_addr); + err |= __put_user(from->si_syscall, &to->si_syscall); + err |= __put_user(from->si_arch, &to->si_arch); + break; +#endif + default: /* this is just in case for now ... */ + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + } + return err; +} +#endif + +#ifndef HAVE_ARCH_COPY_SIGINFO_FROM_USER32 +int copy_siginfo_from_user32(siginfo_t *to, struct compat_siginfo __user *from) +{ + int err; + compat_uptr_t ptr32; + + if (!access_ok(VERIFY_READ, from, sizeof(siginfo_t))) + return -EFAULT; + + /* + * If you change siginfo_t structure, please be sure that + * all these functions are fixed accordingly: + * copy_siginfo_to_user + * copy_siginfo_to_user32 + * copy_siginfo_from_user32 + * signalfd_copyinfo + * They 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. + */ + 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); + if (to->si_code < 0) { + /* + * Note that the compat union may be larger than the normal one due to + * alignment. The copying here causes us to lose the last 4 bytes of + * data, but this shouldn't be too much of a problem in practice. + */ + err |= __copy_from_user(to->_sifields._pad, from->_sifields._pad, SI_PAD_SIZE * sizeof(int)) + ? -EFAULT : 0; + return err; + } + 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); + /* + * Put the sigval in si_int, which matches the convention + * used in get_compat_sigevent. + */ + to->si_ptr = 0; /* Avoid uninitialized bits in the union */ + err |= __get_user(to->si_int, &from->si_int); + 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(ptr32, &from->si_addr); + to->si_addr = compat_ptr(ptr32); +#ifdef __ARCH_SI_TRAPNO + err |= __get_user(to->si_trapno, &from->si_trapno); +#endif + err |= __get_user(to->si_addr_lsb, &from->si_addr_lsb); + err |= __get_user(ptr32, &from->si_lower); + to->si_lower = compat_ptr(ptr32); + err |= __get_user(ptr32, &from->si_upper); + to->si_upper = compat_ptr(ptr32); + 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); + /* + * Put the sigval in si_int, which matches the convention + * used in get_compat_sigevent. + */ + to->si_ptr = 0; /* Avoid uninitialized bits in the union */ + err |= __get_user(to->si_int, &from->si_int); + break; +#ifdef __ARCH_SIGSYS + case __SI_SYS: + err |= __get_user(ptr32, &from->si_call_addr); + to->si_call_addr = compat_ptr(ptr32); + err |= __get_user(to->si_syscall, &from->si_syscall); + err |= __get_user(to->si_arch, &from->si_arch); + break; +#endif + 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; +} +#endif diff --git a/kernel/signal.c b/kernel/signal.c index 0f6bbbe..873e8e2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2713,11 +2713,13 @@ int copy_siginfo_to_user(siginfo_t __user *to, const siginfo_t *from) return __copy_to_user(to, from, sizeof(siginfo_t)) ? -EFAULT : 0; /* - * If you change siginfo_t structure, please be sure - * this code is fixed accordingly. - * Please remember to update the signalfd_copyinfo() function - * inside fs/signalfd.c too, in case siginfo_t changes. - * It should never copy any pad contained in the structure + * If you change siginfo_t structure, please be sure that + * all these functions are fixed accordingly: + * copy_siginfo_to_user + * copy_siginfo_to_user32 + * copy_siginfo_from_user32 + * signalfd_copyinfo + * They 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. */ -- 2.6.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/