linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Peter Collingbourne <pcc@google.com>
To: Catalin Marinas <catalin.marinas@arm.com>,
	Evgenii Stepanov <eugenis@google.com>,
	Kostya Serebryany <kcc@google.com>,
	Vincenzo Frascino <vincenzo.frascino@arm.com>,
	 Dave Martin <Dave.Martin@arm.com>, Will Deacon <will@kernel.org>,
	Oleg Nesterov <oleg@redhat.com>,
	 "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Andrey Konovalov <andreyknvl@google.com>,
	Kevin Brodsky <kevin.brodsky@arm.com>,
	Peter Collingbourne <pcc@google.com>,
	Linux ARM <linux-arm-kernel@lists.infradead.org>,
	Richard Henderson <rth@twiddle.net>
Subject: [PATCH v6 2/3] arm64: Move fault address and fault code into kernel_siginfo
Date: Wed, 20 May 2020 19:29:42 -0700	[thread overview]
Message-ID: <20200521022943.195898-3-pcc@google.com> (raw)
In-Reply-To: <20200521022943.195898-1-pcc@google.com>

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

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);
-- 
2.26.2.761.g0e0b3e54be-goog


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  parent reply	other threads:[~2020-05-21  2:30 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-12 17:17 [PATCH] arm64: Expose original FAR_EL1 value in sigcontext Peter Collingbourne
2020-03-25 13:10 ` Catalin Marinas
2020-03-25 17:41   ` Peter Collingbourne
2020-03-25 17:40 ` [PATCH v2] " Peter Collingbourne
2020-03-26 16:45   ` Catalin Marinas
2020-03-27  7:56     ` Will Deacon
2020-03-27 11:39       ` Catalin Marinas
2020-03-27 19:26         ` Peter Collingbourne
2020-03-27 19:19   ` [PATCH v3] " Peter Collingbourne
2020-04-22 14:25     ` Catalin Marinas
2020-04-29 21:08     ` Will Deacon
2020-04-29 21:42       ` Peter Collingbourne
2020-05-04 17:03         ` Will Deacon
2020-05-07 17:57           ` [PATCH v4] arm64: Expose FAR_EL1 tag bits " Peter Collingbourne
2020-05-08  2:01             ` [PATCH v5] " Peter Collingbourne
2020-05-12 16:25               ` Catalin Marinas
2020-05-13 18:09               ` [PATCH v6] " Peter Collingbourne
2020-05-13 20:28                 ` Dave Martin
2020-05-15  0:58                   ` Peter Collingbourne
2020-05-18  9:53                     ` Dave Martin
2020-05-19 22:00                       ` Peter Collingbourne
2020-05-20  8:55                         ` Will Deacon
2020-05-20  9:26                           ` Dave Martin
2020-05-21  2:28                             ` Peter Collingbourne
2020-05-21  2:29                               ` [PATCH v6 0/3] " Peter Collingbourne
2020-05-21  2:29                                 ` [PATCH v6 1/3] signal: Allow architectures to store arch-specific data in kernel_siginfo Peter Collingbourne
2020-05-21  2:29                                 ` Peter Collingbourne [this message]
2020-05-21 13:34                                   ` [PATCH v6 2/3] arm64: Move fault address and fault code into kernel_siginfo kbuild test robot
2020-05-21  2:29                                 ` [PATCH v6 3/3] arm64: Expose FAR_EL1 tag bits in sigcontext Peter Collingbourne
2020-05-21 12:35                               ` [PATCH v6] " Eric W. Biederman
2020-05-21 18:03                                 ` Peter Collingbourne
2020-05-21 19:24                                   ` Eric W. Biederman
2020-05-21 20:48                                     ` Peter Collingbourne
2020-06-08 18:12                                       ` Peter Collingbourne
2020-06-08 18:14                                         ` [PATCH v7] arm64: Expose FAR_EL1 tag bits in siginfo Peter Collingbourne
     [not found]                                           ` <20200623020134.16655-1-pcc@google.com>
     [not found]                                             ` <87sgemrlgc.fsf@x220.int.ebiederm.org>
2020-06-23 14:38                                               ` [PATCH v8] " Dave Martin
2020-06-23 17:47                                                 ` Eric W. Biederman
2020-06-24  0:40                                                   ` Peter Collingbourne
2020-06-24  9:28                                                     ` Dave Martin
2020-06-24 16:51                                                       ` Peter Collingbourne
2020-06-24 17:12                                                         ` Dave Martin
2020-06-24 19:51                                                           ` Peter Collingbourne
2020-07-06 16:41                                                             ` Dave Martin
2020-07-06 19:20                                                               ` Peter Collingbourne
2020-07-07 14:19                                                                 ` Dave Martin
2020-07-07 19:07                                                                   ` Peter Collingbourne
2020-07-08 11:00                                                                     ` Dave Martin
2020-07-08 13:58                                                                       ` Dave Martin
2020-07-08 22:21                                                                         ` Peter Collingbourne
2020-07-13 13:24                                                                           ` Dave Martin
2020-07-13 20:50                                                                             ` Peter Collingbourne
2020-07-14 17:36                                                                               ` Dave Martin
2020-08-18  3:16                                                                                 ` Peter Collingbourne
2020-08-18 13:50                                                                                   ` Dave Martin
2020-06-23 14:57                                             ` Dave Martin
2020-05-26 13:03                                     ` [PATCH v6] arm64: Expose FAR_EL1 tag bits in sigcontext Dave Martin
2020-04-30  9:50       ` [PATCH v3] arm64: Expose original FAR_EL1 value " Catalin Marinas
2020-04-30  9:59         ` Will Deacon
2020-04-30 13:34           ` Catalin Marinas
2020-05-04 10:19     ` Dave Martin
2020-05-07 17:55       ` Peter Collingbourne
2020-05-13 17:27         ` Dave Martin
2020-05-13 18:00           ` Peter Collingbourne

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200521022943.195898-3-pcc@google.com \
    --to=pcc@google.com \
    --cc=Dave.Martin@arm.com \
    --cc=andreyknvl@google.com \
    --cc=catalin.marinas@arm.com \
    --cc=ebiederm@xmission.com \
    --cc=eugenis@google.com \
    --cc=kcc@google.com \
    --cc=kevin.brodsky@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=oleg@redhat.com \
    --cc=rth@twiddle.net \
    --cc=vincenzo.frascino@arm.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).