diff mbox series

[v6,2/3] arm64: Move fault address and fault code into kernel_siginfo

Message ID 20200521022943.195898-3-pcc@google.com (mailing list archive)
State New, archived
Headers show
Series arm64: Expose FAR_EL1 tag bits in sigcontext | expand

Commit Message

Peter Collingbourne May 21, 2020, 2:29 a.m. UTC
Previously this data was being stored in thread_struct, which is
error-prone because the data is associated with the signal itself
and not the task, and as a result it could get out of sync with the
signal that is currently being delivered. To avoid this problem,
move the fields to kernel_siginfo using the newly-introduced generic
support for doing so.

The new fields store the raw FAR_EL1 and ESR_EL1 values instead of
the cooked ones as we were doing before. For FAR_EL1 this will be
necessary in order to expose the high bits of FAR_EL1 in sigcontext
in an upcoming change. Do the same for ESR_EL1 for consistency and
to make the code less error-prone.

While here, fix an apparent compat bug where, when delivering a
SIGILL signal in response to an invalid syscall, the syscall number
was being interpreted as an ESR_EL1 value and translated into an
FSR before being stored in sigcontext.error_code, rather than being
stored in error_code directly, as was the intention (and the behaviour
of the code in arch/arm). This is achieved by moving the error_code
translation early so that the syscall number can avoid it.

Signed-off-by: Peter Collingbourne <pcc@google.com>
---
 arch/arm64/include/asm/exception.h |   2 +-
 arch/arm64/include/asm/processor.h |   2 -
 arch/arm64/include/asm/signal.h    |  16 ++++
 arch/arm64/include/asm/traps.h     |   8 +-
 arch/arm64/kernel/debug-monitors.c |   4 +-
 arch/arm64/kernel/entry-common.c   |   2 -
 arch/arm64/kernel/probes/uprobes.c |  18 ++---
 arch/arm64/kernel/ptrace.c         |   2 +-
 arch/arm64/kernel/signal.c         |  17 +++--
 arch/arm64/kernel/signal32.c       |  15 ++--
 arch/arm64/kernel/sys_compat.c     |   9 ++-
 arch/arm64/kernel/traps.c          | 117 ++++++++++++++++++++++++-----
 arch/arm64/mm/fault.c              | 111 +++++++--------------------
 13 files changed, 183 insertions(+), 140 deletions(-)
 create mode 100644 arch/arm64/include/asm/signal.h

Comments

kernel test robot May 21, 2020, 1:34 p.m. UTC | #1
Hi Peter,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on arm-perf/for-next/perf]
[also build test WARNING on linus/master v5.7-rc6]
[cannot apply to arm64/for-next/core next-20200519]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Peter-Collingbourne/arm64-Expose-FAR_EL1-tag-bits-in-sigcontext/20200521-103345
base:   https://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git for-next/perf
config: arm64-randconfig-r005-20200520 (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project 3393cc4cebf9969db94dc424b7a2b6195589c33b)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm64 cross compiling tool for clang build
        # apt-get install binutils-aarch64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> arch/arm64/kernel/traps.c:283:55: warning: format specifies type 'unsigned int' but the argument has type 'unsigned long' [-Wformat]
WARN(1, "ESR 0x%x is not DABT or IABT from EL0n", esr);
~~                                  ^~~
%lx
include/asm-generic/bug.h:124:29: note: expanded from macro 'WARN'
__WARN_printf(TAINT_WARN, format);                                                                   ^~~~~~
include/asm-generic/bug.h:92:17: note: expanded from macro '__WARN_printf'
__warn_printk(arg);                                                                      ^~~
arch/arm64/kernel/traps.c:826:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_UNKNOWN]            = "Unknown/Uncategorized",
^~~~~~~~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:827:22: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_WFx]                = "WFI/WFE",
^~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:828:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_CP15_32]            = "CP15 MCR/MRC",
^~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:829:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_CP15_64]            = "CP15 MCRR/MRRC",
^~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:830:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_CP14_MR]            = "CP14 MCR/MRC",
^~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:831:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_CP14_LS]            = "CP14 LDC/STC",
^~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:832:27: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_FP_ASIMD]           = "ASIMD",
^~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:833:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_CP10_ID]            = "CP10 MRC/VMRS",
^~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:834:22: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_PAC]                = "PAC",
^~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:835:26: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_CP14_64]            = "CP14 MCRR/MRRC",
^~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:836:22: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_ILL]                = "PSTATE.IL",
^~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:837:24: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_SVC32]              = "SVC (AArch32)",
^~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:838:24: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_HVC32]              = "HVC (AArch32)",
^~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:839:24: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_SMC32]              = "SMC (AArch32)",
^~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:840:24: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[ESR_ELx_EC_SVC64]              = "SVC (AArch64)",
^~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:825:28: note: previous initialization is here
[0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
^~~~~~~~~~~~~~~~~
arch/arm64/kernel/traps.c:841:24: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]

vim +283 arch/arm64/kernel/traps.c

   236	
   237	static unsigned long esr_to_error_code(unsigned long esr, unsigned long far)
   238	{
   239		/*
   240		 * If the faulting address is in the kernel, we must sanitize the ESR.
   241		 * From userspace's point of view, kernel-only mappings don't exist
   242		 * at all, so we report them as level 0 translation faults.
   243		 * (This is not quite the way that "no mapping there at all" behaves:
   244		 * an alignment fault not caused by the memory type would take
   245		 * precedence over translation fault for a real access to empty
   246		 * space. Unfortunately we can't easily distinguish "alignment fault
   247		 * not caused by memory type" from "alignment fault caused by memory
   248		 * type", so we ignore this wrinkle and just return the translation
   249		 * fault.)
   250		 */
   251		if (!is_ttbr0_addr(untagged_addr(far))) {
   252			switch (ESR_ELx_EC(esr)) {
   253			case ESR_ELx_EC_DABT_LOW:
   254				/*
   255				 * These bits provide only information about the
   256				 * faulting instruction, which userspace knows already.
   257				 * We explicitly clear bits which are architecturally
   258				 * RES0 in case they are given meanings in future.
   259				 * We always report the ESR as if the fault was taken
   260				 * to EL1 and so ISV and the bits in ISS[23:14] are
   261				 * clear. (In fact it always will be a fault to EL1.)
   262				 */
   263				esr &= ESR_ELx_EC_MASK | ESR_ELx_IL |
   264					ESR_ELx_CM | ESR_ELx_WNR;
   265				esr |= ESR_ELx_FSC_FAULT;
   266				break;
   267			case ESR_ELx_EC_IABT_LOW:
   268				/*
   269				 * Claim a level 0 translation fault.
   270				 * All other bits are architecturally RES0 for faults
   271				 * reported with that DFSC value, so we clear them.
   272				 */
   273				esr &= ESR_ELx_EC_MASK | ESR_ELx_IL;
   274				esr |= ESR_ELx_FSC_FAULT;
   275				break;
   276			default:
   277				/*
   278				 * This should never happen (entry.S only brings us
   279				 * into this code for insn and data aborts from a lower
   280				 * exception level). Fail safe by not providing an ESR
   281				 * context record at all.
   282				 */
 > 283				WARN(1, "ESR 0x%x is not DABT or IABT from EL0\n", esr);
   284				esr = 0;
   285				break;
   286			}
   287		}
   288	
   289		if (is_compat_task()) {
   290			/* Use the compat FSR WnR */
   291			return !!(esr & ESR_ELx_WNR) << FSR_WRITE_SHIFT;
   292		}
   293	
   294		return esr;
   295	}
   296	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h
index 7a6e81ca23a8..90e772d9b2cd 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);
 asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
 void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 240fe5e5b720..b326bfbcea62 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -144,8 +144,6 @@  struct thread_struct {
 	void			*sve_state;	/* SVE registers, if any */
 	unsigned int		sve_vl;		/* SVE vector length */
 	unsigned int		sve_vl_onexec;	/* SVE vl after next exec */
-	unsigned long		fault_address;	/* fault info */
-	unsigned long		fault_code;	/* ESR_EL1 value */
 	struct debug_info	debug;		/* debugging */
 #ifdef CONFIG_ARM64_PTR_AUTH
 	struct ptrauth_keys_user	keys_user;
diff --git a/arch/arm64/include/asm/signal.h b/arch/arm64/include/asm/signal.h
new file mode 100644
index 000000000000..f5c001b0a125
--- /dev/null
+++ b/arch/arm64/include/asm/signal.h
@@ -0,0 +1,16 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_ARM64_SIGNAL_H
+#define _ASM_ARM64_SIGNAL_H
+
+#include <uapi/asm/signal.h>
+
+#define __ARCH_HAS_PRIVATE_SIGINFO
+struct arch_private_siginfo {
+	/* FAR_EL1 value */
+	unsigned long fault_address;
+
+	/* Sanitized ESR_EL1 value, or FSR/syscall number in compat mode */
+	unsigned long error_code;
+};
+
+#endif
diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
index cee5928e1b7d..5ed5be5347e6 100644
--- a/arch/arm64/include/asm/traps.h
+++ b/arch/arm64/include/asm/traps.h
@@ -26,8 +26,12 @@  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_fault(int signo, int code, void __user *addr,
+			   unsigned long far, unsigned long esr,
+			   const char *str);
+void arm64_force_sig_mceerr(int code, void __user *addr, short lsb,
+			    unsigned long far, unsigned long esr,
+			    const char *str);
 void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr, const char *str);
 
 /*
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 48222a4760c2..498e6393b2ca 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -232,8 +232,8 @@  static void send_user_sigtrap(int si_code)
 		local_irq_enable();
 
 	arm64_force_sig_fault(SIGTRAP, si_code,
-			     (void __user *)instruction_pointer(regs),
-			     "User debug trap");
+			      (void __user *)instruction_pointer(regs), 0, 0,
+			      "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 c839b5bf1904..045b4f518836 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/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c
index a412d8edbcd2..5bbcd2e813f0 100644
--- a/arch/arm64/kernel/probes/uprobes.c
+++ b/arch/arm64/kernel/probes/uprobes.c
@@ -9,8 +9,6 @@ 
 
 #include "decode-insn.h"
 
-#define UPROBE_INV_FAULT_CODE	UINT_MAX
-
 void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
 		void *src, unsigned long len)
 {
@@ -63,9 +61,6 @@  int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
 	struct uprobe_task *utask = current->utask;
 
-	/* Initialize with an invalid fault code to detect if ol insn trapped */
-	current->thread.fault_code = UPROBE_INV_FAULT_CODE;
-
 	/* Instruction points to execute ol */
 	instruction_pointer_set(regs, utask->xol_vaddr);
 
@@ -78,7 +73,7 @@  int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
 	struct uprobe_task *utask = current->utask;
 
-	WARN_ON_ONCE(current->thread.fault_code != UPROBE_INV_FAULT_CODE);
+	WARN_ON_ONCE(arch_uprobe_xol_was_trapped(current));
 
 	/* Instruction points to execute next to breakpoint address */
 	instruction_pointer_set(regs, utask->vaddr + 4);
@@ -89,13 +84,16 @@  int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 }
 bool arch_uprobe_xol_was_trapped(struct task_struct *t)
 {
+	struct sigqueue *q;
+
 	/*
 	 * Between arch_uprobe_pre_xol and arch_uprobe_post_xol, if an xol
-	 * insn itself is trapped, then detect the case with the help of
-	 * invalid fault code which is being set in arch_uprobe_pre_xol
+	 * insn itself is trapped, then detect the case by checking for
+	 * non-zero esr_el1 in the task's pending signals.
 	 */
-	if (t->thread.fault_code != UPROBE_INV_FAULT_CODE)
-		return true;
+	list_for_each_entry (q, &t->pending.list, list)
+		if (q->info.arch.error_code)
+			return true;
 
 	return false;
 }
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index b3d3005d9515..51bb8bcaf24b 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -198,7 +198,7 @@  static void ptrace_hbptriggered(struct perf_event *bp,
 	}
 #endif
 	arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT,
-			      (void __user *)(bkpt->trigger),
+			      (void __user *)(bkpt->trigger), 0, 0,
 			      desc);
 }
 
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 339882db5a91..10d7e9832a89 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -566,6 +566,7 @@  SYSCALL_DEFINE0(rt_sigreturn)
  *	of the task.
  */
 static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
+				 struct kernel_siginfo *info,
 				 bool add_all)
 {
 	int err;
@@ -576,7 +577,7 @@  static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
 		return err;
 
 	/* fault information, if valid */
-	if (add_all || current->thread.fault_code) {
+	if (add_all || info->arch.error_code) {
 		err = sigframe_alloc(user, &user->esr_offset,
 				     sizeof(struct esr_context));
 		if (err)
@@ -605,7 +606,8 @@  static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
 }
 
 static int setup_sigframe(struct rt_sigframe_user_layout *user,
-			  struct pt_regs *regs, sigset_t *set)
+			  struct pt_regs *regs, sigset_t *set,
+			  struct kernel_siginfo *info)
 {
 	int i, err = 0;
 	struct rt_sigframe __user *sf = user->sigframe;
@@ -621,7 +623,8 @@  static int setup_sigframe(struct rt_sigframe_user_layout *user,
 	__put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err);
 	__put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err);
 
-	__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
+	__put_user_error(untagged_addr(info->arch.fault_address),
+			 &sf->uc.uc_mcontext.fault_address, err);
 
 	err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
 
@@ -638,7 +641,7 @@  static int setup_sigframe(struct rt_sigframe_user_layout *user,
 
 		__put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err);
 		__put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err);
-		__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
+		__put_user_error(info->arch.error_code, &esr_ctx->esr, err);
 	}
 
 	/* Scalable Vector Extension state, if present */
@@ -701,7 +704,7 @@  static int get_sigframe(struct rt_sigframe_user_layout *user,
 	int err;
 
 	init_user_layout(user);
-	err = setup_sigframe_layout(user, false);
+	err = setup_sigframe_layout(user, &ksig->info, false);
 	if (err)
 		return err;
 
@@ -758,7 +761,7 @@  static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
 	__put_user_error(NULL, &frame->uc.uc_link, err);
 
 	err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
-	err |= setup_sigframe(&user, regs, set);
+	err |= setup_sigframe(&user, regs, set, &ksig->info);
 	if (err == 0) {
 		setup_return(regs, &ksig->ka, &user, usig);
 		if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
@@ -958,7 +961,7 @@  void __init minsigstksz_setup(void)
 	 * If this fails, SIGFRAME_MAXSZ needs to be enlarged.  It won't
 	 * be big enough, but it's our best guess:
 	 */
-	if (WARN_ON(setup_sigframe_layout(&user, true)))
+	if (WARN_ON(setup_sigframe_layout(&user, 0, true)))
 		return;
 
 	signal_minsigstksz = sigframe_size(&user) +
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
index 82feca6f7052..b302689b6651 100644
--- a/arch/arm64/kernel/signal32.c
+++ b/arch/arm64/kernel/signal32.c
@@ -37,8 +37,6 @@  struct compat_vfp_sigframe {
 #define VFP_MAGIC		0x56465001
 #define VFP_STORAGE_SIZE	sizeof(struct compat_vfp_sigframe)
 
-#define FSR_WRITE_SHIFT		(11)
-
 struct compat_aux_sigframe {
 	struct compat_vfp_sigframe	vfp;
 
@@ -384,7 +382,8 @@  static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka,
 }
 
 static int compat_setup_sigframe(struct compat_sigframe __user *sf,
-				 struct pt_regs *regs, sigset_t *set)
+				 struct pt_regs *regs, sigset_t *set,
+				 struct kernel_siginfo *info)
 {
 	struct compat_aux_sigframe __user *aux;
 	unsigned long psr = pstate_to_compat_psr(regs->pstate);
@@ -409,10 +408,8 @@  static int compat_setup_sigframe(struct compat_sigframe __user *sf,
 	__put_user_error(psr, &sf->uc.uc_mcontext.arm_cpsr, err);
 
 	__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err);
-	/* set the compat FSR WnR */
-	__put_user_error(!!(current->thread.fault_code & ESR_ELx_WNR) <<
-			 FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err);
-	__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
+	__put_user_error(info->arch.error_code, &sf->uc.uc_mcontext.error_code, err);
+	__put_user_error(info->arch.fault_address, &sf->uc.uc_mcontext.fault_address, err);
 	__put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
 
 	err |= put_sigset_t(&sf->uc.uc_sigmask, set);
@@ -447,7 +444,7 @@  int compat_setup_rt_frame(int usig, struct ksignal *ksig,
 
 	err |= __compat_save_altstack(&frame->sig.uc.uc_stack, regs->compat_sp);
 
-	err |= compat_setup_sigframe(&frame->sig, regs, set);
+	err |= compat_setup_sigframe(&frame->sig, regs, set, &ksig->info);
 
 	if (err == 0) {
 		compat_setup_return(regs, &ksig->ka, frame->sig.retcode, frame, usig);
@@ -471,7 +468,7 @@  int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set,
 
 	__put_user_error(0x5ac3c35a, &frame->uc.uc_flags, err);
 
-	err |= compat_setup_sigframe(frame, regs, set);
+	err |= compat_setup_sigframe(frame, regs, set, &ksig->info);
 	if (err == 0)
 		compat_setup_return(regs, &ksig->ka, frame->retcode, frame, usig);
 
diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c
index 3c18c2454089..d7a0b93a8d9f 100644
--- a/arch/arm64/kernel/sys_compat.c
+++ b/arch/arm64/kernel/sys_compat.c
@@ -69,6 +69,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;
+	struct kernel_siginfo info;
 
 	switch (scno) {
 	/*
@@ -114,7 +115,11 @@  long compat_arm_syscall(struct pt_regs *regs, int scno)
 	addr  = (void __user *)instruction_pointer(regs) -
 		(compat_thumb_mode(regs) ? 2 : 4);
 
-	arm64_notify_die("Oops - bad compat syscall(2)", regs,
-			 SIGILL, ILL_ILLTRP, addr, scno);
+	clear_siginfo(&info);
+	info.si_signo = SIGILL;
+	info.si_code = ILL_ILLTRP;
+	info.si_addr = addr;
+	info.arch.error_code = scno;
+	force_sig_info(&info);
 	return 0;
 }
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index cf402be5c573..4545fe067ea9 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -44,6 +44,8 @@ 
 #include <asm/system_misc.h>
 #include <asm/sysreg.h>
 
+#define FSR_WRITE_SHIFT		(11)
+
 static const char *handler[]= {
 	"Synchronous Abort",
 	"IRQ",
@@ -209,12 +211,11 @@  void die(const char *str, struct pt_regs *regs, int err)
 		do_exit(SIGSEGV);
 }
 
-static void arm64_show_signal(int signo, const char *str)
+static void arm64_show_signal(int signo, unsigned long esr, const char *str)
 {
 	static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL,
 				      DEFAULT_RATELIMIT_BURST);
 	struct task_struct *tsk = current;
-	unsigned int esr = tsk->thread.fault_code;
 	struct pt_regs *regs = task_pt_regs(tsk);
 
 	/* Leave if the signal won't be shown */
@@ -225,7 +226,7 @@  static void arm64_show_signal(int signo, const char *str)
 
 	pr_info("%s[%d]: unhandled exception: ", tsk->comm, task_pid_nr(tsk));
 	if (esr)
-		pr_cont("%s, ESR 0x%08x, ", esr_get_class_string(esr), esr);
+		pr_cont("%s, ESR 0x%08lx, ", esr_get_class_string(esr), esr);
 
 	pr_cont("%s", str);
 	print_vma_addr(KERN_CONT " in ", regs->pc);
@@ -233,42 +234,121 @@  static void arm64_show_signal(int signo, const char *str)
 	__show_regs(regs);
 }
 
+static unsigned long esr_to_error_code(unsigned long esr, unsigned long far)
+{
+	/*
+	 * If the faulting address is in the kernel, we must sanitize the ESR.
+	 * From userspace's point of view, kernel-only mappings don't exist
+	 * at all, so we report them as level 0 translation faults.
+	 * (This is not quite the way that "no mapping there at all" behaves:
+	 * an alignment fault not caused by the memory type would take
+	 * precedence over translation fault for a real access to empty
+	 * space. Unfortunately we can't easily distinguish "alignment fault
+	 * not caused by memory type" from "alignment fault caused by memory
+	 * type", so we ignore this wrinkle and just return the translation
+	 * fault.)
+	 */
+	if (!is_ttbr0_addr(untagged_addr(far))) {
+		switch (ESR_ELx_EC(esr)) {
+		case ESR_ELx_EC_DABT_LOW:
+			/*
+			 * These bits provide only information about the
+			 * faulting instruction, which userspace knows already.
+			 * We explicitly clear bits which are architecturally
+			 * RES0 in case they are given meanings in future.
+			 * We always report the ESR as if the fault was taken
+			 * to EL1 and so ISV and the bits in ISS[23:14] are
+			 * clear. (In fact it always will be a fault to EL1.)
+			 */
+			esr &= ESR_ELx_EC_MASK | ESR_ELx_IL |
+				ESR_ELx_CM | ESR_ELx_WNR;
+			esr |= ESR_ELx_FSC_FAULT;
+			break;
+		case ESR_ELx_EC_IABT_LOW:
+			/*
+			 * Claim a level 0 translation fault.
+			 * All other bits are architecturally RES0 for faults
+			 * reported with that DFSC value, so we clear them.
+			 */
+			esr &= ESR_ELx_EC_MASK | ESR_ELx_IL;
+			esr |= ESR_ELx_FSC_FAULT;
+			break;
+		default:
+			/*
+			 * This should never happen (entry.S only brings us
+			 * into this code for insn and data aborts from a lower
+			 * exception level). Fail safe by not providing an ESR
+			 * context record at all.
+			 */
+			WARN(1, "ESR 0x%x is not DABT or IABT from EL0\n", esr);
+			esr = 0;
+			break;
+		}
+	}
+
+	if (is_compat_task()) {
+		/* Use the compat FSR WnR */
+		return !!(esr & ESR_ELx_WNR) << FSR_WRITE_SHIFT;
+	}
+
+	return esr;
+}
+
 void arm64_force_sig_fault(int signo, int code, void __user *addr,
+			   unsigned long far, unsigned long esr,
 			   const char *str)
 {
-	arm64_show_signal(signo, str);
-	if (signo == SIGKILL)
+	arm64_show_signal(signo, esr, str);
+	if (signo == SIGKILL) {
 		force_sig(SIGKILL);
-	else
-		force_sig_fault(signo, code, addr);
+	} else {
+		struct kernel_siginfo info;
+		clear_siginfo(&info);
+		info.si_signo = signo;
+		info.si_errno = 0;
+		info.si_code = code;
+		info.si_addr = addr;
+		info.arch.fault_address = far;
+		info.arch.error_code = esr_to_error_code(esr, far);
+		force_sig_info(&info);
+	}
 }
 
 void arm64_force_sig_mceerr(int code, void __user *addr, short lsb,
+			    unsigned long far, unsigned long esr,
 			    const char *str)
 {
-	arm64_show_signal(SIGBUS, str);
-	force_sig_mceerr(code, addr, lsb);
+	struct kernel_siginfo info;
+
+	arm64_show_signal(SIGBUS, esr, str);
+
+	clear_siginfo(&info);
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = code;
+	info.si_addr = addr;
+	info.si_addr_lsb = lsb;
+	info.arch.fault_address = far;
+	info.arch.error_code = esr_to_error_code(esr, far);
+	force_sig_info(&info);
 }
 
 void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr,
 				       const char *str)
 {
-	arm64_show_signal(SIGTRAP, str);
+	arm64_show_signal(SIGTRAP, 0, str);
 	force_sig_ptrace_errno_trap(errno, addr);
 }
 
 void arm64_notify_die(const char *str, struct pt_regs *regs,
 		      int signo, int sicode, void __user *addr,
-		      int err)
+		      int esr)
 {
 	if (user_mode(regs)) {
 		WARN_ON(regs != current_pt_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, addr, 0, esr, str);
 	} else {
-		die(str, regs, err);
+		die(str, regs, esr);
 	}
 }
 
@@ -813,10 +893,7 @@  void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
 {
 	void __user *pc = (void __user *)instruction_pointer(regs);
 
-	current->thread.fault_address = 0;
-	current->thread.fault_code = esr;
-
-	arm64_force_sig_fault(SIGILL, ILL_ILLOPC, pc,
+	arm64_force_sig_fault(SIGILL, ILL_ILLOPC, pc, 0, esr,
 			      "Bad EL0 synchronous exception");
 }
 
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index c9cedc0432d2..a7bada1392b3 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -41,7 +41,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;
@@ -320,75 +320,19 @@  static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 	die_kernel_fault(msg, addr, esr, regs);
 }
 
-static void set_thread_esr(unsigned long address, unsigned int esr)
+static void do_bad_area(unsigned long far, unsigned int esr,
+			struct pt_regs *regs)
 {
-	current->thread.fault_address = address;
+	unsigned long addr = untagged_addr(far);
 
-	/*
-	 * If the faulting address is in the kernel, we must sanitize the ESR.
-	 * From userspace's point of view, kernel-only mappings don't exist
-	 * at all, so we report them as level 0 translation faults.
-	 * (This is not quite the way that "no mapping there at all" behaves:
-	 * an alignment fault not caused by the memory type would take
-	 * precedence over translation fault for a real access to empty
-	 * space. Unfortunately we can't easily distinguish "alignment fault
-	 * not caused by memory type" from "alignment fault caused by memory
-	 * type", so we ignore this wrinkle and just return the translation
-	 * fault.)
-	 */
-	if (!is_ttbr0_addr(current->thread.fault_address)) {
-		switch (ESR_ELx_EC(esr)) {
-		case ESR_ELx_EC_DABT_LOW:
-			/*
-			 * These bits provide only information about the
-			 * faulting instruction, which userspace knows already.
-			 * We explicitly clear bits which are architecturally
-			 * RES0 in case they are given meanings in future.
-			 * We always report the ESR as if the fault was taken
-			 * to EL1 and so ISV and the bits in ISS[23:14] are
-			 * clear. (In fact it always will be a fault to EL1.)
-			 */
-			esr &= ESR_ELx_EC_MASK | ESR_ELx_IL |
-				ESR_ELx_CM | ESR_ELx_WNR;
-			esr |= ESR_ELx_FSC_FAULT;
-			break;
-		case ESR_ELx_EC_IABT_LOW:
-			/*
-			 * Claim a level 0 translation fault.
-			 * All other bits are architecturally RES0 for faults
-			 * reported with that DFSC value, so we clear them.
-			 */
-			esr &= ESR_ELx_EC_MASK | ESR_ELx_IL;
-			esr |= ESR_ELx_FSC_FAULT;
-			break;
-		default:
-			/*
-			 * This should never happen (entry.S only brings us
-			 * into this code for insn and data aborts from a lower
-			 * exception level). Fail safe by not providing an ESR
-			 * context record at all.
-			 */
-			WARN(1, "ESR 0x%x is not DABT or IABT from EL0\n", esr);
-			esr = 0;
-			break;
-		}
-	}
-
-	current->thread.fault_code = esr;
-}
-
-static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
-{
 	/*
 	 * If we are in kernel mode at this point, we have no context to
 	 * handle this fault with.
 	 */
 	if (user_mode(regs)) {
 		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);
+				      far, esr, inf->name);
 	} else {
 		__do_kernel_fault(addr, esr, regs);
 	}
@@ -439,7 +383,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;
@@ -447,6 +391,7 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 	vm_fault_t fault, major = 0;
 	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;
@@ -570,13 +515,12 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 	}
 
 	inf = esr_to_fault_info(esr);
-	set_thread_esr(addr, esr);
 	if (fault & VM_FAULT_SIGBUS) {
 		/*
 		 * We had some memory, but were unable to successfully fix up
 		 * this page fault.
 		 */
-		arm64_force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)addr,
+		arm64_force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)addr, far, esr,
 				      inf->name);
 	} else if (fault & (VM_FAULT_HWPOISON_LARGE | VM_FAULT_HWPOISON)) {
 		unsigned int lsb;
@@ -586,7 +530,7 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 			lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
 
 		arm64_force_sig_mceerr(BUS_MCEERR_AR, (void __user *)addr, lsb,
-				       inf->name);
+				       far, esr, inf->name);
 	} else {
 		/*
 		 * Something tried to access memory that isn't in our memory
@@ -594,7 +538,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,
+				      (void __user *)addr, far, esr,
 				      inf->name);
 	}
 
@@ -605,30 +549,32 @@  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;
@@ -644,7 +590,7 @@  static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 	if (esr & ESR_ELx_FnV)
 		siaddr = NULL;
 	else
-		siaddr  = (void __user *)addr;
+		siaddr  = (void __user *)untagged_addr(far);
 	arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr);
 
 	return 0;
@@ -717,11 +663,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)) {
@@ -730,8 +677,8 @@  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,
+			 (void __user *)addr, esr);
 }
 NOKPROBE_SYMBOL(do_mem_abort);
 
@@ -744,8 +691,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,
+			 (void __user *)addr, esr);
 }
 NOKPROBE_SYMBOL(do_sp_pc_abort);
 
@@ -871,8 +818,8 @@  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,
+				 (void __user *)pc, esr);
 	}
 
 	debug_exception_exit(regs);