]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
x86/traps: Allow custom fixups in handle_bug()
authorPeter Zijlstra <peterz@infradead.org>
Mon, 24 Feb 2025 12:37:07 +0000 (13:37 +0100)
committerIngo Molnar <mingo@kernel.org>
Wed, 26 Feb 2025 11:22:39 +0000 (12:22 +0100)
The normal fixup in handle_bug() is simply continuing at the next
instruction. However upcoming patches make this the wrong thing, so
allow handlers (specifically handle_cfi_failure()) to over-ride
regs->ip.

The callchain is such that the fixup needs to be done before it is
determined if the exception is fatal, as such, revert any changes in
that case.

Additionally, have handle_cfi_failure() remember the regs->ip value it
starts with for reporting.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Kees Cook <kees@kernel.org>
Link: https://lore.kernel.org/r/20250224124200.275223080@infradead.org
arch/x86/kernel/cfi.c
arch/x86/kernel/traps.c

index f6905bef0af844e6716aa74db32f0d29f67c40bc..77086cf565ec14ea9bfbbc5b0850f3b9158b92d9 100644 (file)
@@ -67,16 +67,16 @@ static bool decode_cfi_insn(struct pt_regs *regs, unsigned long *target,
  */
 enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
 {
-       unsigned long target;
+       unsigned long target, addr = regs->ip;
        u32 type;
 
        switch (cfi_mode) {
        case CFI_KCFI:
-               if (!is_cfi_trap(regs->ip))
+               if (!is_cfi_trap(addr))
                        return BUG_TRAP_TYPE_NONE;
 
                if (!decode_cfi_insn(regs, &target, &type))
-                       return report_cfi_failure_noaddr(regs, regs->ip);
+                       return report_cfi_failure_noaddr(regs, addr);
 
                break;
 
@@ -90,7 +90,7 @@ enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
                return BUG_TRAP_TYPE_NONE;
        }
 
-       return report_cfi_failure(regs, regs->ip, &target, type);
+       return report_cfi_failure(regs, addr, &target, type);
 }
 
 /*
index a02a51bf433fa0cf945c2ca0397ffd8942370788..c169f3bd3c6c0811ccb9285b166cd9a91eba9a20 100644 (file)
@@ -287,11 +287,12 @@ static inline void handle_invalid_op(struct pt_regs *regs)
 
 static noinstr bool handle_bug(struct pt_regs *regs)
 {
+       unsigned long addr = regs->ip;
        bool handled = false;
        int ud_type, ud_len;
        s32 ud_imm;
 
-       ud_type = decode_bug(regs->ip, &ud_imm, &ud_len);
+       ud_type = decode_bug(addr, &ud_imm, &ud_len);
        if (ud_type == BUG_NONE)
                return handled;
 
@@ -339,8 +340,17 @@ static noinstr bool handle_bug(struct pt_regs *regs)
                break;
        }
 
-       if (handled)
-               regs->ip += ud_len;
+       /*
+        * When continuing, and regs->ip hasn't changed, move it to the next
+        * instruction. When not continuing execution, restore the instruction
+        * pointer.
+        */
+       if (handled) {
+               if (regs->ip == addr)
+                       regs->ip += ud_len;
+       } else {
+               regs->ip = addr;
+       }
 
        if (regs->flags & X86_EFLAGS_IF)
                raw_local_irq_disable();