x86/signals: Add build-time checks to the siginfo compat code
There were at least 3 features added to the __SI_FAULT area of the siginfo struct that did not make it to the compat siginfo: 1. The si_addr_lsb used in SIGBUS's sent for machine checks 2. The upper/lower bounds for MPX SIGSEGV faults 3. The protection key for pkey faults There was also some turmoil when I was attempting to add the pkey field because it needs to be a fixed size on 32 and 64-bit and not have any alignment constraints. This patch adds some compile-time checks to the compat code to make it harder to screw this up. Basically, the checks are supposed to trip any time someone changes the siginfo structure. That sounds bad, but it's what we want. If someone changes siginfo, we want them to also be _forced_ to go look at the compat code. The details are in the comments. Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Hansen <dave@sr71.net> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tony Luck <tony.luck@intel.com> Cc: linux-edac@vger.kernel.org Link: http://lkml.kernel.org/r/20160608172534.C73DAFC3@viggo.jf.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Родитель
a4455082dc
Коммит
02e8fda2cc
|
@ -1,11 +1,104 @@
|
|||
#include <linux/compat.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/*
|
||||
* The compat_siginfo_t structure and handing code is very easy
|
||||
* to break in several ways. It must always be updated when new
|
||||
* updates are made to the main siginfo_t, and
|
||||
* copy_siginfo_to_user32() must be updated when the
|
||||
* (arch-independent) copy_siginfo_to_user() is updated.
|
||||
*
|
||||
* It is also easy to put a new member in the compat_siginfo_t
|
||||
* which has implicit alignment which can move internal structure
|
||||
* alignment around breaking the ABI. This can happen if you,
|
||||
* for instance, put a plain 64-bit value in there.
|
||||
*/
|
||||
static inline void signal_compat_build_tests(void)
|
||||
{
|
||||
int _sifields_offset = offsetof(compat_siginfo_t, _sifields);
|
||||
|
||||
/*
|
||||
* If adding a new si_code, there is probably new data in
|
||||
* the siginfo. Make sure folks bumping the si_code
|
||||
* limits also have to look at this code. Make sure any
|
||||
* new fields are handled in copy_siginfo_to_user32()!
|
||||
*/
|
||||
BUILD_BUG_ON(NSIGILL != 8);
|
||||
BUILD_BUG_ON(NSIGFPE != 8);
|
||||
BUILD_BUG_ON(NSIGSEGV != 4);
|
||||
BUILD_BUG_ON(NSIGBUS != 5);
|
||||
BUILD_BUG_ON(NSIGTRAP != 4);
|
||||
BUILD_BUG_ON(NSIGCHLD != 6);
|
||||
BUILD_BUG_ON(NSIGSYS != 1);
|
||||
|
||||
/* This is part of the ABI and can never change in size: */
|
||||
BUILD_BUG_ON(sizeof(compat_siginfo_t) != 128);
|
||||
/*
|
||||
* The offsets of all the (unioned) si_fields are fixed
|
||||
* in the ABI, of course. Make sure none of them ever
|
||||
* move and are always at the beginning:
|
||||
*/
|
||||
BUILD_BUG_ON(offsetof(compat_siginfo_t, _sifields) != 3 * sizeof(int));
|
||||
#define CHECK_CSI_OFFSET(name) BUILD_BUG_ON(_sifields_offset != offsetof(compat_siginfo_t, _sifields.name))
|
||||
|
||||
/*
|
||||
* Ensure that the size of each si_field never changes.
|
||||
* If it does, it is a sign that the
|
||||
* copy_siginfo_to_user32() code below needs to updated
|
||||
* along with the size in the CHECK_SI_SIZE().
|
||||
*
|
||||
* We repeat this check for both the generic and compat
|
||||
* siginfos.
|
||||
*
|
||||
* Note: it is OK for these to grow as long as the whole
|
||||
* structure stays within the padding size (checked
|
||||
* above).
|
||||
*/
|
||||
#define CHECK_CSI_SIZE(name, size) BUILD_BUG_ON(size != sizeof(((compat_siginfo_t *)0)->_sifields.name))
|
||||
#define CHECK_SI_SIZE(name, size) BUILD_BUG_ON(size != sizeof(((siginfo_t *)0)->_sifields.name))
|
||||
|
||||
CHECK_CSI_OFFSET(_kill);
|
||||
CHECK_CSI_SIZE (_kill, 2*sizeof(int));
|
||||
CHECK_SI_SIZE (_kill, 2*sizeof(int));
|
||||
|
||||
CHECK_CSI_OFFSET(_timer);
|
||||
CHECK_CSI_SIZE (_timer, 5*sizeof(int));
|
||||
CHECK_SI_SIZE (_timer, 6*sizeof(int));
|
||||
|
||||
CHECK_CSI_OFFSET(_rt);
|
||||
CHECK_CSI_SIZE (_rt, 3*sizeof(int));
|
||||
CHECK_SI_SIZE (_rt, 4*sizeof(int));
|
||||
|
||||
CHECK_CSI_OFFSET(_sigchld);
|
||||
CHECK_CSI_SIZE (_sigchld, 5*sizeof(int));
|
||||
CHECK_SI_SIZE (_sigchld, 8*sizeof(int));
|
||||
|
||||
CHECK_CSI_OFFSET(_sigchld_x32);
|
||||
CHECK_CSI_SIZE (_sigchld_x32, 7*sizeof(int));
|
||||
/* no _sigchld_x32 in the generic siginfo_t */
|
||||
|
||||
CHECK_CSI_OFFSET(_sigfault);
|
||||
CHECK_CSI_SIZE (_sigfault, 4*sizeof(int));
|
||||
CHECK_SI_SIZE (_sigfault, 8*sizeof(int));
|
||||
|
||||
CHECK_CSI_OFFSET(_sigpoll);
|
||||
CHECK_CSI_SIZE (_sigpoll, 2*sizeof(int));
|
||||
CHECK_SI_SIZE (_sigpoll, 4*sizeof(int));
|
||||
|
||||
CHECK_CSI_OFFSET(_sigsys);
|
||||
CHECK_CSI_SIZE (_sigsys, 3*sizeof(int));
|
||||
CHECK_SI_SIZE (_sigsys, 4*sizeof(int));
|
||||
|
||||
/* any new si_fields should be added here */
|
||||
}
|
||||
|
||||
int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from)
|
||||
{
|
||||
int err = 0;
|
||||
bool ia32 = test_thread_flag(TIF_IA32);
|
||||
|
||||
signal_compat_build_tests();
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче