Re: [PATCH] x86/alternatives: gracefully skip unrecognized indirect call instructions

李则良 <[email protected]>
Newsgroups org.kernel.vger.linux-kernel
Message-ID <CANd6bgJW51rhUxqU=W8tFUT0BE=Y+Ac0TRu5uLxeXGBVsevtnw@mail.gmail.com>
I am currently testing the more general approach shown below. Your
review is also appreciated.

on vmlinux-O1:

   0xffffffff82f447de <+126>: ff 15 a4 a9 cf ff   call   QWORD PTR
[rip+0xffffffffffcfa9a4]        # 0xffffffff82c3f188 <pv_ops+8>
   0xffffffff82f447e4 <+132>: eb f8               jmp
0xffffffff82f447de <early_fixup_exception+126>
   0xffffffff82f447e6 <+134>: 5b                 pop    rbx
   0xffffffff82f447e7 <+135>: 41 5c               pop    r12
   0xffffffff82f447e9 <+137>: 5d                 pop    rbp

on vmlinux-O2:

   0xffffffff82f3bf4f <+127>: ff 15 33 32 d0 ff   call   QWORD PTR
[rip+0xffffffffffd03233]        # 0xffffffff82c3f188 <pv_ops+8>
   0xffffffff82f3bf55 <+133>: eb f8               jmp
0xffffffff82f3bf4f <early_fixup_exception+127>
   0xffffffff82f3bf57 <+135>: 5b                 pop    rbx
   0xffffffff82f3bf58 <+136>: 41 5c               pop    r12
   0xffffffff82f3bf5a <+138>: 5d                 pop    rbp

This is an early draft – please review. Thanks in advance.

From 5989dcf448e4d23ea4f3a0f91fec34f3dd25d26c Mon Sep 17 00:00:00 2001
From: Zeliang Li <[email protected]>
Date: Fri, 14 Aug 2026 03:17:38 +0800
Subject: [PATCH] x86/paravirt: Force RIP-relative paravirt calls under low
 optimization levels

When compiling the kernel with non-standard lower optimization levels like
-O1 (e.g., during specific debugging or framework testing setups) using
newer toolchains like GCC 15.2.0, the compiler exhibits passive register
hoisting. In complex code paths like early_fixup_exception(), it caches the
base address of the global 'pv_ops' structure into a general-purpose register
instead of issuing direct RIP-relative memory loads, producing:

    mov $0xffffffff82c3f180, %rbx
    call *0x8(%rbx)

While this behavior is bypassed under aggressive -O2 optimizations, under -O1
it leaves a register-relative indirect call. This violates the strict format
assertion in the x86 alternative text-patching engine (alt_replace_call),
which expects an 'ALT_FLAG_DIRECT_CALL' site to be a standard 6-byte
RIP-relative indirect call (ff 15), leading to a boot-time kernel BUG.

Fix this by changing the x86_64 paravirt inline assembly to use an "i"
(immediate) constraint for the function pointer address, and explicitly
reference it via (%rip) in the assembly template. This removes the
toolchain's ability to select any other addressing mode, guaranteeing the
emission of compliant 'call *pv_ops+offset(%rip)' sequences on x86_64
regardless of the active compiler -O flag.

For i386, the original "m" constraint is retained since RIP-relative
addressing does not exist on 32-bit x86.

Signed-off-by: Zeliang Li <[email protected]>
---
 arch/x86/include/asm/paravirt_types.h | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/paravirt_types.h
b/arch/x86/include/asm/paravirt_types.h
index b4c4a23e77a1..e8047bdbed3a 100644
--- a/arch/x86/include/asm/paravirt_types.h
+++ b/arch/x86/include/asm/paravirt_types.h
@@ -184,8 +184,6 @@ struct paravirt_patch_template {

 extern struct paravirt_patch_template pv_ops;

-#define paravirt_ptr(array, op) [paravirt_opptr] "m" (array.op)
-
 /*
  * This generates an indirect call based on the operation type number.
  *
@@ -197,9 +195,20 @@ extern struct paravirt_patch_template pv_ops;
  * OTOH since this is effectively a __nocfi indirect call, the paravirt stubs
  * don't need to bother with CFI prefixes.
  */
+#ifdef CONFIG_X86_64
+
+#define paravirt_ptr(array, op) [paravirt_opptr] "i" (&(array.op))
 #define PARAVIRT_CALL \
  ANNOTATE_RETPOLINE_SAFE "\n\t" \
- "call *%[paravirt_opptr]"
+        "call *%c[paravirt_opptr](%%rip);"
+#else /* CONFIG_X86_32 */
+
+#define paravirt_ptr(array, op) [paravirt_opptr] "m" (array.op)
+#define PARAVIRT_CALL \
+    ANNOTATE_RETPOLINE_SAFE "\n\t" \
+        "call *%[paravirt_opptr];"
+
+#endif /* CONFIG_X86_64 */

 /*
  * These macros are intended to wrap calls through one of the paravirt
-- 
2.53.0

李则良 <[email protected]> 于2026年8月14日周五 03:35写道:
>
> >
> > That's not what I asked, now was it. I asked what opcodes it does
> > generate. Disassemble an affected function and show the difference
> > between the normal -O2 and your -O1 build that causes the failure.
> on vmlinux-O1:
> (gdb) until arch/x86/kernel/alternative.c:557
> alt_replace_call (instr=0xffffffff82f447e5 <early_fixup_exception+133>
> "\377S\b\220\220\353\371[A\\]\303\314\314\314\314\303\314\314\314\314f\017\037D",
> insn_buff=0xffffffff82c03d78 <init_thread_union+15736>
> "\350\263\177\020\377",
>     a=0xffffffff831dae0e) at arch/x86/kernel/alternative.c:557
> 557 pr_err("ALT_FLAG_DIRECT_CALL set for unrecognized indirect call\n");
> vmlinux-O1:
>   0xffffffff82f447e5 <+133>: ff 53 08           call   QWORD PTR [rbx+0x8]
>    0xffffffff82f447e8 <+136>: 90                 nop
>    0xffffffff82f447e9 <+137>: 90                 nop
>    0xffffffff82f447ea <+138>: eb f9               jmp
> 0xffffffff82f447e5 <early_fixup_exception+133>
>    0xffffffff82f447ec <+140>: 5b                 pop    rbx
>    0xffffffff82f447ed <+141>: 41 5c               pop    r12
>
> vmlinux-O2:
>   0xffffffff82f3bf4b <+123>: 74 b0               je
> 0xffffffff82f3befd <early_fixup_exception+45>
>    0xffffffff82f3bf4d <+125>: eb 08               jmp
> 0xffffffff82f3bf57 <early_fixup_exception+135>
>    0xffffffff82f3bf4f <+127>: ff 15 33 32 d0 ff   call   QWORD PTR
> [rip+0xffffffffffd03233]        # 0xffffffff82c3f188 <pv_ops+8>
>    0xffffffff82f3bf55 <+133>: eb f8               jmp
> 0xffffffff82f3bf4f <early_fixup_exception+127>
>    0xffffffff82f3bf57 <+135>: 5b                 pop    rbx
>    0xffffffff82f3bf58 <+136>: 41 5c               pop    r12
>    0xffffffff82f3bf5a <+138>: 5d                 pop    rbp
>
> I use the patch below to bypass the problem while keeping all logic of
> alt_replace_call intact.
>
> From 2538684697e537298aa9082b582bc7dddd29b278 Mon Sep 17 00:00:00 2001
> From: Zeliang Li <[email protected]>
> Date: Fri, 14 Aug 2026 02:32:19 +0800
> Subject: [PATCH] x86/mm: force O2 optimization for early_fixup_exception
>
> When the kernel is compiled with non-standard optimization flags
> (e.g., KCFLAGS=-O1), the function early_fixup_exception may be
> compiled to an indirect call instruction that is not recognized
> by alt_replace_call() (e.g., "call *0x8(%rbx)"). This triggers a
> BUG() during early boot, as seen in:
>
>   ALT_FLAG_DIRECT_CALL: original instruction is not a 6-byte ...
>   instruction at early_fixup_exception+0x85/0xa0, length=5
>   instruction bytes: ff 53 08 90 90
>
> Force this function to be compiled with -O2 optimization level,
> which guarantees the standard 6-byte `ff 15 <disp32>` form used
> by alternatives patching.
>
> Signed-off-by: Zeliang Li <[email protected]>
> ---
>  arch/x86/mm/extable.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c
> index ceb8d03191ab..5d5c7510a817 100644
> --- a/arch/x86/mm/extable.c
> +++ b/arch/x86/mm/extable.c
> @@ -375,6 +375,7 @@ int fixup_exception(struct pt_regs *regs, int
> trapnr, unsigned long error_code,
>  extern unsigned int early_recursion_flag;
>
>  /* Restricted version used during very early boot */
> +__attribute__((optimize("O2")))
>  void __init early_fixup_exception(struct pt_regs *regs, int trapnr)
>  {
>   /* Ignore early NMIs. */
> --
> 2.53.0
>
> --
> KISS == Keep it simple,stupid~:-)
> http://lizeliang.org



-- 
KISS == Keep it simple,stupid~:-)
http://lizeliang.org
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.