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 /drivers/usb/host/xhci-debugfs.h | |
| 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 'drivers/usb/host/xhci-debugfs.h')
| -rw-r--r-- | drivers/usb/host/xhci-debugfs.h | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-debugfs.h b/drivers/usb/host/xhci-debugfs.h new file mode 100644 index 000000000..7c074b4be --- /dev/null +++ b/drivers/usb/host/xhci-debugfs.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * xhci-debugfs.h - xHCI debugfs interface + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ + +#ifndef __LINUX_XHCI_DEBUGFS_H +#define __LINUX_XHCI_DEBUGFS_H + +#include <linux/debugfs.h> + +#define DEBUGFS_NAMELEN 32 + +#define REG_CAPLENGTH 0x00 +#define REG_HCSPARAMS1 0x04 +#define REG_HCSPARAMS2 0x08 +#define REG_HCSPARAMS3 0x0c +#define REG_HCCPARAMS1 0x10 +#define REG_DOORBELLOFF 0x14 +#define REG_RUNTIMEOFF 0x18 +#define REG_HCCPARAMS2 0x1c + +#define REG_USBCMD 0x00 +#define REG_USBSTS 0x04 +#define REG_PAGESIZE 0x08 +#define REG_DNCTRL 0x14 +#define REG_CRCR 0x18 +#define REG_DCBAAP_LOW 0x30 +#define REG_DCBAAP_HIGH 0x34 +#define REG_CONFIG 0x38 + +#define REG_MFINDEX 0x00 +#define REG_IR0_IMAN 0x20 +#define REG_IR0_IMOD 0x24 +#define REG_IR0_ERSTSZ 0x28 +#define REG_IR0_ERSTBA_LOW 0x30 +#define REG_IR0_ERSTBA_HIGH 0x34 +#define REG_IR0_ERDP_LOW 0x38 +#define REG_IR0_ERDP_HIGH 0x3c + +#define REG_EXTCAP_USBLEGSUP 0x00 +#define REG_EXTCAP_USBLEGCTLSTS 0x04 + +#define REG_EXTCAP_REVISION 0x00 +#define REG_EXTCAP_NAME 0x04 +#define REG_EXTCAP_PORTINFO 0x08 +#define REG_EXTCAP_PORTTYPE 0x0c +#define REG_EXTCAP_MANTISSA1 0x10 +#define REG_EXTCAP_MANTISSA2 0x14 +#define REG_EXTCAP_MANTISSA3 0x18 +#define REG_EXTCAP_MANTISSA4 0x1c +#define REG_EXTCAP_MANTISSA5 0x20 +#define REG_EXTCAP_MANTISSA6 0x24 + +#define REG_EXTCAP_DBC_CAPABILITY 0x00 +#define REG_EXTCAP_DBC_DOORBELL 0x04 +#define REG_EXTCAP_DBC_ERSTSIZE 0x08 +#define REG_EXTCAP_DBC_ERST_LOW 0x10 +#define REG_EXTCAP_DBC_ERST_HIGH 0x14 +#define REG_EXTCAP_DBC_ERDP_LOW 0x18 +#define REG_EXTCAP_DBC_ERDP_HIGH 0x1c +#define REG_EXTCAP_DBC_CONTROL 0x20 +#define REG_EXTCAP_DBC_STATUS 0x24 +#define REG_EXTCAP_DBC_PORTSC 0x28 +#define REG_EXTCAP_DBC_CONT_LOW 0x30 +#define REG_EXTCAP_DBC_CONT_HIGH 0x34 +#define REG_EXTCAP_DBC_DEVINFO1 0x38 +#define REG_EXTCAP_DBC_DEVINFO2 0x3c + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = REG_ ##nm, \ +} + +struct xhci_regset { + char name[DEBUGFS_NAMELEN]; + struct debugfs_regset32 regset; + size_t nregs; + struct list_head list; +}; + +struct xhci_file_map { + const char *name; + int (*show)(struct seq_file *s, void *unused); +}; + +struct xhci_ep_priv { + char name[DEBUGFS_NAMELEN]; + struct dentry *root; + struct xhci_stream_info *stream_info; + struct xhci_ring *show_ring; + unsigned int stream_id; +}; + +struct xhci_slot_priv { + char name[DEBUGFS_NAMELEN]; + struct dentry *root; + struct xhci_ep_priv *eps[31]; + struct xhci_virt_device *dev; +}; + +#ifdef CONFIG_DEBUG_FS +void xhci_debugfs_init(struct xhci_hcd *xhci); +void xhci_debugfs_exit(struct xhci_hcd *xhci); +void __init xhci_debugfs_create_root(void); +void __exit xhci_debugfs_remove_root(void); +void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id); +void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id); +void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index); +void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index); +void xhci_debugfs_create_stream_files(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index); +#else +static inline void xhci_debugfs_init(struct xhci_hcd *xhci) { } +static inline void xhci_debugfs_exit(struct xhci_hcd *xhci) { } +static inline void __init xhci_debugfs_create_root(void) { } +static inline void __exit xhci_debugfs_remove_root(void) { } +static inline void xhci_debugfs_create_slot(struct xhci_hcd *x, int s) { } +static inline void xhci_debugfs_remove_slot(struct xhci_hcd *x, int s) { } +static inline void +xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index) { } +static inline void +xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index) { } +static inline void +xhci_debugfs_create_stream_files(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index) { } +#endif /* CONFIG_DEBUG_FS */ + +#endif /* __LINUX_XHCI_DEBUGFS_H */ |
