From patchwork Thu Nov 5 00:50:21 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amanieu d'Antras X-Patchwork-Id: 7556301 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id F1AF4BEEA4 for ; Thu, 5 Nov 2015 00:54:45 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 89ABD2068D for ; Thu, 5 Nov 2015 00:54:44 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 91CC120711 for ; Thu, 5 Nov 2015 00:54:42 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zu8mq-0003qU-1Y; Thu, 05 Nov 2015 00:52:20 +0000 Received: from mail-wm0-x241.google.com ([2a00:1450:400c:c09::241]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zu8mY-0003YJ-4K for linux-arm-kernel@lists.infradead.org; Thu, 05 Nov 2015 00:52:05 +0000 Received: by wmnn186 with SMTP id n186so49237wmn.0 for ; Wed, 04 Nov 2015 16:51:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=BuHyvN8SDwaxFq+gBRXCbN+Y+Q9FLGZrzmsiSjkg8nM=; b=OOrxhraoYrTWWXOOaDzI+DRhVpMzGYVP2wxffwysO/fDbyIvp90Ghz3EkyiPpKW/+q XsScbWjOKM6ukB8aOJD6oCSjhwTGkhOLStvSXIn+JuhxWe257LEtEqtV8NUiyEpqLQ5Z jEauK+fJIA/CNVrcQc5klAi61UyrQwpqg8alO9n0bl+MF/2viQwe3XLD1PSwC2OxjyMd cDy3Jj3sPDi8c7+5rhd0r1VPYrJNPOBHXUK3VPm/CQwjhjidGBt53/SfXcnuRdBtdowZ 3Y2XPar4kVHhyUXPgLZUvTn98xG4VQRzYILNBGGGB61vPX5DokytbcX/xY0Hc4MWk9Na VnBQ== X-Received: by 10.28.7.67 with SMTP id 64mr61991wmh.70.1446684700494; Wed, 04 Nov 2015 16:51:40 -0800 (PST) Received: from amanieu-laptop.wireless.ropemaker.crm.lan ([31.205.92.76]) by smtp.gmail.com with ESMTPSA id 194sm5558927wmh.19.2015.11.04.16.51.39 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 04 Nov 2015 16:51:39 -0800 (PST) From: Amanieu d'Antras To: linux-kernel@vger.kernel.org Subject: [PATCH v2 02/20] compat: Add generic copy_siginfo_{to,from}_user32 Date: Thu, 5 Nov 2015 00:50:21 +0000 Message-Id: <1446684640-4112-3-git-send-email-amanieu@gmail.com> X-Mailer: git-send-email 2.6.2 In-Reply-To: <1446684640-4112-1-git-send-email-amanieu@gmail.com> References: <1446684640-4112-1-git-send-email-amanieu@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151104_165202_608029_9CE2C79A X-CRM114-Status: GOOD ( 23.40 ) X-Spam-Score: -2.0 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-mips@linux-mips.org, Catalin Marinas , Heiko Carstens , Chris Metcalf , Paul Mackerras , "H. Peter Anvin" , sparclinux@vger.kernel.org, linux-arch@vger.kernel.org, linux-s390@vger.kernel.org, Michael Ellerman , Helge Deller , x86@kernel.org, "James E.J. Bottomley" , Ingo Molnar , Benjamin Herrenschmidt , Arnd Bergmann , Will Deacon , Thomas Gleixner , linux-arm-kernel@lists.infradead.org, Amanieu d'Antras , linux-parisc@vger.kernel.org, linux-api@vger.kernel.org, Oleg Nesterov , Ralf Baechle , Martin Schwidefsky , linuxppc-dev@lists.ozlabs.org, "David S. Miller" MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 and have various bugs that cause behavior to diverge from that of a 32-bit kernel. 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. One thing to note is that, because the size of the siginfo_t union differs between 32-bit and 64-bit systems, we need to stash the last 4 bytes of the union in the 4 bytes of padding between the 64-bit union and the initial 3 siginfo_t members. Signed-off-by: Amanieu d'Antras --- 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 + include/uapi/asm-generic/siginfo.h | 1 + kernel/compat.c | 224 +++++++++++++++++++++++++++++++++++++ kernel/signal.c | 12 +- 11 files changed, 248 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/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h index 1e35520..cc8d95e 100644 --- a/include/uapi/asm-generic/siginfo.h +++ b/include/uapi/asm-generic/siginfo.h @@ -49,6 +49,7 @@ typedef struct siginfo { int si_signo; int si_errno; int si_code; + int _pad2[__ARCH_SI_PREAMBLE_SIZE / sizeof(int) - 3]; union { int _pad[SI_PAD_SIZE]; diff --git a/kernel/compat.c b/kernel/compat.c index 333d364..644da25 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -1174,3 +1174,227 @@ 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(compat_siginfo_t __user *to, const siginfo_t *from) +{ + int err, si_code; + + if (!access_ok(VERIFY_WRITE, to, sizeof(compat_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) { + /* + * Copy the tail bytes of the union from the padding, see the + * comment in copy_siginfo_from_user32. Note that this padding + * is always initialized when si_code < 0. + */ + BUILD_BUG_ON(sizeof(to->_sifields._pad) != + sizeof(from->_sifields._pad) + sizeof(from->_pad2)); + err |= __copy_to_user(to->_sifields._pad, from->_sifields._pad, + sizeof(from->_sifields._pad)) ? -EFAULT : 0; + err |= __copy_to_user(to->_sifields._pad + SI_PAD_SIZE, + from->_pad2, sizeof(from->_pad2)) ? -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, compat_siginfo_t __user *from) +{ + int err; + compat_uptr_t ptr32; + + if (!access_ok(VERIFY_READ, from, sizeof(compat_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. We work around this by copying any data + * that doesn't fit in the normal union into the padding before + * the union. + */ + BUILD_BUG_ON(sizeof(to->_sifields._pad) + sizeof(to->_pad2) != + sizeof(from->_sifields._pad)); + err |= __copy_from_user(to->_sifields._pad, + from->_sifields._pad, + sizeof(to->_sifields._pad)) ? -EFAULT : 0; + err |= __copy_from_user(to->_pad2, + from->_sifields._pad + SI_PAD_SIZE, sizeof(to->_pad2)) + ? -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 = NULL; /* 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 = NULL; /* 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. */