objtool, xen: Fix INSN_SYSCALL / INSN_SYSRET semantics
authorJosh Poimboeuf <jpoimboe@kernel.org>
Tue, 8 Apr 2025 07:02:16 +0000 (00:02 -0700)
committerIngo Molnar <mingo@kernel.org>
Tue, 8 Apr 2025 07:14:12 +0000 (09:14 +0200)
Objtool uses an arbitrary rule for INSN_SYSCALL and INSN_SYSRET that
almost works by accident: if it's in a function, control flow continues
after the instruction, otherwise it terminates.

That behavior should instead be based on the semantics of the underlying
instruction.  Change INSN_SYSCALL to always preserve control flow and
INSN_SYSRET to always terminate it.

The changed semantic for INSN_SYSCALL requires a tweak to the
!CONFIG_IA32_EMULATION version of xen_entry_SYSCALL_compat().  In Xen,
SYSCALL is a hypercall which usually returns.  But in this case it's a
hypercall to IRET which doesn't return.  Add UD2 to tell objtool to
terminate control flow, and to prevent undefined behavior at runtime.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Juergen Gross <jgross@suse.com> # for the Xen part
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/19453dfe9a0431b7f016e9dc16d031cad3812a50.1744095216.git.jpoimboe@kernel.org
arch/x86/xen/xen-asm.S
tools/objtool/check.c

index 109af12f76472787a58f943b7412cdc4434ec77f..461bb1526502a0477da9c3fc87528f5394381dca 100644 (file)
@@ -226,9 +226,7 @@ SYM_CODE_END(xen_early_idt_handler_array)
        push %rax
        mov  $__HYPERVISOR_iret, %eax
        syscall         /* Do the IRET. */
-#ifdef CONFIG_MITIGATION_SLS
-       int3
-#endif
+       ud2             /* The SYSCALL should never return. */
 .endm
 
 SYM_CODE_START(xen_iret)
index 2dd89b0f4d5eb09211b2ebf18bcf5bbda71d83f7..69f94bc4749985cf53eef2eea3c240fee104b0bd 100644 (file)
@@ -3685,14 +3685,19 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
                        break;
 
                case INSN_SYSCALL:
+                       if (func && (!next_insn || !next_insn->hint)) {
+                               WARN_INSN(insn, "unsupported instruction in callable function");
+                               return 1;
+                       }
+
+                       break;
+
                case INSN_SYSRET:
-                       if (func) {
-                               if (!next_insn || !next_insn->hint) {
-                                       WARN_INSN(insn, "unsupported instruction in callable function");
-                                       return 1;
-                               }
-                               break;
+                       if (func && (!next_insn || !next_insn->hint)) {
+                               WARN_INSN(insn, "unsupported instruction in callable function");
+                               return 1;
                        }
+
                        return 0;
 
                case INSN_STAC:
@@ -3888,9 +3893,9 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn)
                        return 1;
 
                case INSN_SYSCALL:
+                       break;
+
                case INSN_SYSRET:
-                       if (insn_func(insn))
-                               break;
                        return 0;
 
                case INSN_NOP: