]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
arm64: split EL0/EL1 UNDEF handlers
authorMark Rutland <mark.rutland@arm.com>
Wed, 11 Oct 2023 10:06:47 +0000 (10:06 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 19 Oct 2023 21:05:38 +0000 (23:05 +0200)
commit 61d64a376ea80f9097e7ea599bcd68671b836dc6 upstream.

In general, exceptions taken from EL1 need to be handled separately from
exceptions taken from EL0, as the logic to handle the two cases can be
significantly divergent, and exceptions taken from EL1 typically have
more stringent requirements on locking and instrumentation.

Subsequent patches will rework the way EL1 UNDEFs are handled in order
to address longstanding soundness issues with instrumentation and RCU.
In preparation for that rework, this patch splits the existing
do_undefinstr() handler into separate do_el0_undef() and do_el1_undef()
handlers.

Prior to this patch, do_undefinstr() was marked with NOKPROBE_SYMBOL(),
preventing instrumentation via kprobes. However, do_undefinstr() invokes
other code which can be instrumented, and:

* For UNDEFINED exceptions taken from EL0, there is no risk of recursion
  within kprobes. Therefore it is safe for do_el0_undef to be
  instrumented with kprobes, and it does not need to be marked with
  NOKPROBE_SYMBOL().

* For UNDEFINED exceptions taken from EL1, either:

  (a) The exception is has been taken when manipulating SSBS; these cases
      are limited and do not occur within code that can be invoked
      recursively via kprobes. Hence, in these cases instrumentation
      with kprobes is benign.

  (b) The exception has been taken for an unknown reason, as other than
      manipulating SSBS we do not expect to take UNDEFINED exceptions
      from EL1. Any handling of these exception is best-effort.

  ... and in either case, marking do_el1_undef() with NOKPROBE_SYMBOL()
  isn't sufficient to prevent recursion via kprobes as functions it
  calls (including die()) are instrumentable via kprobes.

  Hence, it's not worthwhile to mark do_el1_undef() with
  NOKPROBE_SYMBOL(). The same applies to do_el1_bti() and do_el1_fpac(),
  so their NOKPROBE_SYMBOL() annotations are also removed.

Aside from the new instrumentability, there should be no functional
change as a result of this patch.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Joey Gouly <joey.gouly@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20221019144123.612388-3-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/arm64/include/asm/exception.h
arch/arm64/kernel/entry-common.c
arch/arm64/kernel/traps.c

index 7466239adac0bd1f33c72279083e1f148f9fa2f3..515ebe24fd44f7d2dcb648de807e242b00170453 100644 (file)
@@ -58,7 +58,8 @@ asmlinkage void call_on_irq_stack(struct pt_regs *regs,
 asmlinkage void asm_exit_to_user_mode(struct pt_regs *regs);
 
 void do_mem_abort(unsigned long far, unsigned long esr, struct pt_regs *regs);
-void do_undefinstr(struct pt_regs *regs, unsigned long esr);
+void do_el0_undef(struct pt_regs *regs, unsigned long esr);
+void do_el1_undef(struct pt_regs *regs, unsigned long esr);
 void do_el0_bti(struct pt_regs *regs);
 void do_el1_bti(struct pt_regs *regs, unsigned long esr);
 void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr,
index 31bdaccabd298db5ed0ff3d41ea7235ec910f32b..864423297c0067118fe2dca52c3e5eba65201228 100644 (file)
@@ -375,7 +375,7 @@ static void noinstr el1_undef(struct pt_regs *regs, unsigned long esr)
 {
        enter_from_kernel_mode(regs);
        local_daif_inherit(regs);
-       do_undefinstr(regs, esr);
+       do_el1_undef(regs, esr);
        local_daif_mask();
        exit_to_kernel_mode(regs);
 }
@@ -570,7 +570,7 @@ static void noinstr el0_undef(struct pt_regs *regs, unsigned long esr)
 {
        enter_from_user_mode(regs);
        local_daif_restore(DAIF_PROCCTX);
-       do_undefinstr(regs, esr);
+       do_el0_undef(regs, esr);
        exit_to_user_mode(regs);
 }
 
index 2ff0c8ddcacf8fa625c69a0b28cfc13fe2abfc41..2999ccf4c117784b0c50dd64e9f3cac41c7f64aa 100644 (file)
@@ -486,7 +486,7 @@ void arm64_notify_segfault(unsigned long addr)
        force_signal_inject(SIGSEGV, code, addr, 0);
 }
 
-void do_undefinstr(struct pt_regs *regs, unsigned long esr)
+void do_el0_undef(struct pt_regs *regs, unsigned long esr)
 {
        /* check for AArch32 breakpoint instructions */
        if (!aarch32_break_handler(regs))
@@ -495,12 +495,16 @@ void do_undefinstr(struct pt_regs *regs, unsigned long esr)
        if (call_undef_hook(regs) == 0)
                return;
 
-       if (!user_mode(regs))
-               die("Oops - Undefined instruction", regs, esr);
-
        force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc, 0);
 }
-NOKPROBE_SYMBOL(do_undefinstr);
+
+void do_el1_undef(struct pt_regs *regs, unsigned long esr)
+{
+       if (call_undef_hook(regs) == 0)
+               return;
+
+       die("Oops - Undefined instruction", regs, esr);
+}
 
 void do_el0_bti(struct pt_regs *regs)
 {
@@ -511,7 +515,6 @@ void do_el1_bti(struct pt_regs *regs, unsigned long esr)
 {
        die("Oops - BTI", regs, esr);
 }
-NOKPROBE_SYMBOL(do_el1_bti);
 
 void do_el0_fpac(struct pt_regs *regs, unsigned long esr)
 {
@@ -526,7 +529,6 @@ void do_el1_fpac(struct pt_regs *regs, unsigned long esr)
         */
        die("Oops - FPAC", regs, esr);
 }
-NOKPROBE_SYMBOL(do_el1_fpac);
 
 #define __user_cache_maint(insn, address, res)                 \
        if (address >= user_addr_max()) {                       \
@@ -763,7 +765,7 @@ void do_el0_cp15(unsigned long esr, struct pt_regs *regs)
                hook_base = cp15_64_hooks;
                break;
        default:
-               do_undefinstr(regs, esr);
+               do_el0_undef(regs, esr);
                return;
        }
 
@@ -778,7 +780,7 @@ void do_el0_cp15(unsigned long esr, struct pt_regs *regs)
         * EL0. Fall back to our usual undefined instruction handler
         * so that we handle these consistently.
         */
-       do_undefinstr(regs, esr);
+       do_el0_undef(regs, esr);
 }
 #endif
 
@@ -797,7 +799,7 @@ void do_el0_sys(unsigned long esr, struct pt_regs *regs)
         * back to our usual undefined instruction handler so that we handle
         * these consistently.
         */
-       do_undefinstr(regs, esr);
+       do_el0_undef(regs, esr);
 }
 
 static const char *esr_class_str[] = {