Previously we were not clearing non-uapi flag bits in sigaction.sa_flags when storing the userspace-provided sa_flags or when returning them via oldact. Start doing so. This allows userspace to detect missing support for flag bits and allows the kernel to use non-uapi bits internally, as we are already doing in arch/x86 for two flag bits. Now that this change is in place, we no longer need the code in arch/x86 that was hiding these bits from userspace, so remove it. This is technically a userspace-visible behavior change for sigaction, as the unknown bits returned via oldact.sa_flags are no longer set. However, we are free to define the behavior for unknown bits exactly because their behavior is currently undefined, so for now we can define the meaning of each of them to be "clear the bit in oldact.sa_flags unless the bit becomes known in the future". Furthermore, this behavior is consistent with OpenBSD [1], illumos [2] and XNU [3] (FreeBSD [4] and NetBSD [5] fail the syscall if unknown bits are set). So there is some precedent for this behavior in other kernels, and in particular in XNU, which is probably the most popular kernel among those that I looked at, which means that this change is less likely to be a compatibility issue. Link: [1]f634a6a4b5/sys/kern/kern_sig.c (L278)Link: [2]76f19f5fdc/usr/src/uts/common/syscall/sigaction.c (L86)Link: [3]a449c6a3b8/bsd/kern/kern_sig.c (L480)Link: [4]eded70c370/sys/kern/kern_sig.c (L699)Link: [5]3365779bec/sys/kern/sys_sig.c (L473)Signed-off-by: Peter Collingbourne <pcc@google.com> Reviewed-by: Dave Martin <Dave.Martin@arm.com> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com> Link: https://linux-review.googlesource.com/id/I35aab6f5be932505d90f3b3450c083b4db1eca86 Link: https://lkml.kernel.org/r/878dbcb5f47bc9b11881c81f745c0bef5c23f97f.1605235762.git.pcc@google.com Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
176 lines
6.6 KiB
C
176 lines
6.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/compat.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/ptrace.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 != 11);
|
|
BUILD_BUG_ON(NSIGFPE != 15);
|
|
BUILD_BUG_ON(NSIGSEGV != 9);
|
|
BUILD_BUG_ON(NSIGBUS != 5);
|
|
BUILD_BUG_ON(NSIGTRAP != 5);
|
|
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))
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_signo) != 0);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_errno) != 4);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_code) != 8);
|
|
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_signo) != 0);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_errno) != 4);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_code) != 8);
|
|
/*
|
|
* 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));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pid) != 0x10);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_uid) != 0x14);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_pid) != 0xC);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_uid) != 0x10);
|
|
|
|
CHECK_CSI_OFFSET(_timer);
|
|
CHECK_CSI_SIZE (_timer, 3*sizeof(int));
|
|
CHECK_SI_SIZE (_timer, 6*sizeof(int));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_tid) != 0x10);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_overrun) != 0x14);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_value) != 0x18);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_tid) != 0x0C);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_overrun) != 0x10);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_value) != 0x14);
|
|
|
|
CHECK_CSI_OFFSET(_rt);
|
|
CHECK_CSI_SIZE (_rt, 3*sizeof(int));
|
|
CHECK_SI_SIZE (_rt, 4*sizeof(int));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pid) != 0x10);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_uid) != 0x14);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_value) != 0x18);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_pid) != 0x0C);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_uid) != 0x10);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_value) != 0x14);
|
|
|
|
CHECK_CSI_OFFSET(_sigchld);
|
|
CHECK_CSI_SIZE (_sigchld, 5*sizeof(int));
|
|
CHECK_SI_SIZE (_sigchld, 8*sizeof(int));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pid) != 0x10);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_uid) != 0x14);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_status) != 0x18);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_utime) != 0x20);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_stime) != 0x28);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_pid) != 0x0C);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_uid) != 0x10);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_status) != 0x14);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_utime) != 0x18);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_stime) != 0x1C);
|
|
|
|
#ifdef CONFIG_X86_X32_ABI
|
|
CHECK_CSI_OFFSET(_sigchld_x32);
|
|
CHECK_CSI_SIZE (_sigchld_x32, 7*sizeof(int));
|
|
/* no _sigchld_x32 in the generic siginfo_t */
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, _sifields._sigchld_x32._utime) != 0x18);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, _sifields._sigchld_x32._stime) != 0x20);
|
|
#endif
|
|
|
|
CHECK_CSI_OFFSET(_sigfault);
|
|
CHECK_CSI_SIZE (_sigfault, 4*sizeof(int));
|
|
CHECK_SI_SIZE (_sigfault, 8*sizeof(int));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_addr) != 0x10);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_addr) != 0x0C);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_addr_lsb) != 0x18);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_addr_lsb) != 0x10);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_lower) != 0x20);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_upper) != 0x28);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_lower) != 0x14);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_upper) != 0x18);
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_pkey) != 0x20);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_pkey) != 0x14);
|
|
|
|
CHECK_CSI_OFFSET(_sigpoll);
|
|
CHECK_CSI_SIZE (_sigpoll, 2*sizeof(int));
|
|
CHECK_SI_SIZE (_sigpoll, 4*sizeof(int));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_band) != 0x10);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_fd) != 0x18);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_band) != 0x0C);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_fd) != 0x10);
|
|
|
|
CHECK_CSI_OFFSET(_sigsys);
|
|
CHECK_CSI_SIZE (_sigsys, 3*sizeof(int));
|
|
CHECK_SI_SIZE (_sigsys, 4*sizeof(int));
|
|
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_call_addr) != 0x10);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_syscall) != 0x18);
|
|
BUILD_BUG_ON(offsetof(siginfo_t, si_arch) != 0x1C);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_call_addr) != 0x0C);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_syscall) != 0x10);
|
|
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_arch) != 0x14);
|
|
|
|
/* any new si_fields should be added here */
|
|
}
|
|
|
|
void sigaction_compat_abi(struct k_sigaction *act, struct k_sigaction *oact)
|
|
{
|
|
signal_compat_build_tests();
|
|
|
|
if (!act)
|
|
return;
|
|
|
|
if (in_ia32_syscall())
|
|
act->sa.sa_flags |= SA_IA32_ABI;
|
|
if (in_x32_syscall())
|
|
act->sa.sa_flags |= SA_X32_ABI;
|
|
}
|