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/serial/empeg.c | |
| 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/serial/empeg.c')
| -rw-r--r-- | drivers/usb/serial/empeg.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c new file mode 100644 index 000000000..4937575ca --- /dev/null +++ b/drivers/usb/serial/empeg.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Empeg empeg-car player driver + * + * Copyright (C) 2000, 2001 + * Gary Brubaker (xavyer@ix.netcom.com) + * + * Copyright (C) 1999 - 2001 + * Greg Kroah-Hartman (greg@kroah.com) + * + * See Documentation/usb/usb-serial.rst for more information on using this + * driver + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Gary Brubaker <xavyer@ix.netcom.com>" +#define DRIVER_DESC "USB Empeg Mark I/II Driver" + +#define EMPEG_VENDOR_ID 0x084f +#define EMPEG_PRODUCT_ID 0x0001 + +/* function prototypes for an empeg-car player */ +static int empeg_startup(struct usb_serial *serial); +static void empeg_init_termios(struct tty_struct *tty); + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(EMPEG_VENDOR_ID, EMPEG_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_serial_driver empeg_device = { + .driver = { + .name = "empeg", + }, + .id_table = id_table, + .num_ports = 1, + .bulk_out_size = 256, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, + .attach = empeg_startup, + .init_termios = empeg_init_termios, +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &empeg_device, NULL +}; + +static int empeg_startup(struct usb_serial *serial) +{ + int r; + + if (serial->dev->actconfig->desc.bConfigurationValue != 1) { + dev_err(&serial->dev->dev, "active config #%d != 1 ??\n", + serial->dev->actconfig->desc.bConfigurationValue); + return -ENODEV; + } + + r = usb_reset_configuration(serial->dev); + + /* continue on with initialization */ + return r; +} + +static void empeg_init_termios(struct tty_struct *tty) +{ + struct ktermios *termios = &tty->termios; + + /* + * The empeg-car player wants these particular tty settings. + * You could, for example, change the baud rate, however the + * player only supports 115200 (currently), so there is really + * no point in support for changes to the tty settings. + * (at least for now) + * + * The default requirements for this device are: + */ + termios->c_iflag + &= ~(IGNBRK /* disable ignore break */ + | BRKINT /* disable break causes interrupt */ + | PARMRK /* disable mark parity errors */ + | ISTRIP /* disable clear high bit of input characters */ + | INLCR /* disable translate NL to CR */ + | IGNCR /* disable ignore CR */ + | ICRNL /* disable translate CR to NL */ + | IXON); /* disable enable XON/XOFF flow control */ + + termios->c_oflag + &= ~OPOST; /* disable postprocess output characters */ + + termios->c_lflag + &= ~(ECHO /* disable echo input characters */ + | ECHONL /* disable echo new line */ + | ICANON /* disable erase, kill, werase, and rprnt special characters */ + | ISIG /* disable interrupt, quit, and suspend special characters */ + | IEXTEN); /* disable non-POSIX special characters */ + + termios->c_cflag + &= ~(CSIZE /* no size */ + | PARENB /* disable parity bit */ + | CBAUD); /* clear current baud rate */ + + termios->c_cflag + |= CS8; /* character size 8 bits */ + + tty_encode_baud_rate(tty, 115200, 115200); +} + +module_usb_serial_driver(serial_drivers, id_table); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); |
