diff options
| author | Jinke Han <jinkehan@didiglobal.com> | 2026-09-08 15:37:42 +0800 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-09-17 09:48:42 +0200 |
| commit | a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch) | |
| tree | 73fef824f9a846cf935869d0dbc5070ef6e2f741 /arch/arm/boot/bootp | |
| download | linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip | |
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function,
a crash can be triggered.
The eBPF tool probes offset 257 of the __hrtimer_run_queues()
function:
<__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1)
<__hrtimer_run_queues+254>: mov %r14,%rdi
<__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12>
<__hrtimer_run_queues+263>: mov %eax,%r12d
<__hrtimer_run_queues+266>: xchg %ax,%ax
<__hrtimer_run_queues+268>: mov %r13,%rdi
Which triggers this crash:
BUG: unable to handle page fault for address: 00000000000f41c9
#PF: supervisor write access in kernel mode
#PF: error_code(0x0002) - not-present page
PGD 0 P4D 0
Oops: 0002 [#1] SMP NOPTI
CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P
RIP: 0010:__hrtimer_run_queues+0x106/0x230
Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is
at the 6th byte of the above CS CALL instruction. Since the CS CALL
instruction occupies 6 bytes, the exception occurred in the middle of that
call instruction.
The root cause is that when using eBPF tools to probe in the middle of a
function, a kprobe with INT3 is used as the underlying implementation.
During single-step emulation of the original CALL instruction,
int3_emulate_call() assumes that the probed CALL instruction is 5 bytes
long. However, the actual CS-prefixed CALL instruction occupies 6 bytes,
so it constructs an incorrect exception return address. When the CPU
returns from the kprobe handler, the next instruction to be executed is at
the address of the last byte of that CS CALL instruction. Coincidentally,
starting from that address, the CPU fetches and decodes a completely
different instruction, which ultimately triggers a kernel crash.
Fix the issue by using the actual instruction length obtained from
the instruction decoder when constructing the exception return
address, rather than relying on the hardcoded CALL_INSN_SIZE macro.
[ mingo: Refined the changelog ]
Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step")
Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Jinke Han <jinkehan@didiglobal.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Yafang Shao <laoar.shao@gmail.com>
Acked-by: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'arch/arm/boot/bootp')
| -rw-r--r-- | arch/arm/boot/bootp/Makefile | 59 | ||||
| -rw-r--r-- | arch/arm/boot/bootp/bootp.lds | 27 | ||||
| -rw-r--r-- | arch/arm/boot/bootp/init.S | 85 | ||||
| -rw-r--r-- | arch/arm/boot/bootp/initrd.S | 7 | ||||
| -rw-r--r-- | arch/arm/boot/bootp/kernel.S | 7 |
5 files changed, 185 insertions, 0 deletions
diff --git a/arch/arm/boot/bootp/Makefile b/arch/arm/boot/bootp/Makefile new file mode 100644 index 000000000..f3443f7d7 --- /dev/null +++ b/arch/arm/boot/bootp/Makefile @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# linux/arch/arm/boot/bootp/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. +# + +ifdef PHYS_OFFSET +add_hex = $(shell printf 0x%x $$(( $(1) + $(2) )) ) + +# If PHYS_OFFSET is set, INITRD_PHYS and PARAMS_PHYS can be derived, +# otherwise they must be passed on the command line. +# +# Note: the following conditions must always be true: +# PARAMS_PHYS must be within 4MB of ZRELADDR +# INITRD_PHYS must be in RAM + +PARAMS_PHYS := $(call add_hex, $(PHYS_OFFSET), 0x100) + +# guess an initrd location if possible +initrd_offset-$(CONFIG_ARCH_FOOTBRIDGE) += 0x00800000 +initrd_offset-$(CONFIG_ARCH_SA1100) += 0x00800000 +initrd_offset-$(CONFIG_ARCH_RPC) += 0x08000000 +INITRD_OFFSET := $(initrd_offset-y) +ifdef INITRD_OFFSET +INITRD_PHYS := $(call add_hex, $(PHYS_OFFSET), $(INITRD_OFFSET)) +endif + +endif + +PHONY += initrd +initrd: + @test "$(PARAMS_PHYS)" != "" || \ + (echo bootpImage: You must specify PHYS_OFFSET of PARAMS_PHYS ; exit -1) + @test "$(INITRD_PHYS)" != "" || \ + (echo bootpImage: You must specify INITRD_OFFSET or INITRD_PHYS ; exit -1) + @test "$(INITRD)" != "" || \ + (echo bootpImage: You must specify INITRD; exit -1) + +LDFLAGS_bootp := --no-undefined -X \ + --defsym initrd_phys=$(INITRD_PHYS) \ + --defsym params_phys=$(PARAMS_PHYS) -T +AFLAGS_initrd.o :=-DINITRD=\"$(INITRD)\" + +targets := bootp init.o kernel.o initrd.o + +# Note that bootp.lds picks up kernel.o and initrd.o +$(obj)/bootp: $(src)/bootp.lds $(addprefix $(obj)/,init.o kernel.o initrd.o) FORCE + $(call if_changed,ld) + +# kernel.o and initrd.o includes a binary image using +# .incbin, a dependency which is not tracked automatically + +$(obj)/kernel.o: arch/arm/boot/zImage FORCE + +$(obj)/initrd.o: initrd $(INITRD) FORCE + +PHONY += $(INITRD) diff --git a/arch/arm/boot/bootp/bootp.lds b/arch/arm/boot/bootp/bootp.lds new file mode 100644 index 000000000..160128186 --- /dev/null +++ b/arch/arm/boot/bootp/bootp.lds @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/boot/bootp/bootp.lds + * + * Copyright (C) 2000-2002 Russell King + */ +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + . = 0; + .text : { + _stext = .; + *(.start) + *(.text) + initrd_size = initrd_end - initrd_start; + _etext = .; + } + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/arch/arm/boot/bootp/init.S b/arch/arm/boot/bootp/init.S new file mode 100644 index 000000000..b562da2f7 --- /dev/null +++ b/arch/arm/boot/bootp/init.S @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/boot/bootp/init.S + * + * Copyright (C) 2000-2003 Russell King. + * + * "Header" file for splitting kernel + initrd. Note that we pass + * r0 through to r3 straight through. + * + * This demonstrates how to append code to the start of the kernel + * zImage, and boot the kernel without copying it around. This + * example would be simpler; if we didn't have an object of unknown + * size immediately following the kernel, we could build this into + * a binary blob, and concatenate the zImage using the cat command. + */ + .section .start, "ax" + .type _start, #function + .globl _start + +_start: add lr, pc, #-0x8 @ lr = current load addr + adr r13, data + ldmia r13!, {r4-r6} @ r5 = dest, r6 = length + add r4, r4, lr @ r4 = initrd_start + load addr + bl move @ move the initrd + +/* + * Setup the initrd parameters to pass to the kernel. This can only be + * passed in via the tagged list. + */ + ldmia r13, {r5-r9} @ get size and addr of initrd + @ r5 = ATAG_CORE + @ r6 = ATAG_INITRD2 + @ r7 = initrd start + @ r8 = initrd end + @ r9 = param_struct address + + ldr r10, [r9, #4] @ get first tag + teq r10, r5 @ is it ATAG_CORE? +/* + * If we didn't find a valid tag list, create a dummy ATAG_CORE entry. + */ + movne r10, #0 @ terminator + movne r4, #2 @ Size of this entry (2 words) + stmiane r9, {r4, r5, r10} @ Size, ATAG_CORE, terminator + +/* + * find the end of the tag list, and then add an INITRD tag on the end. + * If there is already an INITRD tag, then we ignore it; the last INITRD + * tag takes precedence. + */ +taglist: ldr r10, [r9, #0] @ tag length + teq r10, #0 @ last tag (zero length)? + addne r9, r9, r10, lsl #2 + bne taglist + + mov r5, #4 @ Size of initrd tag (4 words) + stmia r9, {r5, r6, r7, r8, r10} + b kernel_start @ call kernel + +/* + * Move the block of memory length r6 from address r4 to address r5 + */ +move: ldmia r4!, {r7 - r10} @ move 32-bytes at a time + stmia r5!, {r7 - r10} + ldmia r4!, {r7 - r10} + stmia r5!, {r7 - r10} + subs r6, r6, #8 * 4 + bcs move + mov pc, lr + + .size _start, . - _start + + .align + + .type data,#object +data: .word initrd_start @ source initrd address + .word initrd_phys @ destination initrd address + .word initrd_size @ initrd size + + .word 0x54410001 @ r5 = ATAG_CORE + .word 0x54420005 @ r6 = ATAG_INITRD2 + .word initrd_phys @ r7 + .word initrd_size @ r8 + .word params_phys @ r9 + .size data, . - data diff --git a/arch/arm/boot/bootp/initrd.S b/arch/arm/boot/bootp/initrd.S new file mode 100644 index 000000000..dd3d04971 --- /dev/null +++ b/arch/arm/boot/bootp/initrd.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + .type initrd_start,#object + .globl initrd_start +initrd_start: + .incbin INITRD + .globl initrd_end +initrd_end: diff --git a/arch/arm/boot/bootp/kernel.S b/arch/arm/boot/bootp/kernel.S new file mode 100644 index 000000000..dc6236c17 --- /dev/null +++ b/arch/arm/boot/bootp/kernel.S @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + .globl kernel_start +kernel_start: + .incbin "arch/arm/boot/zImage" + .globl kernel_end +kernel_end: + .align 2 |
