* [PATCH v11 6/8] signal: deduplicate code dealing with common _sigfault fields
2020-10-09 0:44 [PATCH v11 0/8] arm64: expose FAR_EL1 tag bits in siginfo Peter Collingbourne
` (4 preceding siblings ...)
2020-10-09 0:44 ` [PATCH v11 5/8] signal: define the SA_UNSUPPORTED bit in sa_flags Peter Collingbourne
@ 2020-10-09 0:44 ` Peter Collingbourne
2020-10-09 0:44 ` [PATCH v11 7/8] signal: define the field siginfo.si_xflags Peter Collingbourne
2020-10-09 0:44 ` [PATCH v11 8/8] arm64: expose FAR_EL1 tag bits in siginfo Peter Collingbourne
7 siblings, 0 replies; 11+ messages in thread
From: Peter Collingbourne @ 2020-10-09 0:44 UTC (permalink / raw)
To: Catalin Marinas, Evgenii Stepanov, Kostya Serebryany,
Vincenzo Frascino, Dave Martin, Will Deacon, Oleg Nesterov,
Eric W. Biederman, James E.J. Bottomley
Cc: Peter Collingbourne, Linux ARM, Kevin Brodsky, Andrey Konovalov,
Richard Henderson, linux-api, Helge Deller, David Spickett
We're about to add more common _sigfault fields, so deduplicate the
existing code for initializing _sigfault fields in {send,force}_sig_*,
and for copying _sigfault fields in copy_siginfo_to_external32 and
post_copy_siginfo_from_user32, to reduce the number of places that
will need to be updated by upcoming changes.
Signed-off-by: Peter Collingbourne <pcc@google.com>
Link: https://linux-review.googlesource.com/id/I4f56174e1b7b2bf4a3c8139e6879cbfd52750a24
---
include/linux/signal.h | 13 ++++++
kernel/signal.c | 101 ++++++++++++++++-------------------------
2 files changed, 53 insertions(+), 61 deletions(-)
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 7bbc0e9cf084..9b7fef0c559d 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -50,6 +50,19 @@ enum siginfo_layout {
enum siginfo_layout siginfo_layout(unsigned sig, int si_code);
+static inline bool siginfo_layout_is_fault(enum siginfo_layout layout)
+{
+ switch (layout) {
+ case SIL_FAULT:
+ case SIL_FAULT_MCEERR:
+ case SIL_FAULT_BNDERR:
+ case SIL_FAULT_PKUERR:
+ return true;
+ default:
+ return false;
+ }
+}
+
/*
* Define some primitives to manipulate sigset_t.
*/
diff --git a/kernel/signal.c b/kernel/signal.c
index 018c19f6cf66..acdfd5a6d424 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1649,6 +1649,15 @@ void force_sigsegv(int sig)
force_sig(SIGSEGV);
}
+static void set_sigfault_common_fields(struct kernel_siginfo *info, int sig,
+ int code, void __user *addr)
+{
+ info->si_signo = sig;
+ info->si_errno = 0;
+ info->si_code = code;
+ info->si_addr = addr;
+}
+
int force_sig_fault_to_task(int sig, int code, void __user *addr
___ARCH_SI_TRAPNO(int trapno)
___ARCH_SI_IA64(int imm, unsigned int flags, unsigned long isr)
@@ -1657,10 +1666,7 @@ int force_sig_fault_to_task(int sig, int code, void __user *addr
struct kernel_siginfo info;
clear_siginfo(&info);
- info.si_signo = sig;
- info.si_errno = 0;
- info.si_code = code;
- info.si_addr = addr;
+ set_sigfault_common_fields(&info, sig, code, addr);
#ifdef __ARCH_SI_TRAPNO
info.si_trapno = trapno;
#endif
@@ -1689,10 +1695,7 @@ int send_sig_fault(int sig, int code, void __user *addr
struct kernel_siginfo info;
clear_siginfo(&info);
- info.si_signo = sig;
- info.si_errno = 0;
- info.si_code = code;
- info.si_addr = addr;
+ set_sigfault_common_fields(&info, sig, code, addr);
#ifdef __ARCH_SI_TRAPNO
info.si_trapno = trapno;
#endif
@@ -1710,10 +1713,7 @@ int force_sig_mceerr(int code, void __user *addr, short lsb)
WARN_ON((code != BUS_MCEERR_AO) && (code != BUS_MCEERR_AR));
clear_siginfo(&info);
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = code;
- info.si_addr = addr;
+ set_sigfault_common_fields(&info, SIGBUS, code, addr);
info.si_addr_lsb = lsb;
return force_sig_info(&info);
}
@@ -1724,10 +1724,7 @@ int send_sig_mceerr(int code, void __user *addr, short lsb, struct task_struct *
WARN_ON((code != BUS_MCEERR_AO) && (code != BUS_MCEERR_AR));
clear_siginfo(&info);
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = code;
- info.si_addr = addr;
+ set_sigfault_common_fields(&info, SIGBUS, code, addr);
info.si_addr_lsb = lsb;
return send_sig_info(info.si_signo, &info, t);
}
@@ -1738,10 +1735,7 @@ int force_sig_bnderr(void __user *addr, void __user *lower, void __user *upper)
struct kernel_siginfo info;
clear_siginfo(&info);
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = SEGV_BNDERR;
- info.si_addr = addr;
+ set_sigfault_common_fields(&info, SIGSEGV, SEGV_BNDERR, addr);
info.si_lower = lower;
info.si_upper = upper;
return force_sig_info(&info);
@@ -1753,10 +1747,7 @@ int force_sig_pkuerr(void __user *addr, u32 pkey)
struct kernel_siginfo info;
clear_siginfo(&info);
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = SEGV_PKUERR;
- info.si_addr = addr;
+ set_sigfault_common_fields(&info, SIGSEGV, SEGV_PKUERR, addr);
info.si_pkey = pkey;
return force_sig_info(&info);
}
@@ -1770,10 +1761,8 @@ int force_sig_ptrace_errno_trap(int errno, void __user *addr)
struct kernel_siginfo info;
clear_siginfo(&info);
- info.si_signo = SIGTRAP;
+ set_sigfault_common_fields(&info, SIGTRAP, TRAP_HWBKPT, addr);
info.si_errno = errno;
- info.si_code = TRAP_HWBKPT;
- info.si_addr = addr;
return force_sig_info(&info);
}
@@ -3266,12 +3255,23 @@ int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from)
void copy_siginfo_to_external32(struct compat_siginfo *to,
const struct kernel_siginfo *from)
{
+ enum siginfo_layout layout =
+ siginfo_layout(from->si_signo, from->si_code);
+
memset(to, 0, sizeof(*to));
to->si_signo = from->si_signo;
to->si_errno = from->si_errno;
to->si_code = from->si_code;
- switch(siginfo_layout(from->si_signo, from->si_code)) {
+
+ if (siginfo_layout_is_fault(layout)) {
+ to->si_addr = ptr_to_compat(from->si_addr);
+#ifdef __ARCH_SI_TRAPNO
+ to->si_trapno = from->si_trapno;
+#endif
+ }
+
+ switch (layout) {
case SIL_KILL:
to->si_pid = from->si_pid;
to->si_uid = from->si_uid;
@@ -3286,31 +3286,15 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
to->si_fd = from->si_fd;
break;
case SIL_FAULT:
- to->si_addr = ptr_to_compat(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
break;
case SIL_FAULT_MCEERR:
- to->si_addr = ptr_to_compat(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
to->si_addr_lsb = from->si_addr_lsb;
break;
case SIL_FAULT_BNDERR:
- to->si_addr = ptr_to_compat(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
to->si_lower = ptr_to_compat(from->si_lower);
to->si_upper = ptr_to_compat(from->si_upper);
break;
case SIL_FAULT_PKUERR:
- to->si_addr = ptr_to_compat(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
to->si_pkey = from->si_pkey;
break;
case SIL_CHLD:
@@ -3347,11 +3331,22 @@ int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
const struct compat_siginfo *from)
{
+ enum siginfo_layout layout =
+ siginfo_layout(from->si_signo, from->si_code);
+
clear_siginfo(to);
to->si_signo = from->si_signo;
to->si_errno = from->si_errno;
to->si_code = from->si_code;
- switch(siginfo_layout(from->si_signo, from->si_code)) {
+
+ if (siginfo_layout_is_fault(layout)) {
+ to->si_addr = compat_ptr(from->si_addr);
+#ifdef __ARCH_SI_TRAPNO
+ to->si_trapno = from->si_trapno;
+#endif
+ }
+
+ switch (layout) {
case SIL_KILL:
to->si_pid = from->si_pid;
to->si_uid = from->si_uid;
@@ -3366,31 +3361,15 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
to->si_fd = from->si_fd;
break;
case SIL_FAULT:
- to->si_addr = compat_ptr(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
break;
case SIL_FAULT_MCEERR:
- to->si_addr = compat_ptr(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
to->si_addr_lsb = from->si_addr_lsb;
break;
case SIL_FAULT_BNDERR:
- to->si_addr = compat_ptr(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
to->si_lower = compat_ptr(from->si_lower);
to->si_upper = compat_ptr(from->si_upper);
break;
case SIL_FAULT_PKUERR:
- to->si_addr = compat_ptr(from->si_addr);
-#ifdef __ARCH_SI_TRAPNO
- to->si_trapno = from->si_trapno;
-#endif
to->si_pkey = from->si_pkey;
break;
case SIL_CHLD:
--
2.28.0.1011.ga647a8990f-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v11 7/8] signal: define the field siginfo.si_xflags
2020-10-09 0:44 [PATCH v11 0/8] arm64: expose FAR_EL1 tag bits in siginfo Peter Collingbourne
` (5 preceding siblings ...)
2020-10-09 0:44 ` [PATCH v11 6/8] signal: deduplicate code dealing with common _sigfault fields Peter Collingbourne
@ 2020-10-09 0:44 ` Peter Collingbourne
2020-10-09 0:44 ` [PATCH v11 8/8] arm64: expose FAR_EL1 tag bits in siginfo Peter Collingbourne
7 siblings, 0 replies; 11+ messages in thread
From: Peter Collingbourne @ 2020-10-09 0:44 UTC (permalink / raw)
To: Catalin Marinas, Evgenii Stepanov, Kostya Serebryany,
Vincenzo Frascino, Dave Martin, Will Deacon, Oleg Nesterov,
Eric W. Biederman, James E.J. Bottomley
Cc: Peter Collingbourne, Linux ARM, Kevin Brodsky, Andrey Konovalov,
Richard Henderson, linux-api, Helge Deller, David Spickett
This field will contain flags that may be used by signal handlers to
determine whether other fields in the _sigfault portion of siginfo are
valid. An example use case is the following patch, which introduces
the si_addr_ignored_bits{,_mask} fields.
A new sigcontext flag, SA_XFLAGS, is introduced in order to allow
a signal handler to require the kernel to set the field (but note
that the field will be set anyway if the kernel supports the flag,
regardless of its value). In combination with the previous patches,
this allows a userspace program to determine whether the kernel will
set the field.
It is possible for an si_xflags-unaware program to cause a signal
handler in an si_xflags-aware program to be called with a provided
siginfo data structure by using one of the following syscalls:
- ptrace(PTRACE_SETSIGINFO)
- pidfd_send_signal
- rt_sigqueueinfo
- rt_tgsigqueueinfo
So we need to prevent the si_xflags-unaware program from causing an
uninitialized read of si_xflags in the si_xflags-aware program when
it uses one of these syscalls.
The last three cases can be handled by observing that each of these
syscalls fails if si_code >= 0. We also observe that kill(2) and
tgkill(2) may be used to send a signal where si_code == 0 (SI_USER),
so we define si_xflags to only be valid if si_code > 0.
There is no such check on si_code in ptrace(PTRACE_SETSIGINFO), so
we make ptrace(PTRACE_SETSIGINFO) clear the si_xflags field if it
detects that the signal would use the _sigfault layout, and introduce
a new ptrace request type, PTRACE_SETSIGINFO2, that a si_xflags-aware
program may use to opt out of this behavior.
It is also possible for the kernel to inject a signal specified to
use _sigfault by calling force_sig (e.g. there are numerous calls to
force_sig(SIGSEGV)). In this case si_code is set to SI_KERNEL and the
_kill union member is used, so document that si_code must be < SI_KERNEL.
Ideally this field could have just been named si_flags, but that
name was already taken by ia64, so a different name was chosen.
I considered making ia64's si_flags a generic field and having it
appear at the end of _sigfault (in the same place as this patch has
si_xflags) on non-ia64, keeping it in the same place on ia64. ia64's
si_flags is a 32-bit field with only one flag bit allocated, so we
would have 31 bits to use if we do this. However, it seems simplest
to avoid entangling these fields.
Signed-off-by: Peter Collingbourne <pcc@google.com>
Link: https://linux-review.googlesource.com/id/Ide155ce29366c3eab2a944ae4c51205982e5b8b2
---
v11:
- update comment to say that si_code must > 0
- change ptrace(PTRACE_SETSIGINFO2) to take a flags argument
v10:
- make the new field compatible with the various ways
that a siginfo can be injected from another process
- eliminate some duplication by adding a refactoring patch
before this one
arch/powerpc/platforms/powernv/vas-fault.c | 1 +
arch/x86/kernel/signal_compat.c | 4 +--
include/linux/compat.h | 2 ++
include/linux/signal_types.h | 2 +-
include/uapi/asm-generic/siginfo.h | 4 +++
include/uapi/asm-generic/signal-defs.h | 4 +++
include/uapi/linux/ptrace.h | 12 ++++++++
kernel/ptrace.c | 32 ++++++++++++++++++----
kernel/signal.c | 3 ++
9 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/vas-fault.c b/arch/powerpc/platforms/powernv/vas-fault.c
index 3d21fce254b7..3bbb335561f5 100644
--- a/arch/powerpc/platforms/powernv/vas-fault.c
+++ b/arch/powerpc/platforms/powernv/vas-fault.c
@@ -154,6 +154,7 @@ static void update_csb(struct vas_window *window,
info.si_errno = EFAULT;
info.si_code = SEGV_MAPERR;
info.si_addr = csb_addr;
+ info.si_xflags = 0;
/*
* process will be polling on csb.flags after request is sent to
diff --git a/arch/x86/kernel/signal_compat.c b/arch/x86/kernel/signal_compat.c
index c599013ae8cb..6b99f0c8a068 100644
--- a/arch/x86/kernel/signal_compat.c
+++ b/arch/x86/kernel/signal_compat.c
@@ -121,8 +121,8 @@ static inline void signal_compat_build_tests(void)
#endif
CHECK_CSI_OFFSET(_sigfault);
- CHECK_CSI_SIZE (_sigfault, 4*sizeof(int));
- CHECK_SI_SIZE (_sigfault, 8*sizeof(int));
+ CHECK_CSI_SIZE (_sigfault, 8*sizeof(int));
+ CHECK_SI_SIZE (_sigfault, 16*sizeof(int));
BUILD_BUG_ON(offsetof(siginfo_t, si_addr) != 0x10);
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_addr) != 0x0C);
diff --git a/include/linux/compat.h b/include/linux/compat.h
index b354ce58966e..758f95aff5b5 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -231,7 +231,9 @@ typedef struct compat_siginfo {
char _dummy_pkey[__COMPAT_ADDR_BND_PKEY_PAD];
u32 _pkey;
} _addr_pkey;
+ compat_uptr_t _pad[6];
};
+ u64 _xflags;
} _sigfault;
/* SIGPOLL */
diff --git a/include/linux/signal_types.h b/include/linux/signal_types.h
index a7887ad84d36..75ca861d982a 100644
--- a/include/linux/signal_types.h
+++ b/include/linux/signal_types.h
@@ -78,6 +78,6 @@ struct ksignal {
#define UAPI_SA_FLAGS \
(SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART | \
- SA_NODEFER | SA_RESETHAND | __ARCH_UAPI_SA_FLAGS)
+ SA_NODEFER | SA_RESETHAND | SA_XFLAGS | __ARCH_UAPI_SA_FLAGS)
#endif /* _LINUX_SIGNAL_TYPES_H */
diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index cb3d6c267181..fc12513f5d09 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -91,7 +91,9 @@ union __sifields {
char _dummy_pkey[__ADDR_BND_PKEY_PAD];
__u32 _pkey;
} _addr_pkey;
+ void *_pad[6];
};
+ u64 _xflags;
} _sigfault;
/* SIGPOLL */
@@ -152,6 +154,8 @@ typedef struct siginfo {
#define si_trapno _sifields._sigfault._trapno
#endif
#define si_addr_lsb _sifields._sigfault._addr_lsb
+/* si_xflags is only valid if 0 < si_code < SI_KERNEL */
+#define si_xflags _sifields._sigfault._xflags
#define si_lower _sifields._sigfault._addr_bnd._lower
#define si_upper _sifields._sigfault._addr_bnd._upper
#define si_pkey _sifields._sigfault._addr_pkey._pkey
diff --git a/include/uapi/asm-generic/signal-defs.h b/include/uapi/asm-generic/signal-defs.h
index 0126ebda4d31..cd522819f4ba 100644
--- a/include/uapi/asm-generic/signal-defs.h
+++ b/include/uapi/asm-generic/signal-defs.h
@@ -20,6 +20,9 @@
* so this bit allows flag bit support to be detected from userspace while
* allowing an old kernel to be distinguished from a kernel that supports every
* flag bit.
+ * SA_XFLAGS indicates that the signal handler requires the siginfo.si_xflags
+ * field to be valid. Note that if the kernel supports SA_XFLAGS, the field will
+ * be valid regardless of the value of this flag.
*
* SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
* Unix names RESETHAND and NODEFER respectively.
@@ -49,6 +52,7 @@
#define SA_RESETHAND 0x80000000
#endif
#define SA_UNSUPPORTED 0x00000400
+#define SA_XFLAGS 0x00000800
#define SA_NOMASK SA_NODEFER
#define SA_ONESHOT SA_RESETHAND
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h
index a71b6e3b03eb..93946edf0139 100644
--- a/include/uapi/linux/ptrace.h
+++ b/include/uapi/linux/ptrace.h
@@ -101,6 +101,18 @@ struct ptrace_syscall_info {
};
};
+#define PTRACE_SETSIGINFO2 0x420f
+/*
+ * These flags are passed as the addr argument to ptrace.
+ */
+
+/*
+ * Asserts that the caller is aware of the field siginfo.si_xflags. Prevents
+ * the kernel from automatically setting the field to 0 when the signal uses
+ * a sigfault layout.
+ */
+#define PTRACE_SIGINFO_XFLAGS 0x1
+
/*
* These values are stored in task->ptrace_message
* by tracehook_report_syscall_* to describe the current syscall-stop.
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 43d6179508d6..85b5b4e38661 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -687,18 +687,32 @@ static int ptrace_getsiginfo(struct task_struct *child, kernel_siginfo_t *info)
return error;
}
-static int ptrace_setsiginfo(struct task_struct *child, const kernel_siginfo_t *info)
+static int ptrace_setsiginfo(struct task_struct *child, unsigned long flags,
+ kernel_siginfo_t *info)
{
- unsigned long flags;
+ unsigned long lock_flags;
int error = -ESRCH;
- if (lock_task_sighand(child, &flags)) {
+ if (flags & ~PTRACE_SIGINFO_XFLAGS) {
+ return -EINVAL;
+ }
+
+ /*
+ * If the caller is unaware of si_xflags and we're using a layout that
+ * requires it, set it to 0 which means "no fields are available".
+ */
+ if (!(flags & PTRACE_SIGINFO_XFLAGS) &&
+ siginfo_layout_is_fault(
+ siginfo_layout(info->si_signo, info->si_code)))
+ info->si_xflags = 0;
+
+ if (lock_task_sighand(child, &lock_flags)) {
error = -EINVAL;
if (likely(child->last_siginfo != NULL)) {
copy_siginfo(child->last_siginfo, info);
error = 0;
}
- unlock_task_sighand(child, &flags);
+ unlock_task_sighand(child, &lock_flags);
}
return error;
}
@@ -1038,9 +1052,12 @@ int ptrace_request(struct task_struct *child, long request,
break;
case PTRACE_SETSIGINFO:
+ addr = 0;
+
+ case PTRACE_SETSIGINFO2:
ret = copy_siginfo_from_user(&siginfo, datavp);
if (!ret)
- ret = ptrace_setsiginfo(child, &siginfo);
+ ret = ptrace_setsiginfo(child, addr, &siginfo);
break;
case PTRACE_GETSIGMASK: {
@@ -1347,10 +1364,13 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
break;
case PTRACE_SETSIGINFO:
+ addr = 0;
+
+ case PTRACE_SETSIGINFO2:
ret = copy_siginfo_from_user32(
&siginfo, (struct compat_siginfo __user *) datap);
if (!ret)
- ret = ptrace_setsiginfo(child, &siginfo);
+ ret = ptrace_setsiginfo(child, addr, &siginfo);
break;
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
case PTRACE_GETREGSET:
diff --git a/kernel/signal.c b/kernel/signal.c
index acdfd5a6d424..5c715a01942f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1656,6 +1656,7 @@ static void set_sigfault_common_fields(struct kernel_siginfo *info, int sig,
info->si_errno = 0;
info->si_code = code;
info->si_addr = addr;
+ info->si_xflags = 0;
}
int force_sig_fault_to_task(int sig, int code, void __user *addr
@@ -3269,6 +3270,7 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
#ifdef __ARCH_SI_TRAPNO
to->si_trapno = from->si_trapno;
#endif
+ to->si_xflags = from->si_xflags;
}
switch (layout) {
@@ -3344,6 +3346,7 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
#ifdef __ARCH_SI_TRAPNO
to->si_trapno = from->si_trapno;
#endif
+ to->si_xflags = from->si_xflags;
}
switch (layout) {
--
2.28.0.1011.ga647a8990f-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v11 8/8] arm64: expose FAR_EL1 tag bits in siginfo
2020-10-09 0:44 [PATCH v11 0/8] arm64: expose FAR_EL1 tag bits in siginfo Peter Collingbourne
` (6 preceding siblings ...)
2020-10-09 0:44 ` [PATCH v11 7/8] signal: define the field siginfo.si_xflags Peter Collingbourne
@ 2020-10-09 0:44 ` Peter Collingbourne
7 siblings, 0 replies; 11+ messages in thread
From: Peter Collingbourne @ 2020-10-09 0:44 UTC (permalink / raw)
To: Catalin Marinas, Evgenii Stepanov, Kostya Serebryany,
Vincenzo Frascino, Dave Martin, Will Deacon, Oleg Nesterov,
Eric W. Biederman, James E.J. Bottomley
Cc: Peter Collingbourne, Linux ARM, Kevin Brodsky, Andrey Konovalov,
Richard Henderson, linux-api, Helge Deller, David Spickett
The kernel currently clears the tag bits (i.e. bits 56-63) in the fault
address exposed via siginfo.si_addr and sigcontext.fault_address. However,
the tag bits may be needed by tools in order to accurately diagnose
memory errors, such as HWASan [1] or future tools based on the Memory
Tagging Extension (MTE).
We should not stop clearing these bits in the existing fault address
fields, because there may be existing userspace applications that are
expecting the tag bits to be cleared. Instead, create a new pair of
union fields in siginfo._sigfault, and store the tag bits of FAR_EL1
there, together with a mask specifying which bits are valid.
A flag is added to si_xflags to allow userspace to determine whether
the values in the fields are valid.
[1] http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
Signed-off-by: Peter Collingbourne <pcc@google.com>
Link: https://linux-review.googlesource.com/id/Ia8876bad8c798e0a32df7c2ce1256c4771c81446
---
v11:
- add a comment explaining what the arch hook should do
- rename ignored bits to tag bits
v10:
- rename the flag to SIXFLAG_ADDR_IGNORED_BITS
- use an arch hook to specify which bits are ignored, instead
of passing them explicitly
- while refactoring for the arch hook, noticed that my previous
patches missed a case involving cache maintenance instructions,
so expose the tag bits for that signal as well
v9:
- make the ignored bits fields generic
- add some new dependent patches that prepare us to store the
field in such a way that userspace can detect their presence
v8:
- rebase onto 5.8rc2
v7:
- switch to a new siginfo field instead of using sigcontext
- merge the patch back into one since the other patches are now
unnecessary
v6:
- move fault address and fault code into the kernel_siginfo data structure
- split the patch in three since it was getting large and now has
generic and arch-specific parts
v5:
- add padding to fault_addr_top_byte_context in order to ensure the correct
size and preserve sp alignment
v4:
- expose only the tag bits in the context instead of the entire FAR_EL1
- remove mention of the new context from the sigcontext.__reserved[] note
v3:
- add documentation to tagged-pointers.rst
- update comments in sigcontext.h
v2:
- revert changes to hw_breakpoint.c
- rename set_thread_esr to set_thread_far_esr
Documentation/arm64/tagged-pointers.rst | 21 ++++++---
arch/arm64/include/asm/exception.h | 2 +-
arch/arm64/include/asm/signal.h | 17 +++++++
arch/arm64/include/asm/system_misc.h | 2 +-
arch/arm64/include/asm/traps.h | 6 +--
arch/arm64/kernel/debug-monitors.c | 5 +--
arch/arm64/kernel/entry-common.c | 2 -
arch/arm64/kernel/ptrace.c | 7 +--
arch/arm64/kernel/sys_compat.c | 5 +--
arch/arm64/kernel/traps.c | 29 ++++++------
arch/arm64/mm/fault.c | 59 +++++++++++++------------
arch/x86/kernel/signal_compat.c | 4 +-
include/linux/compat.h | 2 +
include/linux/signal.h | 16 +++++++
include/uapi/asm-generic/siginfo.h | 10 +++++
kernel/signal.c | 18 +++++++-
16 files changed, 134 insertions(+), 71 deletions(-)
create mode 100644 arch/arm64/include/asm/signal.h
diff --git a/Documentation/arm64/tagged-pointers.rst b/Documentation/arm64/tagged-pointers.rst
index eab4323609b9..032c09a876f4 100644
--- a/Documentation/arm64/tagged-pointers.rst
+++ b/Documentation/arm64/tagged-pointers.rst
@@ -53,12 +53,21 @@ visibility.
Preserving tags
---------------
-Non-zero tags are not preserved when delivering signals. This means that
-signal handlers in applications making use of tags cannot rely on the
-tag information for user virtual addresses being maintained for fields
-inside siginfo_t. One exception to this rule is for signals raised in
-response to watchpoint debug exceptions, where the tag information will
-be preserved.
+Non-zero tags are not preserved in the fault address fields
+siginfo.si_addr or sigcontext.fault_address when delivering
+signals. This means that signal handlers in applications making use
+of tags cannot rely on the tag information for user virtual addresses
+being maintained in these fields. One exception to this rule is for
+signals raised in response to watchpoint debug exceptions, where the
+tag information will be preserved.
+
+The fault address tag is preserved in the si_addr_tag_bits field
+of siginfo, which is set for signals raised in response to data aborts
+and instruction aborts. The si_addr_tag_bits_mask field indicates
+which bits of the field are valid. The validity of these fields is
+indicated by the SIXFLAG_ADDR_TAG_BITS flag in siginfo.si_xflags,
+and the validity of si_xflags in turn is indicated by the kernel
+indicating support for the sigaction.sa_flags flag SA_XFLAGS.
The architecture prevents the use of a tagged PC, so the upper byte will
be set to a sign-extension of bit 55 on exception return.
diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h
index 7577a754d443..950d55dae948 100644
--- a/arch/arm64/include/asm/exception.h
+++ b/arch/arm64/include/asm/exception.h
@@ -32,7 +32,7 @@ static inline u32 disr_to_esr(u64 disr)
}
asmlinkage void enter_from_user_mode(void);
-void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
+void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs);
void do_undefinstr(struct pt_regs *regs);
void do_bti(struct pt_regs *regs);
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
diff --git a/arch/arm64/include/asm/signal.h b/arch/arm64/include/asm/signal.h
new file mode 100644
index 000000000000..01cb002feacb
--- /dev/null
+++ b/arch/arm64/include/asm/signal.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ARM64_ASM_SIGNAL_H
+#define __ARM64_ASM_SIGNAL_H
+
+#include <uapi/asm/signal.h>
+#include <uapi/asm/siginfo.h>
+
+static inline unsigned long arch_addr_tag_bits_mask(unsigned long sig,
+ unsigned long si_code)
+{
+ if (sig == SIGTRAP && si_code == TRAP_BRKPT)
+ return 0;
+ return 0xffUL << 56;
+}
+#define arch_addr_tag_bits_mask arch_addr_tag_bits_mask
+
+#endif
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index 1ab63cfbbaf1..673be2d1263c 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -22,7 +22,7 @@ void die(const char *msg, struct pt_regs *regs, int err);
struct siginfo;
void arm64_notify_die(const char *str, struct pt_regs *regs,
- int signo, int sicode, void __user *addr,
+ int signo, int sicode, unsigned long far,
int err);
void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
index cee5928e1b7d..9bb5376608d3 100644
--- a/arch/arm64/include/asm/traps.h
+++ b/arch/arm64/include/asm/traps.h
@@ -26,9 +26,9 @@ void register_undef_hook(struct undef_hook *hook);
void unregister_undef_hook(struct undef_hook *hook);
void force_signal_inject(int signal, int code, unsigned long address);
void arm64_notify_segfault(unsigned long addr);
-void arm64_force_sig_fault(int signo, int code, void __user *addr, const char *str);
-void arm64_force_sig_mceerr(int code, void __user *addr, short lsb, const char *str);
-void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr, const char *str);
+void arm64_force_sig_fault(int signo, int code, unsigned long far, const char *str);
+void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str);
+void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str);
/*
* Move regs->pc to next instruction and do necessary setup before it
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 7310a4f7f993..943e932def37 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -234,9 +234,8 @@ static void send_user_sigtrap(int si_code)
if (interrupts_enabled(regs))
local_irq_enable();
- arm64_force_sig_fault(SIGTRAP, si_code,
- (void __user *)instruction_pointer(regs),
- "User debug trap");
+ arm64_force_sig_fault(SIGTRAP, si_code, instruction_pointer(regs),
+ "User debug trap");
}
static int single_step_handler(unsigned long unused, unsigned int esr,
diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index d3be9dbf5490..65ed01606480 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -22,7 +22,6 @@ static void notrace el1_abort(struct pt_regs *regs, unsigned long esr)
unsigned long far = read_sysreg(far_el1);
local_daif_inherit(regs);
- far = untagged_addr(far);
do_mem_abort(far, esr, regs);
}
NOKPROBE_SYMBOL(el1_abort);
@@ -104,7 +103,6 @@ static void notrace el0_da(struct pt_regs *regs, unsigned long esr)
user_exit_irqoff();
local_daif_restore(DAIF_PROCCTX);
- far = untagged_addr(far);
do_mem_abort(far, esr, regs);
}
NOKPROBE_SYMBOL(el0_da);
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index d8ebfd813e28..3f12c731fa9a 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -191,14 +191,11 @@ static void ptrace_hbptriggered(struct perf_event *bp,
break;
}
}
- arm64_force_sig_ptrace_errno_trap(si_errno,
- (void __user *)bkpt->trigger,
+ arm64_force_sig_ptrace_errno_trap(si_errno, bkpt->trigger,
desc);
}
#endif
- arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT,
- (void __user *)(bkpt->trigger),
- desc);
+ arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT, bkpt->trigger, desc);
}
/*
diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c
index 3c18c2454089..265fe3eb1069 100644
--- a/arch/arm64/kernel/sys_compat.c
+++ b/arch/arm64/kernel/sys_compat.c
@@ -68,7 +68,7 @@ do_compat_cache_op(unsigned long start, unsigned long end, int flags)
*/
long compat_arm_syscall(struct pt_regs *regs, int scno)
{
- void __user *addr;
+ unsigned long addr;
switch (scno) {
/*
@@ -111,8 +111,7 @@ long compat_arm_syscall(struct pt_regs *regs, int scno)
break;
}
- addr = (void __user *)instruction_pointer(regs) -
- (compat_thumb_mode(regs) ? 2 : 4);
+ addr = instruction_pointer(regs) - (compat_thumb_mode(regs) ? 2 : 4);
arm64_notify_die("Oops - bad compat syscall(2)", regs,
SIGILL, ILL_ILLTRP, addr, scno);
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 13ebd5ca2070..8f8105076f92 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -234,32 +234,32 @@ static void arm64_show_signal(int signo, const char *str)
__show_regs(regs);
}
-void arm64_force_sig_fault(int signo, int code, void __user *addr,
+void arm64_force_sig_fault(int signo, int code, unsigned long far,
const char *str)
{
arm64_show_signal(signo, str);
if (signo == SIGKILL)
force_sig(SIGKILL);
else
- force_sig_fault(signo, code, addr);
+ force_sig_fault(signo, code, (void __user *)far);
}
-void arm64_force_sig_mceerr(int code, void __user *addr, short lsb,
+void arm64_force_sig_mceerr(int code, unsigned long far, short lsb,
const char *str)
{
arm64_show_signal(SIGBUS, str);
- force_sig_mceerr(code, addr, lsb);
+ force_sig_mceerr(code, (void __user *)far, lsb);
}
-void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr,
+void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far,
const char *str)
{
arm64_show_signal(SIGTRAP, str);
- force_sig_ptrace_errno_trap(errno, addr);
+ force_sig_ptrace_errno_trap(errno, (void __user *)far);
}
void arm64_notify_die(const char *str, struct pt_regs *regs,
- int signo, int sicode, void __user *addr,
+ int signo, int sicode, unsigned long far,
int err)
{
if (user_mode(regs)) {
@@ -267,7 +267,7 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
current->thread.fault_address = 0;
current->thread.fault_code = err;
- arm64_force_sig_fault(signo, sicode, addr, str);
+ arm64_force_sig_fault(signo, sicode, far, str);
} else {
die(str, regs, err);
}
@@ -438,7 +438,7 @@ void force_signal_inject(int signal, int code, unsigned long address)
signal = SIGKILL;
}
- arm64_notify_die(desc, regs, signal, code, (void __user *)address, 0);
+ arm64_notify_die(desc, regs, signal, code, address, 0);
}
/*
@@ -449,7 +449,7 @@ void arm64_notify_segfault(unsigned long addr)
int code;
mmap_read_lock(current->mm);
- if (find_vma(current->mm, addr) == NULL)
+ if (find_vma(current->mm, untagged_addr(addr)) == NULL)
code = SEGV_MAPERR;
else
code = SEGV_ACCERR;
@@ -501,12 +501,13 @@ NOKPROBE_SYMBOL(do_bti);
static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
{
- unsigned long address;
+ unsigned long tagged_address, address;
int rt = ESR_ELx_SYS64_ISS_RT(esr);
int crm = (esr & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT;
int ret = 0;
- address = untagged_addr(pt_regs_read_reg(regs, rt));
+ tagged_address = pt_regs_read_reg(regs, rt);
+ address = untagged_addr(tagged_address);
switch (crm) {
case ESR_ELx_SYS64_ISS_CRM_DC_CVAU: /* DC CVAU, gets promoted */
@@ -533,7 +534,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
}
if (ret)
- arm64_notify_segfault(address);
+ arm64_notify_segfault(tagged_address);
else
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
}
@@ -824,7 +825,7 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
*/
void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
{
- void __user *pc = (void __user *)instruction_pointer(regs);
+ unsigned long pc = instruction_pointer(regs);
current->thread.fault_address = 0;
current->thread.fault_code = esr;
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index f07333e86c2f..d17953d9b3d9 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -40,7 +40,7 @@
#include <asm/traps.h>
struct fault_info {
- int (*fn)(unsigned long addr, unsigned int esr,
+ int (*fn)(unsigned long far, unsigned int esr,
struct pt_regs *regs);
int sig;
int code;
@@ -383,8 +383,11 @@ static void set_thread_esr(unsigned long address, unsigned int esr)
current->thread.fault_code = esr;
}
-static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+static void do_bad_area(unsigned long far, unsigned int esr,
+ struct pt_regs *regs)
{
+ unsigned long addr = untagged_addr(far);
+
/*
* If we are in kernel mode at this point, we have no context to
* handle this fault with.
@@ -393,8 +396,7 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
const struct fault_info *inf = esr_to_fault_info(esr);
set_thread_esr(addr, esr);
- arm64_force_sig_fault(inf->sig, inf->code, (void __user *)addr,
- inf->name);
+ arm64_force_sig_fault(inf->sig, inf->code, far, inf->name);
} else {
__do_kernel_fault(addr, esr, regs);
}
@@ -446,7 +448,7 @@ static bool is_write_abort(unsigned int esr)
return (esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM);
}
-static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
+static int __kprobes do_page_fault(unsigned long far, unsigned int esr,
struct pt_regs *regs)
{
const struct fault_info *inf;
@@ -454,6 +456,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
vm_fault_t fault;
unsigned long vm_flags = VM_ACCESS_FLAGS;
unsigned int mm_flags = FAULT_FLAG_DEFAULT;
+ unsigned long addr = untagged_addr(far);
if (kprobe_page_fault(regs, esr))
return 0;
@@ -565,8 +568,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
* We had some memory, but were unable to successfully fix up
* this page fault.
*/
- arm64_force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)addr,
- inf->name);
+ arm64_force_sig_fault(SIGBUS, BUS_ADRERR, far, inf->name);
} else if (fault & (VM_FAULT_HWPOISON_LARGE | VM_FAULT_HWPOISON)) {
unsigned int lsb;
@@ -574,8 +576,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
if (fault & VM_FAULT_HWPOISON_LARGE)
lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
- arm64_force_sig_mceerr(BUS_MCEERR_AR, (void __user *)addr, lsb,
- inf->name);
+ arm64_force_sig_mceerr(BUS_MCEERR_AR, far, lsb, inf->name);
} else {
/*
* Something tried to access memory that isn't in our memory
@@ -583,8 +584,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
*/
arm64_force_sig_fault(SIGSEGV,
fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR,
- (void __user *)addr,
- inf->name);
+ far, inf->name);
}
return 0;
@@ -594,33 +594,35 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
return 0;
}
-static int __kprobes do_translation_fault(unsigned long addr,
+static int __kprobes do_translation_fault(unsigned long far,
unsigned int esr,
struct pt_regs *regs)
{
+ unsigned long addr = untagged_addr(far);
+
if (is_ttbr0_addr(addr))
- return do_page_fault(addr, esr, regs);
+ return do_page_fault(far, esr, regs);
- do_bad_area(addr, esr, regs);
+ do_bad_area(far, esr, regs);
return 0;
}
-static int do_alignment_fault(unsigned long addr, unsigned int esr,
+static int do_alignment_fault(unsigned long far, unsigned int esr,
struct pt_regs *regs)
{
- do_bad_area(addr, esr, regs);
+ do_bad_area(far, esr, regs);
return 0;
}
-static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+static int do_bad(unsigned long far, unsigned int esr, struct pt_regs *regs)
{
return 1; /* "fault" */
}
-static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+static int do_sea(unsigned long far, unsigned int esr, struct pt_regs *regs)
{
const struct fault_info *inf;
- void __user *siaddr;
+ unsigned long siaddr;
inf = esr_to_fault_info(esr);
@@ -633,9 +635,9 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
}
if (esr & ESR_ELx_FnV)
- siaddr = NULL;
+ siaddr = 0;
else
- siaddr = (void __user *)addr;
+ siaddr = untagged_addr(far);
arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr);
return 0;
@@ -708,11 +710,12 @@ static const struct fault_info fault_info[] = {
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
};
-void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs)
{
const struct fault_info *inf = esr_to_fault_info(esr);
+ unsigned long addr = untagged_addr(far);
- if (!inf->fn(addr, esr, regs))
+ if (!inf->fn(far, esr, regs))
return;
if (!user_mode(regs)) {
@@ -721,8 +724,7 @@ void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
show_pte(addr);
}
- arm64_notify_die(inf->name, regs,
- inf->sig, inf->code, (void __user *)addr, esr);
+ arm64_notify_die(inf->name, regs, inf->sig, inf->code, addr, esr);
}
NOKPROBE_SYMBOL(do_mem_abort);
@@ -735,8 +737,8 @@ NOKPROBE_SYMBOL(do_el0_irq_bp_hardening);
void do_sp_pc_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
{
- arm64_notify_die("SP/PC alignment exception", regs,
- SIGBUS, BUS_ADRALN, (void __user *)addr, esr);
+ arm64_notify_die("SP/PC alignment exception", regs, SIGBUS, BUS_ADRALN,
+ addr, esr);
}
NOKPROBE_SYMBOL(do_sp_pc_abort);
@@ -862,8 +864,7 @@ void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
arm64_apply_bp_hardening();
if (inf->fn(addr_if_watchpoint, esr, regs)) {
- arm64_notify_die(inf->name, regs,
- inf->sig, inf->code, (void __user *)pc, esr);
+ arm64_notify_die(inf->name, regs, inf->sig, inf->code, pc, esr);
}
debug_exception_exit(regs);
diff --git a/arch/x86/kernel/signal_compat.c b/arch/x86/kernel/signal_compat.c
index 6b99f0c8a068..9a40c47f66c2 100644
--- a/arch/x86/kernel/signal_compat.c
+++ b/arch/x86/kernel/signal_compat.c
@@ -121,8 +121,8 @@ static inline void signal_compat_build_tests(void)
#endif
CHECK_CSI_OFFSET(_sigfault);
- CHECK_CSI_SIZE (_sigfault, 8*sizeof(int));
- CHECK_SI_SIZE (_sigfault, 16*sizeof(int));
+ CHECK_CSI_SIZE (_sigfault, 10*sizeof(int));
+ CHECK_SI_SIZE (_sigfault, 20*sizeof(int));
BUILD_BUG_ON(offsetof(siginfo_t, si_addr) != 0x10);
BUILD_BUG_ON(offsetof(compat_siginfo_t, si_addr) != 0x0C);
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 758f95aff5b5..cf9ff9d4e816 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -234,6 +234,8 @@ typedef struct compat_siginfo {
compat_uptr_t _pad[6];
};
u64 _xflags;
+ compat_uptr_t _addr_tag_bits;
+ compat_uptr_t _addr_tag_bits_mask;
} _sigfault;
/* SIGPOLL */
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 9b7fef0c559d..e12f211f5c12 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -480,4 +480,20 @@ struct seq_file;
extern void render_sigset_t(struct seq_file *, const char *, sigset_t *);
#endif
+#ifndef arch_addr_tag_bits_mask
+/*
+ * Given a signal and si_code which correspond to the _sigfault union member,
+ * if tag bits are present in the fault address which must appear in
+ * si_addr_tag_bits instead of si_addr, this hook must return a bitmask where 1
+ * corresponds to bits appearing in si_addr_tag_bits and 0 corresponds to bits
+ * appearing in si_addr. The value returned by this function will also be
+ * available in si_addr_tag_bits_mask.
+ */
+static inline unsigned long arch_addr_tag_bits_mask(unsigned long sig,
+ unsigned long si_code)
+{
+ return 0;
+}
+#endif
+
#endif /* _LINUX_SIGNAL_H */
diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index fc12513f5d09..07fac6d409f5 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -94,6 +94,8 @@ union __sifields {
void *_pad[6];
};
u64 _xflags;
+ unsigned long _addr_tag_bits;
+ unsigned long _addr_tag_bits_mask;
} _sigfault;
/* SIGPOLL */
@@ -156,6 +158,8 @@ typedef struct siginfo {
#define si_addr_lsb _sifields._sigfault._addr_lsb
/* si_xflags is only valid if 0 < si_code < SI_KERNEL */
#define si_xflags _sifields._sigfault._xflags
+#define si_addr_tag_bits _sifields._sigfault._addr_tag_bits
+#define si_addr_tag_bits_mask _sifields._sigfault._addr_tag_bits_mask
#define si_lower _sifields._sigfault._addr_bnd._lower
#define si_upper _sifields._sigfault._addr_bnd._upper
#define si_pkey _sifields._sigfault._addr_pkey._pkey
@@ -296,6 +300,12 @@ typedef struct siginfo {
#define EMT_TAGOVF 1 /* tag overflow */
#define NSIGEMT 1
+/*
+ * SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT si_xflags
+ */
+#define SIXFLAG_ADDR_TAG_BITS 1
+/* si_addr_tag_bits{,_mask} fields valid */
+
/*
* sigevent definitions
*
diff --git a/kernel/signal.c b/kernel/signal.c
index 5c715a01942f..f41b40c9caf1 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1652,11 +1652,16 @@ void force_sigsegv(int sig)
static void set_sigfault_common_fields(struct kernel_siginfo *info, int sig,
int code, void __user *addr)
{
+ unsigned long addr_long = (unsigned long)addr;
+ unsigned long tag_bits_mask = arch_addr_tag_bits_mask(sig, code);
+
info->si_signo = sig;
info->si_errno = 0;
info->si_code = code;
- info->si_addr = addr;
- info->si_xflags = 0;
+ info->si_addr = (void __user *)(addr_long & ~tag_bits_mask);
+ info->si_xflags = SIXFLAG_ADDR_TAG_BITS;
+ info->si_addr_tag_bits = addr_long & tag_bits_mask;
+ info->si_addr_tag_bits_mask = tag_bits_mask;
}
int force_sig_fault_to_task(int sig, int code, void __user *addr
@@ -3271,6 +3276,13 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
to->si_trapno = from->si_trapno;
#endif
to->si_xflags = from->si_xflags;
+ /*
+ * These assignments involve a truncation, but as with si_addr
+ * they will be derived from a 32-bit fault address so we
+ * should not expect any truncation in practice.
+ */
+ to->si_addr_tag_bits = from->si_addr_tag_bits;
+ to->si_addr_tag_bits_mask = from->si_addr_tag_bits_mask;
}
switch (layout) {
@@ -3347,6 +3359,8 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
to->si_trapno = from->si_trapno;
#endif
to->si_xflags = from->si_xflags;
+ to->si_addr_tag_bits = from->si_addr_tag_bits;
+ to->si_addr_tag_bits_mask = from->si_addr_tag_bits_mask;
}
switch (layout) {
--
2.28.0.1011.ga647a8990f-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread