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