| 0/0 |
2026/05/30 05:26 |
flow |
assessment-security |
38m
Results: map[DenialOfService:false Explanation:Based on the analysis of the provided bug report and the kernel source code,
here is the assessment of the issue:
**Root Cause Analysis:**
The bug report indicates an RCU task stall (`rcu_preempt detected stalls on
CPUs/tasks: Tasks blocked on level-0 rcu_node`). This specific type of stall
occurs when a task is preempted while inside an RCU read-side critical section
and fails to resume and call `rcu_read_unlock()` within the RCU grace period
timeout (typically 21 seconds).
Looking at the stack trace, the `kworker` thread was executing `usb_probe` for
the `yealink` driver, which eventually called `kmalloc`. Because KASAN is
enabled, `kmalloc` saves the stack trace (`kasan_save_stack` ->
`stack_trace_save` -> `arch_stack_walk` -> `unwind_next_frame`).
Inside `unwind_next_frame`, the ORC unwinder uses `guard(rcu)()` to protect
against module unloading, which internally calls `rcu_read_lock()` and
`rcu_read_unlock()`.
The instruction pointer (`RIP: lock_acquire+0x5e/0x380`) points exactly to the
`raw_local_irq_restore(flags)` call at the end of `lock_acquire` (which is
called by lockdep during `rcu_read_lock`).
What happened is that the moment interrupts were re-enabled by
`raw_local_irq_restore`, a pending timer interrupt fired
(`asm_sysvec_apic_timer_interrupt`). Upon returning from this interrupt
(`irqentry_exit` -> `preempt_schedule_irq`), the kernel decided to preempt the
`kworker` task.
Because the task was preempted exactly after acquiring the RCU read lock but
before releasing it, it was placed on the RCU node's blocked tasks list. The
task state is shown as `state:R running task`, meaning it was placed on the
runqueue but was starved of CPU time for over 21 seconds. This starvation is a
known artifact of fuzzers like syzkaller, which often overload the system with
heavy workloads or real-time (`SCHED_FIFO`) tasks on small VMs, preventing
normal tasks from getting scheduled.
**Security Impact:**
There is no infinite loop, deadlock, or memory corruption in the `yealink`
driver or the USB subsystem in this trace. The code paths involved (like
`usb_probe`, `input_register_device`, and `cdev_add`) only contain small,
bounded loops. The delay is purely a scheduling artifact caused by
fuzzer-induced CPU starvation combined with the heavy overhead of KASAN and
Lockdep.
In a production kernel (without KASAN/Lockdep and without malicious real-time
tasks hogging the CPU), this code path executes extremely fast. Therefore, this
is a false positive and does not represent a genuine vulnerability or a
Denial-of-Service vector.
**Conclusion:**
- **Exploitable:** No
- **Denial of Service:** No (False positive due to fuzzer-induced CPU
starvation)
- **Trigger Mechanisms:** None (Not a bug) Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/30 05:26 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/usb/ch9.h>
static unsigned long long procid;
static void sleep_ms(uint64_t ms)
{
usleep(ms * 1000);
}
#define MAX_FDS 30
#define USB_MAX_IFACE_NUM 4
#define USB_MAX_EP_NUM 32
#define USB_MAX_FDS 6
struct usb_endpoint_index {
struct usb_endpoint_descriptor desc;
int handle;
};
struct usb_iface_index {
struct usb_interface_descriptor* iface;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bInterfaceClass;
struct usb_endpoint_index eps[USB_MAX_EP_NUM];
int eps_num;
};
struct usb_device_index {
struct usb_device_descriptor* dev;
struct usb_config_descriptor* config;
uint8_t bDeviceClass;
uint8_t bMaxPower;
int config_length;
struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
int ifaces_num;
int iface_cur;
};
struct usb_info {
int fd;
struct usb_device_index index;
};
static struct usb_info usb_devices[USB_MAX_FDS];
static struct usb_device_index* lookup_usb_index(int fd)
{
for (int i = 0; i < USB_MAX_FDS; i++) {
if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd)
return &usb_devices[i].index;
}
return NULL;
}
static int usb_devices_num;
static bool parse_usb_descriptor(const char* buffer, size_t length, struct usb_device_index* index)
{
if (length < sizeof(*index->dev) + sizeof(*index->config))
return false;
memset(index, 0, sizeof(*index));
index->dev = (struct usb_device_descriptor*)buffer;
index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev));
index->bDeviceClass = index->dev->bDeviceClass;
index->bMaxPower = index->config->bMaxPower;
index->config_length = length - sizeof(*index->dev);
index->iface_cur = -1;
size_t offset = 0;
while (true) {
if (offset + 1 >= length)
break;
uint8_t desc_length = buffer[offset];
uint8_t desc_type = buffer[offset + 1];
if (desc_length <= 2)
break;
if (offset + desc_length > length)
break;
if (desc_type == USB_DT_INTERFACE && index->ifaces_num < USB_MAX_IFACE_NUM) {
struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset);
index->ifaces[index->ifaces_num].iface = iface;
index->ifaces[index->ifaces_num].bInterfaceNumber = iface->bInterfaceNumber;
index->ifaces[index->ifaces_num].bAlternateSetting = iface->bAlternateSetting;
index->ifaces[index->ifaces_num].bInterfaceClass = iface->bInterfaceClass;
index->ifaces_num++;
}
if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) {
struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1];
if (iface->eps_num < USB_MAX_EP_NUM) {
memcpy(&iface->eps[iface->eps_num].desc, buffer + offset, sizeof(iface->eps[iface->eps_num].desc));
iface->eps_num++;
}
}
offset += desc_length;
}
return true;
}
static struct usb_device_index* add_usb_index(int fd, const char* dev, size_t dev_len)
{
int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED);
if (i >= USB_MAX_FDS)
return NULL;
if (!parse_usb_descriptor(dev, dev_len, &usb_devices[i].index))
return NULL;
__atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE);
return &usb_devices[i].index;
}
struct vusb_connect_string_descriptor {
uint32_t len;
char* str;
} __attribute__((packed));
struct vusb_connect_descriptors {
uint32_t qual_len;
char* qual;
uint32_t bos_len;
char* bos;
uint32_t strs_len;
struct vusb_connect_string_descriptor strs[0];
} __attribute__((packed));
static const char default_string[] = {
8, USB_DT_STRING,
's', 0, 'y', 0, 'z', 0
};
static const char default_lang_id[] = {
4, USB_DT_STRING,
0x09, 0x04
};
static bool lookup_connect_response_in(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl,
struct usb_qualifier_descriptor* qual,
char** response_data, uint32_t* response_length)
{
struct usb_device_index* index = lookup_usb_index(fd);
uint8_t str_idx;
if (!index)
return false;
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
switch (ctrl->wValue >> 8) {
case USB_DT_DEVICE:
*response_data = (char*)index->dev;
*response_length = sizeof(*index->dev);
return true;
case USB_DT_CONFIG:
*response_data = (char*)index->config;
*response_length = index->config_length;
return true;
case USB_DT_STRING:
str_idx = (uint8_t)ctrl->wValue;
if (descs && str_idx < descs->strs_len) {
*response_data = descs->strs[str_idx].str;
*response_length = descs->strs[str_idx].len;
return true;
}
if (str_idx == 0) {
*response_data = (char*)&default_lang_id[0];
*response_length = default_lang_id[0];
return true;
}
*response_data = (char*)&default_string[0];
*response_length = default_string[0];
return true;
case USB_DT_BOS:
*response_data = descs->bos;
*response_length = descs->bos_len;
return true;
case USB_DT_DEVICE_QUALIFIER:
if (!descs->qual) {
qual->bLength = sizeof(*qual);
qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
qual->bcdUSB = index->dev->bcdUSB;
qual->bDeviceClass = index->dev->bDeviceClass;
qual->bDeviceSubClass = index->dev->bDeviceSubClass;
qual->bDeviceProtocol = index->dev->bDeviceProtocol;
qual->bMaxPacketSize0 = index->dev->bMaxPacketSize0;
qual->bNumConfigurations = index->dev->bNumConfigurations;
qual->bRESERVED = 0;
*response_data = (char*)qual;
*response_length = sizeof(*qual);
return true;
}
*response_data = descs->qual;
*response_length = descs->qual_len;
return true;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
return false;
}
typedef bool (*lookup_connect_out_response_t)(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done);
static bool lookup_connect_response_out_generic(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done)
{
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_SET_CONFIGURATION:
*done = true;
return true;
default:
break;
}
break;
}
return false;
}
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
__u8 driver_name[UDC_NAME_LENGTH_MAX];
__u8 device_name[UDC_NAME_LENGTH_MAX];
__u8 speed;
};
enum usb_raw_event_type {
USB_RAW_EVENT_INVALID = 0,
USB_RAW_EVENT_CONNECT = 1,
USB_RAW_EVENT_CONTROL = 2,
};
struct usb_raw_event {
__u32 type;
__u32 length;
__u8 data[0];
};
struct usb_raw_ep_io {
__u16 ep;
__u16 flags;
__u32 length;
__u8 data[0];
};
#define USB_RAW_EPS_NUM_MAX 30
#define USB_RAW_EP_NAME_MAX 16
#define USB_RAW_EP_ADDR_ANY 0xff
struct usb_raw_ep_caps {
__u32 type_control : 1;
__u32 type_iso : 1;
__u32 type_bulk : 1;
__u32 type_int : 1;
__u32 dir_in : 1;
__u32 dir_out : 1;
};
struct usb_raw_ep_limits {
__u16 maxpacket_limit;
__u16 max_streams;
__u32 reserved;
};
struct usb_raw_ep_info {
__u8 name[USB_RAW_EP_NAME_MAX];
__u32 addr;
struct usb_raw_ep_caps caps;
struct usb_raw_ep_limits limits;
};
struct usb_raw_eps_info {
struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
};
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN _IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
#define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
#define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32)
#define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32)
#define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32)
static int usb_raw_open()
{
return open("/dev/raw-gadget", O_RDWR);
}
static int usb_raw_init(int fd, uint32_t speed, const char* driver, const char* device)
{
struct usb_raw_init arg;
strncpy((char*)&arg.driver_name[0], driver, sizeof(arg.driver_name));
strncpy((char*)&arg.device_name[0], device, sizeof(arg.device_name));
arg.speed = speed;
return ioctl(fd, USB_RAW_IOCTL_INIT, &arg);
}
static int usb_raw_run(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_RUN, 0);
}
static int usb_raw_ep_write(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io);
}
static int usb_raw_configure(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0);
}
static int usb_raw_vbus_draw(int fd, uint32_t power)
{
return ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, power);
}
static int usb_raw_ep0_write(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io);
}
static int usb_raw_ep0_read(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_READ, io);
}
static int usb_raw_event_fetch(int fd, struct usb_raw_event* event)
{
return ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event);
}
static int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor* desc)
{
return ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, desc);
}
static int usb_raw_ep_disable(int fd, int ep)
{
return ioctl(fd, USB_RAW_IOCTL_EP_DISABLE, ep);
}
static int usb_raw_ep0_stall(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
static int lookup_endpoint(int fd, uint8_t bEndpointAddress)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
if (index->iface_cur < 0)
return -1;
for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++)
if (index->ifaces[index->iface_cur].eps[ep].desc.bEndpointAddress == bEndpointAddress)
return index->ifaces[index->iface_cur].eps[ep].handle;
return -1;
}
#define USB_MAX_PACKET_SIZE 4096
struct usb_raw_control_event {
struct usb_raw_event inner;
struct usb_ctrlrequest ctrl;
char data[USB_MAX_PACKET_SIZE];
};
struct usb_raw_ep_io_data {
struct usb_raw_ep_io inner;
char data[USB_MAX_PACKET_SIZE];
};
static void set_interface(int fd, int n)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return;
if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) {
int rv = usb_raw_ep_disable(fd, index->ifaces[index->iface_cur].eps[ep].handle);
if (rv < 0) {
} else {
}
}
}
if (n >= 0 && n < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[n].eps_num; ep++) {
int rv = usb_raw_ep_enable(fd, &index->ifaces[n].eps[ep].desc);
if (rv < 0) {
} else {
index->ifaces[n].eps[ep].handle = rv;
}
}
index->iface_cur = n;
}
}
static int configure_device(int fd)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
int rv = usb_raw_vbus_draw(fd, index->bMaxPower);
if (rv < 0) {
return rv;
}
rv = usb_raw_configure(fd);
if (rv < 0) {
return rv;
}
set_interface(fd, 0);
return 0;
}
static volatile long syz_usb_connect_impl(uint64_t speed, uint64_t dev_len, const char* dev,
const struct vusb_connect_descriptors* descs,
lookup_connect_out_response_t lookup_connect_response_out)
{
if (!dev) {
return -1;
}
int fd = usb_raw_open();
if (fd < 0) {
return fd;
}
if (fd >= MAX_FDS) {
close(fd);
return -1;
}
struct usb_device_index* index = add_usb_index(fd, dev, dev_len);
if (!index) {
return -1;
}
char device[32];
sprintf(&device[0], "dummy_udc.%llu", procid);
int rv = usb_raw_init(fd, speed, "dummy_udc", &device[0]);
if (rv < 0) {
return rv;
}
rv = usb_raw_run(fd);
if (rv < 0) {
return rv;
}
bool done = false;
while (!done) {
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = sizeof(event.ctrl);
rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL)
continue;
char* response_data = NULL;
uint32_t response_length = 0;
struct usb_qualifier_descriptor qual;
if (event.ctrl.bRequestType & USB_DIR_IN) {
if (!lookup_connect_response_in(fd, descs, &event.ctrl, &qual, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
continue;
}
} else {
if (!lookup_connect_response_out(fd, descs, &event.ctrl, &done)) {
usb_raw_ep0_stall(fd);
continue;
}
response_data = NULL;
response_length = event.ctrl.wLength;
}
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
event.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
rv = configure_device(fd);
if (rv < 0) {
return rv;
}
}
struct usb_raw_ep_io_data response;
response.inner.ep = 0;
response.inner.flags = 0;
if (response_length > sizeof(response.data))
response_length = 0;
if (event.ctrl.wLength < response_length)
response_length = event.ctrl.wLength;
response.inner.length = response_length;
if (response_data)
memcpy(&response.data[0], response_data, response_length);
else
memset(&response.data[0], 0, response_length);
if (event.ctrl.bRequestType & USB_DIR_IN) {
rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
} else {
rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
}
if (rv < 0) {
return rv;
}
}
sleep_ms(200);
return fd;
}
static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
uint64_t speed = a0;
uint64_t dev_len = a1;
const char* dev = (const char*)a2;
const struct vusb_connect_descriptors* descs = (const struct vusb_connect_descriptors*)a3;
return syz_usb_connect_impl(speed, dev_len, dev, descs, &lookup_connect_response_out_generic);
}
static volatile long syz_usb_ep_write(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
int fd = a0;
uint8_t ep = a1;
uint32_t len = a2;
char* data = (char*)a3;
int ep_handle = lookup_endpoint(fd, ep);
if (ep_handle < 0) {
return -1;
}
struct usb_raw_ep_io_data io_data;
io_data.inner.ep = ep_handle;
io_data.inner.flags = 0;
if (len > sizeof(io_data.data))
len = sizeof(io_data.data);
io_data.inner.length = len;
memcpy(&io_data.data[0], data, len);
int rv = usb_raw_ep_write(fd, (struct usb_raw_ep_io*)&io_data);
if (rv < 0) {
return rv;
}
sleep_ms(200);
return 0;
}
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_usb_connect arguments: [
// speed: usb_device_speed = 0x2 (8 bytes)
// dev_len: len = 0x2d (8 bytes)
// dev: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {12 01 00 00 ea fa 72 40 93 69 01 b0 29 3d f4 00 10 01 09 02 1b 00 01 24 a8 00 80 09 04 00 00 01 03 00 00 00 09 05} (length 0x26)
// }
// union ANYUNION {
// ANYRES32: ANYRES32 (resource)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000000040, "... [truncated large byte array] ...", 38);
*(uint32_t*)0x200000000066 = -1;
syz_usb_connect(/*speed=USB_SPEED_FULL*/2, /*dev_len=*/0x2d, /*dev=*/0x200000000040, /*conn_descs=*/0);
// syz_usb_ep_write$ath9k_ep1 arguments: [
// fd: fd_usb_ath9k (resource)
// ep: const = 0x82 (1 bytes)
// len: bytesize = 0x1380 (8 bytes)
// data: nil
// ]
syz_usb_ep_write(/*fd=*/-1, /*ep=*/0x82, /*len=*/0x1380, /*data=*/0);
return 0;
}
]
|
| 2/1 |
2026/05/30 05:26 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/55e5d20408a8e61117c5e3ce7d7a9a50efa578ea]
|
| 3/1 |
2026/05/30 05:28 |
action |
kernel-builder |
14m
Results: map[KernelObj:/app/workdir/cache/build/a2f70931edc1c7485560623acb0e723e7bd21f58]
|
| 4/1 |
2026/05/30 05:42 |
action |
codesearch-prepare |
15m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/30 05:57 |
agent |
expert |
7m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: Tasks blocked on level-0 rcu_node (CPUs 0-1):
P3032/1:b..l
rcu: (detected by 0, t=10502 jiffies, g=42045, q=380374 ncpus=2)
task:kworker/1:2 state:R running task
stack:23640 pid:3032 tgid:3032 ppid:2 task_flags:0x4208060 flags:0x00080000
Workqueue: usb_hub_wq hub_event
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5298 [inline]
__schedule+0xeb1/0x4220 kernel/sched/core.c:6911
preempt_schedule_irq+0x50/0x90 kernel/sched/core.c:7238
irqentry_exit+0x15a/0x620 kernel/entry/common.c:239
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:lock_acquire+0x5e/0x380 kernel/locking/lockdep.c:5872
Code: 05 3b 44 66 0b 83 f8 07 0f 87 f0 00 00 00 48 0f a3 05 c6 20 61 09 0f 82 c2 02 00 00 8b 35 6e 30 61 09 85 f6 0f 85 dd 00 00 00 <48> 8b 44 24 30 65 48 2b 05 dd 43 66 0b 0f 85 02 03 00 00 48 83 c4
RSP: 0018:ffffc9000191e998 EFLAGS: 00000206
RAX: 0000000000000046 RBX: 0000000000000000 RCX: 0000000000000006
RDX: 0000000000000000 RSI: ffffffff890010b3 RDI: ffffffff87afc6a0
RBP: ffffffff896de6e0 R08: 0000000055b0517e R09: 0000000000000007
R10: 0000000000000200 R11: 0000000000000000 R12: 0000000000000002
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
rcu_lock_acquire include/linux/rcupdate.h:312 [inline]
rcu_read_lock include/linux/rcupdate.h:850 [inline]
class_rcu_constructor include/linux/rcupdate.h:1193 [inline]
unwind_next_frame+0xd1/0x1ea0 arch/x86/kernel/unwind_orc.c:495
arch_stack_walk+0x94/0xf0 arch/x86/kernel/stacktrace.c:25
stack_trace_save+0x8e/0xc0 kernel/stacktrace.c:122
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x8f/0xa0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5260 [inline]
__kmalloc_noprof+0x302/0x810 mm/slub.c:5272
kmalloc_noprof include/linux/slab.h:954 [inline]
kobj_map+0x80/0x4b0 drivers/base/map.c:44
cdev_add+0xa7/0x1a0 fs/char_dev.c:488
cdev_device_add+0x10f/0x270 fs/char_dev.c:548
evdev_connect+0x3a8/0x4b0 drivers/input/evdev.c:1383
input_attach_handler.isra.0+0x177/0x1e0 drivers/input/input.c:994
input_register_device.cold+0x139/0x375 drivers/input/input.c:2378
usb_probe+0x1226/0x17c0 drivers/input/misc/yealink.c:928
usb_probe_interface+0x303/0x8f0 drivers/usb/core/driver.c:396
call_driver_probe drivers/base/dd.c:583 [inline]
really_probe+0x241/0xa60 drivers/base/dd.c:661
__driver_probe_device+0x1de/0x400 drivers/base/dd.c:803
driver_probe_device+0x4c/0x1b0 drivers/base/dd.c:833
__device_attach_driver+0x1df/0x340 drivers/base/dd.c:961
bus_for_each_drv+0x159/0x1e0 drivers/base/bus.c:500
__device_attach+0x1e4/0x4d0 drivers/base/dd.c:1033
device_initial_probe+0xaf/0xd0 drivers/base/dd.c:1088
bus_probe_device+0x64/0x160 drivers/base/bus.c:574
device_add+0x11d9/0x1950 drivers/base/core.c:3689
usb_set_configuration+0xd97/0x1c60 drivers/usb/core/message.c:2268
usb_generic_driver_probe+0xa1/0xe0 drivers/usb/core/generic.c:250
usb_probe_device+0xef/0x400 drivers/usb/core/driver.c:291
call_driver_probe drivers/base/dd.c:583 [inline]
really_probe+0x241/0xa60 drivers/base/dd.c:661
__driver_probe_device+0x1de/0x400 drivers/base/dd.c:803
driver_probe_device+0x4c/0x1b0 drivers/base/dd.c:833
__device_attach_driver+0x1df/0x340 drivers/base/dd.c:961
bus_for_each_drv+0x159/0x1e0 drivers/base/bus.c:500
__device_attach+0x1e4/0x4d0 drivers/base/dd.c:1033
device_initial_probe+0xaf/0xd0 drivers/base/dd.c:1088
bus_probe_device+0x64/0x160 drivers/base/bus.c:574
device_add+0x11d9/0x1950 drivers/base/core.c:3689
usb_new_device.cold+0x685/0x115c drivers/usb/core/hub.c:2695
hub_port_connect drivers/usb/core/hub.c:5567 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
port_event drivers/usb/core/hub.c:5871 [inline]
hub_event+0x314d/0x4af0 drivers/usb/core/hub.c:5953
process_one_work+0xa23/0x19a0 kernel/workqueue.c:3276
process_scheduled_works kernel/workqueue.c:3359 [inline]
worker_thread+0x5ef/0xe50 kernel/workqueue.c:3440
kthread+0x370/0x450 kernel/kthread.c:436
ret_from_fork+0x6c3/0xcb0 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
It is reproducible with the followint program.
Keep in mind that it may lack the precise threading, sandboxing, and some arguments of a working reproducer.
But it should give an idea of the involved syscalls.
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/usb/ch9.h>
static unsigned long long procid;
static void sleep_ms(uint64_t ms)
{
usleep(ms * 1000);
}
#define MAX_FDS 30
#define USB_MAX_IFACE_NUM 4
#define USB_MAX_EP_NUM 32
#define USB_MAX_FDS 6
struct usb_endpoint_index {
struct usb_endpoint_descriptor desc;
int handle;
};
struct usb_iface_index {
struct usb_interface_descriptor* iface;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bInterfaceClass;
struct usb_endpoint_index eps[USB_MAX_EP_NUM];
int eps_num;
};
struct usb_device_index {
struct usb_device_descriptor* dev;
struct usb_config_descriptor* config;
uint8_t bDeviceClass;
uint8_t bMaxPower;
int config_length;
struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
int ifaces_num;
int iface_cur;
};
struct usb_info {
int fd;
struct usb_device_index index;
};
static struct usb_info usb_devices[USB_MAX_FDS];
static struct usb_device_index* lookup_usb_index(int fd)
{
for (int i = 0; i < USB_MAX_FDS; i++) {
if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd)
return &usb_devices[i].index;
}
return NULL;
}
static int usb_devices_num;
static bool parse_usb_descriptor(const char* buffer, size_t length, struct usb_device_index* index)
{
if (length < sizeof(*index->dev) + sizeof(*index->config))
return false;
memset(index, 0, sizeof(*index));
index->dev = (struct usb_device_descriptor*)buffer;
index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev));
index->bDeviceClass = index->dev->bDeviceClass;
index->bMaxPower = index->config->bMaxPower;
index->config_length = length - sizeof(*index->dev);
index->iface_cur = -1;
size_t offset = 0;
while (true) {
if (offset + 1 >= length)
break;
uint8_t desc_length = buffer[offset];
uint8_t desc_type = buffer[offset + 1];
if (desc_length <= 2)
break;
if (offset + desc_length > length)
break;
if (desc_type == USB_DT_INTERFACE && index->ifaces_num < USB_MAX_IFACE_NUM) {
struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset);
index->ifaces[index->ifaces_num].iface = iface;
index->ifaces[index->ifaces_num].bInterfaceNumber = iface->bInterfaceNumber;
index->ifaces[index->ifaces_num].bAlternateSetting = iface->bAlternateSetting;
index->ifaces[index->ifaces_num].bInterfaceClass = iface->bInterfaceClass;
index->ifaces_num++;
}
if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) {
struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1];
if (iface->eps_num < USB_MAX_EP_NUM) {
memcpy(&iface->eps[iface->eps_num].desc, buffer + offset, sizeof(iface->eps[iface->eps_num].desc));
iface->eps_num++;
}
}
offset += desc_length;
}
return true;
}
static struct usb_device_index* add_usb_index(int fd, const char* dev, size_t dev_len)
{
int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED);
if (i >= USB_MAX_FDS)
return NULL;
if (!parse_usb_descriptor(dev, dev_len, &usb_devices[i].index))
return NULL;
__atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE);
return &usb_devices[i].index;
}
struct vusb_connect_string_descriptor {
uint32_t len;
char* str;
} __attribute__((packed));
struct vusb_connect_descriptors {
uint32_t qual_len;
char* qual;
uint32_t bos_len;
char* bos;
uint32_t strs_len;
struct vusb_connect_string_descriptor strs[0];
} __attribute__((packed));
static const char default_string[] = {
8, USB_DT_STRING,
's', 0, 'y', 0, 'z', 0
};
static const char default_lang_id[] = {
4, USB_DT_STRING,
0x09, 0x04
};
static bool lookup_connect_response_in(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl,
struct usb_qualifier_descriptor* qual,
char** response_data, uint32_t* response_length)
{
struct usb_device_index* index = lookup_usb_index(fd);
uint8_t str_idx;
if (!index)
return false;
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
switch (ctrl->wValue >> 8) {
case USB_DT_DEVICE:
*response_data = (char*)index->dev;
*response_length = sizeof(*index->dev);
return true;
case USB_DT_CONFIG:
*response_data = (char*)index->config;
*response_length = index->config_length;
return true;
case USB_DT_STRING:
str_idx = (uint8_t)ctrl->wValue;
if (descs && str_idx < descs->strs_len) {
*response_data = descs->strs[str_idx].str;
*response_length = descs->strs[str_idx].len;
return true;
}
if (str_idx == 0) {
*response_data = (char*)&default_lang_id[0];
*response_length = default_lang_id[0];
return true;
}
*response_data = (char*)&default_string[0];
*response_length = default_string[0];
return true;
case USB_DT_BOS:
*response_data = descs->bos;
*response_length = descs->bos_len;
return true;
case USB_DT_DEVICE_QUALIFIER:
if (!descs->qual) {
qual->bLength = sizeof(*qual);
qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
qual->bcdUSB = index->dev->bcdUSB;
qual->bDeviceClass = index->dev->bDeviceClass;
qual->bDeviceSubClass = index->dev->bDeviceSubClass;
qual->bDeviceProtocol = index->dev->bDeviceProtocol;
qual->bMaxPacketSize0 = index->dev->bMaxPacketSize0;
qual->bNumConfigurations = index->dev->bNumConfigurations;
qual->bRESERVED = 0;
*response_data = (char*)qual;
*response_length = sizeof(*qual);
return true;
}
*response_data = descs->qual;
*response_length = descs->qual_len;
return true;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
return false;
}
typedef bool (*lookup_connect_out_response_t)(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done);
static bool lookup_connect_response_out_generic(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done)
{
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_SET_CONFIGURATION:
*done = true;
return true;
default:
break;
}
break;
}
return false;
}
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
__u8 driver_name[UDC_NAME_LENGTH_MAX];
__u8 device_name[UDC_NAME_LENGTH_MAX];
__u8 speed;
};
enum usb_raw_event_type {
USB_RAW_EVENT_INVALID = 0,
USB_RAW_EVENT_CONNECT = 1,
USB_RAW_EVENT_CONTROL = 2,
};
struct usb_raw_event {
__u32 type;
__u32 length;
__u8 data[0];
};
struct usb_raw_ep_io {
__u16 ep;
__u16 flags;
__u32 length;
__u8 data[0];
};
#define USB_RAW_EPS_NUM_MAX 30
#define USB_RAW_EP_NAME_MAX 16
#define USB_RAW_EP_ADDR_ANY 0xff
struct usb_raw_ep_caps {
__u32 type_control : 1;
__u32 type_iso : 1;
__u32 type_bulk : 1;
__u32 type_int : 1;
__u32 dir_in : 1;
__u32 dir_out : 1;
};
struct usb_raw_ep_limits {
__u16 maxpacket_limit;
__u16 max_streams;
__u32 reserved;
};
struct usb_raw_ep_info {
__u8 name[USB_RAW_EP_NAME_MAX];
__u32 addr;
struct usb_raw_ep_caps caps;
struct usb_raw_ep_limits limits;
};
struct usb_raw_eps_info {
struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
};
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN _IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
#define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
#define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32)
#define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32)
#define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32)
static int usb_raw_open()
{
return open("/dev/raw-gadget", O_RDWR);
}
static int usb_raw_init(int fd, uint32_t speed, const char* driver, const char* device)
{
struct usb_raw_init arg;
strncpy((char*)&arg.driver_name[0], driver, sizeof(arg.driver_name));
strncpy((char*)&arg.device_name[0], device, sizeof(arg.device_name));
arg.speed = speed;
return ioctl(fd, USB_RAW_IOCTL_INIT, &arg);
}
static int usb_raw_run(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_RUN, 0);
}
static int usb_raw_ep_write(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP_WRITE, io);
}
static int usb_raw_configure(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0);
}
static int usb_raw_vbus_draw(int fd, uint32_t power)
{
return ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, power);
}
static int usb_raw_ep0_write(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io);
}
static int usb_raw_ep0_read(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_READ, io);
}
static int usb_raw_event_fetch(int fd, struct usb_raw_event* event)
{
return ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event);
}
static int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor* desc)
{
return ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, desc);
}
static int usb_raw_ep_disable(int fd, int ep)
{
return ioctl(fd, USB_RAW_IOCTL_EP_DISABLE, ep);
}
static int usb_raw_ep0_stall(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
static int lookup_endpoint(int fd, uint8_t bEndpointAddress)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
if (index->iface_cur < 0)
return -1;
for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++)
if (index->ifaces[index->iface_cur].eps[ep].desc.bEndpointAddress == bEndpointAddress)
return index->ifaces[index->iface_cur].eps[ep].handle;
return -1;
}
#define USB_MAX_PACKET_SIZE 4096
struct usb_raw_control_event {
struct usb_raw_event inner;
struct usb_ctrlrequest ctrl;
char data[USB_MAX_PACKET_SIZE];
};
struct usb_raw_ep_io_data {
struct usb_raw_ep_io inner;
char data[USB_MAX_PACKET_SIZE];
};
static void set_interface(int fd, int n)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return;
if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) {
int rv = usb_raw_ep_disable(fd, index->ifaces[index->iface_cur].eps[ep].handle);
if (rv < 0) {
} else {
}
}
}
if (n >= 0 && n < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[n].eps_num; ep++) {
int rv = usb_raw_ep_enable(fd, &index->ifaces[n].eps[ep].desc);
if (rv < 0) {
} else {
index->ifaces[n].eps[ep].handle = rv;
}
}
index->iface_cur = n;
}
}
static int configure_device(int fd)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
int rv = usb_raw_vbus_draw(fd, index->bMaxPower);
if (rv < 0) {
return rv;
}
rv = usb_raw_configure(fd);
if (rv < 0) {
return rv;
}
set_interface(fd, 0);
return 0;
}
static volatile long syz_usb_connect_impl(uint64_t speed, uint64_t dev_len, const char* dev,
const struct vusb_connect_descriptors* descs,
lookup_connect_out_response_t lookup_connect_response_out)
{
if (!dev) {
return -1;
}
int fd = usb_raw_open();
if (fd < 0) {
return fd;
}
if (fd >= MAX_FDS) {
close(fd);
return -1;
}
struct usb_device_index* index = add_usb_index(fd, dev, dev_len);
if (!index) {
return -1;
}
char device[32];
sprintf(&device[0], "dummy_udc.%llu", procid);
int rv = usb_raw_init(fd, speed, "dummy_udc", &device[0]);
if (rv < 0) {
return rv;
}
rv = usb_raw_run(fd);
if (rv < 0) {
return rv;
}
bool done = false;
while (!done) {
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = sizeof(event.ctrl);
rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL)
continue;
char* response_data = NULL;
uint32_t response_length = 0;
struct usb_qualifier_descriptor qual;
if (event.ctrl.bRequestType & USB_DIR_IN) {
if (!lookup_connect_response_in(fd, descs, &event.ctrl, &qual, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
continue;
}
} else {
if (!lookup_connect_response_out(fd, descs, &event.ctrl, &done)) {
usb_raw_ep0_stall(fd);
continue;
}
response_data = NULL;
response_length = event.ctrl.wLength;
}
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
event.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
rv = configure_device(fd);
if (rv < 0) {
return rv;
}
}
struct usb_raw_ep_io_data response;
response.inner.ep = 0;
response.inner.flags = 0;
if (response_length > sizeof(response.data))
response_length = 0;
if (event.ctrl.wLength < response_length)
response_length = event.ctrl.wLength;
response.inner.length = response_length;
if (response_data)
memcpy(&response.data[0], response_data, response_length);
else
memset(&response.data[0], 0, response_length);
if (event.ctrl.bRequestType & USB_DIR_IN) {
rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
} else {
rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
}
if (rv < 0) {
return rv;
}
}
sleep_ms(200);
return fd;
}
static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
uint64_t speed = a0;
uint64_t dev_len = a1;
const char* dev = (const char*)a2;
const struct vusb_connect_descriptors* descs = (const struct vusb_connect_descriptors*)a3;
return syz_usb_connect_impl(speed, dev_len, dev, descs, &lookup_connect_response_out_generic);
}
static volatile long syz_usb_ep_write(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
int fd = a0;
uint8_t ep = a1;
uint32_t len = a2;
char* data = (char*)a3;
int ep_handle = lookup_endpoint(fd, ep);
if (ep_handle < 0) {
return -1;
}
struct usb_raw_ep_io_data io_data;
io_data.inner.ep = ep_handle;
io_data.inner.flags = 0;
if (len > sizeof(io_data.data))
len = sizeof(io_data.data);
io_data.inner.length = len;
memcpy(&io_data.data[0], data, len);
int rv = usb_raw_ep_write(fd, (struct usb_raw_ep_io*)&io_data);
if (rv < 0) {
return rv;
}
sleep_ms(200);
return 0;
}
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_usb_connect arguments: [
// speed: usb_device_speed = 0x2 (8 bytes)
// dev_len: len = 0x2d (8 bytes)
// dev: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {12 01 00 00 ea fa 72 40 93 69 01 b0 29 3d f4 00 10 01 09 02 1b 00 01 24 a8 00 80 09 04 00 00 01 03 00 00 00 09 05} (length 0x26)
// }
// union ANYUNION {
// ANYRES32: ANYRES32 (resource)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000000040, "... [truncated large byte array] ...", 38);
*(uint32_t*)0x200000000066 = -1;
syz_usb_connect(/*speed=USB_SPEED_FULL*/2, /*dev_len=*/0x2d, /*dev=*/0x200000000040, /*conn_descs=*/0);
// syz_usb_ep_write$ath9k_ep1 arguments: [
// fd: fd_usb_ath9k (resource)
// ep: const = 0x82 (1 bytes)
// len: bytesize = 0x1380 (8 bytes)
// data: nil
// ]
syz_usb_ep_write(/*fd=*/-1, /*ep=*/0x82, /*len=*/0x1380, /*data=*/0);
return 0;
}
Reply: Based on the analysis of the provided bug report and the kernel source code, here is the assessment of the issue:
**Root Cause Analysis:**
The bug report indicates an RCU task stall (`rcu_preempt detected stalls on CPUs/tasks: Tasks blocked on level-0 rcu_node`). This specific type of stall occurs when a task is preempted while inside an RCU read-side critical section and fails to resume and call `rcu_read_unlock()` within the RCU grace period timeout (typically 21 seconds).
Looking at the stack trace, the `kworker` thread was executing `usb_probe` for the `yealink` driver, which eventually called `kmalloc`. Because KASAN is enabled, `kmalloc` saves the stack trace (`kasan_save_stack` -> `stack_trace_save` -> `arch_stack_walk` -> `unwind_next_frame`).
Inside `unwind_next_frame`, the ORC unwinder uses `guard(rcu)()` to protect against module unloading, which internally calls `rcu_read_lock()` and `rcu_read_unlock()`.
The instruction pointer (`RIP: lock_acquire+0x5e/0x380`) points exactly to the `raw_local_irq_restore(flags)` call at the end of `lock_acquire` (which is called by lockdep during `rcu_read_lock`).
What happened is that the moment interrupts were re-enabled by `raw_local_irq_restore`, a pending timer interrupt fired (`asm_sysvec_apic_timer_interrupt`). Upon returning from this interrupt (`irqentry_exit` -> `preempt_schedule_irq`), the kernel decided to preempt the `kworker` task.
Because the task was preempted exactly after acquiring the RCU read lock but before releasing it, it was placed on the RCU node's blocked tasks list. The task state is shown as `state:R running task`, meaning it was placed on the runqueue but was starved of CPU time for over 21 seconds. This starvation is a known artifact of fuzzers like syzkaller, which often overload the system with heavy workloads or real-time (`SCHED_FIFO`) tasks on small VMs, preventing normal tasks from getting scheduled.
**Security Impact:**
There is no infinite loop, deadlock, or memory corruption in the `yealink` driver or the USB subsystem in this trace. The code paths involved (like `usb_probe`, `input_register_device`, and `cdev_add`) only contain small, bounded loops. The delay is purely a scheduling artifact caused by fuzzer-induced CPU starvation combined with the heavy overhead of KASAN and Lockdep.
In a production kernel (without KASAN/Lockdep and without malicious real-time tasks hogging the CPU), this code path executes extremely fast. Therefore, this is a false positive and does not represent a genuine vulnerability or a Denial-of-Service vector.
**Conclusion:**
- **Exploitable:** No
- **Denial of Service:** No (False positive due to fuzzer-induced CPU starvation)
- **Trigger Mechanisms:** None (Not a bug)
|
| 6/2 |
2026/05/30 05:57 |
llm |
expert |
0m
Model:
Tokens:
input: 14739
output: 37
thoughts: 829
Thoughts: **RCU Stall Investigation**
Okay, here's the deal. I've got this bug report, an RCU stall detected. The stack trace points to `lock_acquire` in `lockdep.c`, but that's just where the RCU stall *happened* to be detected, not the root cause. This trace shows a few things, though, namely we're in `rcu_read_lock`, which is called through a few levels and ultimately, through `usb_probe`. The trace shows: `unwind_next_frame` -> `class_rcu_constructor` -> `rcu_read_lock` -> `lock_acquire`. Not especially helpful on its own.
My immediate thought is that this stall isn't really an RCU issue *per se*. The real culprit is probably something blocking *within* that call to `usb_probe` in the Yealink driver (`drivers/input/misc/yealink.c`). It looks like the stall is happening *during* the probe.
So, instead of focusing on the RCU part, I need to dig into what `usb_probe` in that specific driver is doing. I need to check line 928, and the surrounding code, in `drivers/input/misc/yealink.c`. I'm guessing a loop, a lengthy operation without yielding, a deadlock, or some other long-running task is the actual issue. Let's see what's going on there.
|
| 7/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:usb_probe]
Results: map[SourceCode: 815: static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
816: {
817: struct usb_device *udev = interface_to_usbdev (intf);
818: struct driver_info *nfo = (struct driver_info *)id->driver_info;
819: struct usb_host_interface *interface;
820: struct usb_endpoint_descriptor *endpoint;
821: struct yealink_dev *yld;
822: struct input_dev *input_dev;
823: int ret, pipe, i;
824:
825: interface = intf->cur_altsetting;
826:
827: if (interface->desc.bNumEndpoints < 1)
828: return -ENODEV;
829:
830: endpoint = &interface->endpoint[0].desc;
831: if (!usb_endpoint_is_int_in(endpoint))
832: return -ENODEV;
833:
834: yld = kzalloc_obj(*yld);
835: if (!yld)
836: return -ENOMEM;
837:
838: yld->udev = udev;
839: yld->intf = intf;
840: mutex_init(&yld->sysfs_mutex);
841:
842: yld->idev = input_dev = input_allocate_device();
843: if (!input_dev)
844: return usb_cleanup(yld, -ENOMEM);
845:
846: /* allocate usb buffers */
847: yld->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
848: GFP_KERNEL, &yld->irq_dma);
849: if (yld->irq_data == NULL)
850: return usb_cleanup(yld, -ENOMEM);
851:
852: yld->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
853: GFP_KERNEL, &yld->ctl_dma);
854: if (!yld->ctl_data)
855: return usb_cleanup(yld, -ENOMEM);
856:
857: yld->ctl_req = kmalloc_obj(*(yld->ctl_req));
858: if (yld->ctl_req == NULL)
859: return usb_cleanup(yld, -ENOMEM);
860:
861: /* allocate urb structures */
862: yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
863: if (yld->urb_irq == NULL)
864: return usb_cleanup(yld, -ENOMEM);
865:
866: yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
867: if (yld->urb_ctl == NULL)
868: return usb_cleanup(yld, -ENOMEM);
869:
870: /* get a handle to the interrupt data pipe */
871: pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
872: ret = usb_maxpacket(udev, pipe);
873: if (ret != USB_PKT_LEN)
874: dev_err(&intf->dev, "invalid payload size %d, expected %zd\n",
875: ret, USB_PKT_LEN);
876:
877: /* initialise irq urb */
878: usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
879: USB_PKT_LEN,
880: urb_irq_callback,
881: yld, endpoint->bInterval);
882: yld->urb_irq->transfer_dma = yld->irq_dma;
883: yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
884: yld->urb_irq->dev = udev;
885:
886: /* initialise ctl urb */
887: yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
888: USB_DIR_OUT;
889: yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
890: yld->ctl_req->wValue = cpu_to_le16(0x200);
891: yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
892: yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
893:
894: usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
895: (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
896: urb_ctl_callback, yld);
897: yld->urb_ctl->transfer_dma = yld->ctl_dma;
898: yld->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
899: yld->urb_ctl->dev = udev;
900:
901: /* find out the physical bus location */
902: usb_make_path(udev, yld->phys, sizeof(yld->phys));
903: strlcat(yld->phys, "/input0", sizeof(yld->phys));
904:
905: /* register settings for the input device */
906: input_dev->name = nfo->name;
907: input_dev->phys = yld->phys;
908: usb_to_input_id(udev, &input_dev->id);
909: input_dev->dev.parent = &intf->dev;
910:
911: input_set_drvdata(input_dev, yld);
912:
913: input_dev->open = input_open;
914: input_dev->close = input_close;
915: /* input_dev->event = input_ev; TODO */
916:
917: /* register available key events */
918: input_dev->evbit[0] = BIT_MASK(EV_KEY);
919: for (i = 0; i < 256; i++) {
920: int k = map_p1k_to_key(i);
921: if (k >= 0) {
922: set_bit(k & 0xff, input_dev->keybit);
923: if (k >> 8)
924: set_bit(k >> 8, input_dev->keybit);
925: }
926: }
927:
928: ret = input_register_device(yld->idev);
929: if (ret)
930: return usb_cleanup(yld, ret);
931:
932: usb_set_intfdata(intf, yld);
933:
934: /* clear visible elements */
935: for (i = 0; i < ARRAY_SIZE(lcdMap); i++)
936: setChar(yld, i, ' ');
937:
938: /* display driver version on LCD line 3 */
939: store_line3(&intf->dev, NULL,
940: DRIVER_VERSION, sizeof(DRIVER_VERSION));
941:
942: return 0;
943: }
SourceFile:drivers/input/misc/yealink.c]
|
| 8/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 18144
output: 43
thoughts: 176
Thoughts: **Debugging a USB Input Device Registration Stall**
Okay, so I'm hitting a stall, specifically in `usb_probe` at line 928. The troublesome line is `ret = input_register_device(yld->idev);`. I need to figure out *why* this is hanging. Before that call, I see this loop:
```c
for (i = 0; i < 256; i++) {
int k = map_p1k_to_key(i);
if (k >= 0) {
set_bit(k & 0xff, input_dev->keybit);
if (k >> 8)
set_bit(k >> 8, input_dev->keybit);
}
}
```
The thing that immediately jumps out is `map_p1k_to_key(i)`. Is *that* the bottleneck? Is there a tight loop or a slow operation inside of `map_p1k_to_key`? This is the first place to look. I need to dive into `map_p1k_to_key` and see what it's doing.
|
| 9/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:map_p1k_to_key]
Results: map[SourceCode: 174: /* Map device buttons to internal key events.
175: *
176: * USB-P1K button layout:
177: *
178: * up
179: * IN OUT
180: * down
181: *
182: * pickup C hangup
183: * 1 2 3
184: * 4 5 6
185: * 7 8 9
186: * * 0 #
187: *
188: * The "up" and "down" keys, are symbolised by arrows on the button.
189: * The "pickup" and "hangup" keys are symbolised by a green and red phone
190: * on the button.
191: */
192: static int map_p1k_to_key(int scancode)
193: {
194: switch(scancode) { /* phone key: */
195: case 0x23: return KEY_LEFT; /* IN */
196: case 0x33: return KEY_UP; /* up */
197: case 0x04: return KEY_RIGHT; /* OUT */
198: case 0x24: return KEY_DOWN; /* down */
199: case 0x03: return KEY_ENTER; /* pickup */
200: case 0x14: return KEY_BACKSPACE; /* C */
201: case 0x13: return KEY_ESC; /* hangup */
202: case 0x00: return KEY_1; /* 1 */
203: case 0x01: return KEY_2; /* 2 */
204: case 0x02: return KEY_3; /* 3 */
205: case 0x10: return KEY_4; /* 4 */
206: case 0x11: return KEY_5; /* 5 */
207: case 0x12: return KEY_6; /* 6 */
208: case 0x20: return KEY_7; /* 7 */
209: case 0x21: return KEY_8; /* 8 */
210: case 0x22: return KEY_9; /* 9 */
211: case 0x30: return KEY_KPASTERISK; /* * */
212: case 0x31: return KEY_0; /* 0 */
213: case 0x32: return KEY_LEFTSHIFT |
214: KEY_3 << 8; /* # */
215: }
216: return -EINVAL;
217: }
SourceFile:drivers/input/misc/yealink.c]
|
| 10/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 19476
output: 36
thoughts: 59
Thoughts: Okay, let's see...
**Analyzing the USB Input Device Registration**
Right, `map_p1k_to_key`... that's just a glorified switch statement. So it's not the bottleneck. The loop inside `usb_probe`? It should be relatively quick, then. The system is probably designed to optimize it. Okay, moving on... the key function here seems to be `input_register_device`. Let's dig into that. I need to understand what's happening at this crucial registration step. This is where the magic happens, or where things could go sideways. I need to know how the device is integrated into the input subsystem from here. What resources are being allocated? How are the event handlers set up? Time to dive in.
|
| 11/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/input.c Name:input_register_device]
Results: map[SourceCode:2289: /**
2290: * input_register_device - register device with input core
2291: * @dev: device to be registered
2292: *
2293: * This function registers device with input core. The device must be
2294: * allocated with input_allocate_device() and all it's capabilities
2295: * set up before registering.
2296: * If function fails the device must be freed with input_free_device().
2297: * Once device has been successfully registered it can be unregistered
2298: * with input_unregister_device(); input_free_device() should not be
2299: * called in this case.
2300: *
2301: * Note that this function is also used to register managed input devices
2302: * (ones allocated with devm_input_allocate_device()). Such managed input
2303: * devices need not be explicitly unregistered or freed, their tear down
2304: * is controlled by the devres infrastructure. It is also worth noting
2305: * that tear down of managed input devices is internally a 2-step process:
2306: * registered managed input device is first unregistered, but stays in
2307: * memory and can still handle input_event() calls (although events will
2308: * not be delivered anywhere). The freeing of managed input device will
2309: * happen later, when devres stack is unwound to the point where device
2310: * allocation was made.
2311: */
2312: int input_register_device(struct input_dev *dev)
2313: {
2314: struct input_devres *devres = NULL;
2315: struct input_handler *handler;
2316: const char *path;
2317: int error;
2318:
2319: if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
2320: dev_err(&dev->dev,
2321: "Absolute device without dev->absinfo, refusing to register\n");
2322: return -EINVAL;
2323: }
2324:
2325: if (dev->devres_managed) {
2326: devres = devres_alloc(devm_input_device_unregister,
2327: sizeof(*devres), GFP_KERNEL);
2328: if (!devres)
2329: return -ENOMEM;
2330:
2331: devres->input = dev;
2332: }
2333:
2334: /* Every input device generates EV_SYN/SYN_REPORT events. */
2335: __set_bit(EV_SYN, dev->evbit);
2336:
2337: /* KEY_RESERVED is not supposed to be transmitted to userspace. */
2338: __clear_bit(KEY_RESERVED, dev->keybit);
2339:
2340: /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
2341: input_cleanse_bitmasks(dev);
2342:
2343: error = input_device_tune_vals(dev);
2344: if (error)
2345: goto err_devres_free;
2346:
2347: /*
2348: * If delay and period are pre-set by the driver, then autorepeating
2349: * is handled by the driver itself and we don't do it in input.c.
2350: */
2351: if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
2352: input_enable_softrepeat(dev, 250, 33);
2353:
2354: if (!dev->getkeycode)
2355: dev->getkeycode = input_default_getkeycode;
2356:
2357: if (!dev->setkeycode)
2358: dev->setkeycode = input_default_setkeycode;
2359:
2360: if (dev->poller)
2361: input_dev_poller_finalize(dev->poller);
2362:
2363: error = device_add(&dev->dev);
2364: if (error)
2365: goto err_devres_free;
2366:
2367: path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
2368: pr_info("%s as %s\n",
2369: dev->name ? dev->name : "Unspecified device",
2370: path ? path : "N/A");
2371: kfree(path);
2372:
2373: error = -EINTR;
2374: scoped_cond_guard(mutex_intr, goto err_device_del, &input_mutex) {
2375: list_add_tail(&dev->node, &input_dev_list);
2376:
2377: list_for_each_entry(handler, &input_handler_list, node)
2378: input_attach_handler(dev, handler);
2379:
2380: input_wakeup_procfs_readers();
2381: }
2382:
2383: if (dev->devres_managed) {
2384: dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
2385: __func__, dev_name(&dev->dev));
2386: devres_add(dev->dev.parent, devres);
2387: }
2388: return 0;
2389:
2390: err_device_del:
2391: device_del(&dev->dev);
2392: err_devres_free:
2393: devres_free(devres);
2394: return error;
2395: }
SourceFile:drivers/input/input.c]
|
| 12/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 21414
output: 36
thoughts: 209
Thoughts: **Analyzing the Kernel Stack Trace**
Okay, so I'm looking at this stack trace, and my initial thought is, "Right, let's break this down." The kernel's throwing a fit somewhere, and this trace is giving me a breadcrumb trail. Specifically, it points to a problem within the device driver, and it looks like it's within the `evdev` subsystem.
The trace suggests the error originates in `kobj_map`, which often deals with object mapping within the kernel's object model. Then there is an issue adding the device. That makes sense, `cdev_add`, `cdev_device_add` β these are all functions related to creating and managing character devices, the standard way to hook up devices in a *nix system.
Now, we hit the real crux: `evdev_connect`. This is where the virtual event device is getting its hooks in. Then we see `input_attach_handler`, `input_register_device`. This is the critical linkage between the driver and the input subsystem. So, my gut tells me the issue is almost certainly within `evdev_connect` itself. That seems to be the crucial entry point, the one that's likely the cause of the problem. Time to dive into that function and see what's going on.
|
| 13/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/evdev.c Name:evdev_connect]
Results: map[SourceCode:1330: /*
1331: * Create new evdev device. Note that input core serializes calls
1332: * to connect and disconnect.
1333: */
1334: static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
1335: const struct input_device_id *id)
1336: {
1337: struct evdev *evdev;
1338: int minor;
1339: int dev_no;
1340: int error;
1341:
1342: minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
1343: if (minor < 0) {
1344: error = minor;
1345: pr_err("failed to reserve new minor: %d\n", error);
1346: return error;
1347: }
1348:
1349: evdev = kzalloc_obj(struct evdev);
1350: if (!evdev) {
1351: error = -ENOMEM;
1352: goto err_free_minor;
1353: }
1354:
1355: INIT_LIST_HEAD(&evdev->client_list);
1356: spin_lock_init(&evdev->client_lock);
1357: mutex_init(&evdev->mutex);
1358: evdev->exist = true;
1359:
1360: dev_no = minor;
1361: /* Normalize device number if it falls into legacy range */
1362: if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
1363: dev_no -= EVDEV_MINOR_BASE;
1364: dev_set_name(&evdev->dev, "event%d", dev_no);
1365:
1366: evdev->handle.dev = input_get_device(dev);
1367: evdev->handle.name = dev_name(&evdev->dev);
1368: evdev->handle.handler = handler;
1369: evdev->handle.private = evdev;
1370:
1371: evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
1372: evdev->dev.class = &input_class;
1373: evdev->dev.parent = &dev->dev;
1374: evdev->dev.release = evdev_free;
1375: device_initialize(&evdev->dev);
1376:
1377: error = input_register_handle(&evdev->handle);
1378: if (error)
1379: goto err_free_evdev;
1380:
1381: cdev_init(&evdev->cdev, &evdev_fops);
1382:
1383: error = cdev_device_add(&evdev->cdev, &evdev->dev);
1384: if (error)
1385: goto err_cleanup_evdev;
1386:
1387: return 0;
1388:
1389: err_cleanup_evdev:
1390: evdev_cleanup(evdev);
1391: input_unregister_handle(&evdev->handle);
1392: err_free_evdev:
1393: put_device(&evdev->dev);
1394: err_free_minor:
1395: input_free_minor(minor);
1396: return error;
1397: }
SourceFile:drivers/input/evdev.c]
|
| 14/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 22969
output: 35
thoughts: 112
Thoughts: **Analyzing the Kernel Object Mapping**
Okay, so the stack trace is pointing directly at a kernel object issue. Specifically, I'm seeing `kobj_map` in `drivers/base/map.c` is being called at a certain offset. Following that, I see `cdev_add` in `fs/char_dev.c` called, which is expected for character device creation, and subsequently `cdev_device_add` β also in `fs/char_dev.c`. This suggests a potential problem with how a character device is being linked to a kernel object within the `kobj_map`. My immediate next step will be to delve into `kobj_map` in `drivers/base/map.c` to see precisely what's happening at line 44. I need to understand what's going wrong in the mapping process that's leading to this error condition, likely related to how the character device is being integrated.
|
| 15/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/map.c Name:kobj_map]
Results: map[SourceCode: 32: int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
33: struct module *module, kobj_probe_t *probe,
34: int (*lock)(dev_t, void *), void *data)
35: {
36: unsigned int n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
37: unsigned int index = MAJOR(dev);
38: unsigned int i;
39: struct probe *p;
40:
41: if (n > 255)
42: n = 255;
43:
44: p = kmalloc_objs(struct probe, n);
45: if (p == NULL)
46: return -ENOMEM;
47:
48: for (i = 0; i < n; i++, p++) {
49: p->owner = module;
50: p->get = probe;
51: p->lock = lock;
52: p->dev = dev;
53: p->range = range;
54: p->data = data;
55: }
56: mutex_lock(domain->lock);
57: for (i = 0, p -= n; i < n; i++, p++, index++) {
58: struct probe **s = &domain->probes[index % 255];
59: while (*s && (*s)->range < range)
60: s = &(*s)->next;
61: p->next = *s;
62: *s = p;
63: }
64: mutex_unlock(domain->lock);
65: return 0;
66: }
SourceFile:drivers/base/map.c]
|
| 16/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 23839
output: 36
thoughts: 222
Thoughts: **Analyzing a Potential RCU Stall in USB Device Creation**
Okay, so I'm looking at this stack trace and immediately see that `kobj_map` is allocating memory. Specifically, it's calling `kmalloc_objs` which then calls `__kmalloc_noprof`. The KASAN output and stack trace are screaming memory allocation right here.
But the real issue is the RCU stall I'm seeing. This is probably related to syzkaller's reproducer generating a ton of USB devices. This suggests `hub_event` is involved, and might be the bottleneck. The volume of those events is, well, *huge* during testing. Let's dig into `hub_event` to see what's happening.
|
| 17/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hub.c Name:hub_event]
Results: map[SourceCode:5874: static void hub_event(struct work_struct *work)
5875: {
5876: struct usb_device *hdev;
5877: struct usb_interface *intf;
5878: struct usb_hub *hub;
5879: struct device *hub_dev;
5880: u16 hubstatus;
5881: u16 hubchange;
5882: int i, ret;
5883:
5884: hub = container_of(work, struct usb_hub, events);
5885: hdev = hub->hdev;
5886: hub_dev = hub->intfdev;
5887: intf = to_usb_interface(hub_dev);
5888:
5889: kcov_remote_start_usb((u64)hdev->bus->busnum);
5890:
5891: dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
5892: hdev->state, hdev->maxchild,
5893: /* NOTE: expects max 15 ports... */
5894: (u16) hub->change_bits[0],
5895: (u16) hub->event_bits[0]);
5896:
5897: /* Lock the device, then check to see if we were
5898: * disconnected while waiting for the lock to succeed. */
5899: usb_lock_device(hdev);
5900: if (unlikely(hub->disconnected))
5901: goto out_hdev_lock;
5902:
5903: /* If the hub has died, clean up after it */
5904: if (hdev->state == USB_STATE_NOTATTACHED) {
5905: hub->error = -ENODEV;
5906: hub_quiesce(hub, HUB_DISCONNECT);
5907: goto out_hdev_lock;
5908: }
5909:
5910: /* Autoresume */
5911: ret = usb_autopm_get_interface(intf);
5912: if (ret) {
5913: dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
5914: goto out_hdev_lock;
5915: }
5916:
5917: /* If this is an inactive hub, do nothing */
5918: if (hub->quiescing)
5919: goto out_autopm;
5920:
5921: if (hub->error) {
5922: dev_dbg(hub_dev, "resetting for error %d\n", hub->error);
5923:
5924: ret = usb_reset_device(hdev);
5925: if (ret) {
5926: dev_dbg(hub_dev, "error resetting hub: %d\n", ret);
5927: goto out_autopm;
5928: }
5929:
5930: hub->nerrors = 0;
5931: hub->error = 0;
5932: }
5933:
5934: /* deal with port status changes */
5935: for (i = 1; i <= hdev->maxchild; i++) {
5936: struct usb_port *port_dev = hub->ports[i - 1];
5937:
5938: if (test_bit(i, hub->event_bits)
5939: || test_bit(i, hub->change_bits)
5940: || test_bit(i, hub->wakeup_bits)) {
5941: /*
5942: * The get_noresume and barrier ensure that if
5943: * the port was in the process of resuming, we
5944: * flush that work and keep the port active for
5945: * the duration of the port_event(). However,
5946: * if the port is runtime pm suspended
5947: * (powered-off), we leave it in that state, run
5948: * an abbreviated port_event(), and move on.
5949: */
5950: pm_runtime_get_noresume(&port_dev->dev);
5951: pm_runtime_barrier(&port_dev->dev);
5952: usb_lock_port(port_dev);
5953: port_event(hub, i);
5954: usb_unlock_port(port_dev);
5955: pm_runtime_put_sync(&port_dev->dev);
5956: }
5957: }
5958:
5959: /* deal with hub status changes */
5960: if (test_and_clear_bit(0, hub->event_bits) == 0)
5961: ; /* do nothing */
5962: else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
5963: dev_err(hub_dev, "get_hub_status failed\n");
5964: else {
5965: if (hubchange & HUB_CHANGE_LOCAL_POWER) {
5966: dev_dbg(hub_dev, "power change\n");
5967: clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
5968: if (hubstatus & HUB_STATUS_LOCAL_POWER)
5969: /* FIXME: Is this always true? */
5970: hub->limited_power = 1;
5971: else
5972: hub->limited_power = 0;
5973: }
5974: if (hubchange & HUB_CHANGE_OVERCURRENT) {
5975: u16 status = 0;
5976: u16 unused;
5977:
5978: dev_dbg(hub_dev, "over-current change\n");
5979: clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
5980: msleep(500); /* Cool down */
5981: hub_power_on(hub, true);
5982: hub_hub_status(hub, &status, &unused);
5983: if (status & HUB_STATUS_OVERCURRENT)
5984: dev_err(hub_dev, "over-current condition\n");
5985: }
5986: }
5987:
5988: out_autopm:
5989: /* Balance the usb_autopm_get_interface() above */
5990: usb_autopm_put_interface_no_suspend(intf);
5991: out_hdev_lock:
5992: usb_unlock_device(hdev);
5993:
5994: /* Balance the stuff in kick_hub_wq() and allow autosuspend */
5995: usb_autopm_put_interface(intf);
5996: hub_put(hub);
5997:
5998: kcov_remote_stop();
5999: }
SourceFile:drivers/usb/core/hub.c]
|
| 18/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 26270
output: 38
thoughts: 134
Thoughts: **Analyzing the USB Hub Connection**
Okay, here's what I'm seeing: The stack trace clearly pinpoints the issue within the USB core, specifically around hub connection events. My initial focus is on this sequence: `hub_port_connect`, then `hub_port_connect_change`, followed by `port_event`, and finally culminating in `hub_event`. Seeing `hub_port_connect` called, that's where I need to start digging. This is a very typical USB debugging scenario. Let me jump into the code for `hub_port_connect` and see what's going on within that function. I have a hunch it is not doing what it is supposed to.
|
| 19/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hub.c Name:hub_port_connect]
Results: map[SourceCode:5390: static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
5391: u16 portchange)
5392: {
5393: int status = -ENODEV;
5394: int i;
5395: unsigned unit_load;
5396: struct usb_device *hdev = hub->hdev;
5397: struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
5398: struct usb_port *port_dev = hub->ports[port1 - 1];
5399: struct usb_device *udev = port_dev->child;
5400: static int unreliable_port = -1;
5401: bool retry_locked;
5402:
5403: /* Disconnect any existing devices under this port */
5404: if (udev) {
5405: if (hcd->usb_phy && !hdev->parent)
5406: usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
5407: usb_disconnect(&port_dev->child);
5408: }
5409:
5410: /* We can forget about a "removed" device when there's a physical
5411: * disconnect or the connect status changes.
5412: */
5413: if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
5414: (portchange & USB_PORT_STAT_C_CONNECTION))
5415: clear_bit(port1, hub->removed_bits);
5416:
5417: if (portchange & (USB_PORT_STAT_C_CONNECTION |
5418: USB_PORT_STAT_C_ENABLE)) {
5419: status = hub_port_debounce_be_stable(hub, port1);
5420: if (status < 0) {
5421: if (status != -ENODEV &&
5422: port1 != unreliable_port &&
5423: printk_ratelimit())
5424: dev_err(&port_dev->dev, "connect-debounce failed\n");
5425: portstatus &= ~USB_PORT_STAT_CONNECTION;
5426: unreliable_port = port1;
5427: } else {
5428: portstatus = status;
5429: }
5430: }
5431:
5432: /* Return now if debouncing failed or nothing is connected or
5433: * the device was "removed".
5434: */
5435: if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
5436: test_bit(port1, hub->removed_bits)) {
5437:
5438: /*
5439: * maybe switch power back on (e.g. root hub was reset)
5440: * but only if the port isn't owned by someone else.
5441: */
5442: if (hub_is_port_power_switchable(hub)
5443: && !usb_port_is_power_on(hub, portstatus)
5444: && !port_dev->port_owner)
5445: set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
5446:
5447: if (portstatus & USB_PORT_STAT_ENABLE)
5448: goto done;
5449: return;
5450: }
5451: if (hub_is_superspeed(hub->hdev))
5452: unit_load = 150;
5453: else
5454: unit_load = 100;
5455:
5456: status = 0;
5457:
5458: for (i = 0; i < PORT_INIT_TRIES; i++) {
5459: if (hub_port_stop_enumerate(hub, port1, i)) {
5460: status = -ENODEV;
5461: break;
5462: }
5463:
5464: usb_lock_port(port_dev);
5465: mutex_lock(hcd->address0_mutex);
5466: retry_locked = true;
5467: /* reallocate for each attempt, since references
5468: * to the previous one can escape in various ways
5469: */
5470: udev = usb_alloc_dev(hdev, hdev->bus, port1);
5471: if (!udev) {
5472: dev_err(&port_dev->dev,
5473: "couldn't allocate usb_device\n");
5474: mutex_unlock(hcd->address0_mutex);
5475: usb_unlock_port(port_dev);
5476: goto done;
5477: }
5478:
5479: usb_set_device_state(udev, USB_STATE_POWERED);
5480: udev->bus_mA = hub->mA_per_port;
5481: udev->level = hdev->level + 1;
5482:
5483: /* Devices connected to SuperSpeed hubs are USB 3.0 or later */
5484: if (hub_is_superspeed(hub->hdev))
5485: udev->speed = USB_SPEED_SUPER;
5486: else
5487: udev->speed = USB_SPEED_UNKNOWN;
5488:
5489: choose_devnum(udev);
5490: if (udev->devnum <= 0) {
5491: status = -ENOTCONN; /* Don't retry */
5492: goto loop;
5493: }
5494:
5495: /* reset (non-USB 3.0 devices) and get descriptor */
5496: status = hub_port_init(hub, udev, port1, i, NULL);
5497: if (status < 0)
5498: goto loop;
5499:
5500: mutex_unlock(hcd->address0_mutex);
5501: usb_unlock_port(port_dev);
5502: retry_locked = false;
5503:
5504: if (udev->quirks & USB_QUIRK_DELAY_INIT)
5505: msleep(2000);
5506:
5507: /* consecutive bus-powered hubs aren't reliable; they can
5508: * violate the voltage drop budget. if the new child has
5509: * a "powered" LED, users should notice we didn't enable it
5510: * (without reading syslog), even without per-port LEDs
5511: * on the parent.
5512: */
5513: if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
5514: && udev->bus_mA <= unit_load) {
5515: u16 devstat;
5516:
5517: status = usb_get_std_status(udev, USB_RECIP_DEVICE, 0,
5518: &devstat);
5519: if (status) {
5520: dev_dbg(&udev->dev, "get status %d ?\n", status);
5521: goto loop_disable;
5522: }
5523: if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
5524: dev_err(&udev->dev,
5525: "can't connect bus-powered hub "
5526: "to this port\n");
5527: if (hub->has_indicators) {
5528: hub->indicator[port1-1] =
5529: INDICATOR_AMBER_BLINK;
5530: queue_delayed_work(
5531: system_power_efficient_wq,
5532: &hub->leds, 0);
5533: }
5534: status = -ENOTCONN; /* Don't retry */
5535: goto loop_disable;
5536: }
5537: }
5538:
5539: /* check for devices running slower than they could */
5540: if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
5541: && udev->speed == USB_SPEED_FULL
5542: && highspeed_hubs != 0)
5543: check_highspeed(hub, udev, port1);
5544:
5545: /* Store the parent's children[] pointer. At this point
5546: * udev becomes globally accessible, although presumably
5547: * no one will look at it until hdev is unlocked.
5548: */
5549: status = 0;
5550:
5551: mutex_lock(&usb_port_peer_mutex);
5552:
5553: /* We mustn't add new devices if the parent hub has
5554: * been disconnected; we would race with the
5555: * recursively_mark_NOTATTACHED() routine.
5556: */
5557: spin_lock_irq(&device_state_lock);
5558: if (hdev->state == USB_STATE_NOTATTACHED)
5559: status = -ENOTCONN;
5560: else
5561: port_dev->child = udev;
5562: spin_unlock_irq(&device_state_lock);
5563: mutex_unlock(&usb_port_peer_mutex);
5564:
5565: /* Run it through the hoops (find a driver, etc) */
5566: if (!status) {
5567: status = usb_new_device(udev);
5568: if (status) {
5569: mutex_lock(&usb_port_peer_mutex);
5570: spin_lock_irq(&device_state_lock);
5571: port_dev->child = NULL;
5572: spin_unlock_irq(&device_state_lock);
5573: mutex_unlock(&usb_port_peer_mutex);
5574: } else {
5575: if (hcd->usb_phy && !hdev->parent)
5576: usb_phy_notify_connect(hcd->usb_phy,
5577: udev->speed);
5578: }
5579: }
5580:
5581: if (status)
5582: goto loop_disable;
5583:
5584: status = hub_power_remaining(hub);
5585: if (status)
5586: dev_dbg(hub->intfdev, "%dmA power budget left\n", status);
5587:
5588: return;
5589:
5590: loop_disable:
5591: hub_port_disable(hub, port1, 1);
5592: loop:
5593: usb_ep0_reinit(udev);
5594: release_devnum(udev);
5595: hub_free_dev(udev);
5596: if (retry_locked) {
5597: mutex_unlock(hcd->address0_mutex);
5598: usb_unlock_port(port_dev);
5599: }
5600: usb_put_dev(udev);
5601: if ((status == -ENOTCONN) || (status == -ENOTSUPP))
5602: break;
5603:
5604: /* When halfway through our retry count, power-cycle the port */
5605: if (i == (PORT_INIT_TRIES - 1) / 2) {
5606: dev_info(&port_dev->dev, "attempt power cycle\n");
5607: usb_hub_set_port_power(hdev, hub, port1, false);
5608: msleep(2 * hub_power_on_good_delay(hub));
5609: usb_hub_set_port_power(hdev, hub, port1, true);
5610: msleep(hub_power_on_good_delay(hub));
5611: }
5612: }
5613: if (hub->hdev->parent ||
5614: !hcd->driver->port_handed_over ||
5615: !(hcd->driver->port_handed_over)(hcd, port1)) {
5616: if (status != -ENOTCONN && status != -ENODEV)
5617: dev_err(&port_dev->dev,
5618: "unable to enumerate USB device\n");
5619: }
5620:
5621: done:
5622: hub_port_disable(hub, port1, 1);
5623: if (hcd->driver->relinquish_port && !hub->hdev->parent) {
5624: if (status != -ENOTCONN && status != -ENODEV)
5625: hcd->driver->relinquish_port(hcd, port1);
5626: }
5627: }
SourceFile:drivers/usb/core/hub.c]
|
| 20/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 30313
output: 38
thoughts: 92
Thoughts: **Analyzing the USB Device Connection Process**
Okay, so the stack trace points directly to `usb_new_device`. That's a good starting point. It appears the problem originated within that function. I need to understand what's happening within `usb_new_device` to determine the root cause. The trace then reveals `hub_port_connect`. This is intriguing β `hub_port_connect` is called by `usb_new_device`, and further suggests the issue involves a USB hub connection. Let's delve into the code to see where the problem originates.
|
| 21/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hub.c Name:usb_new_device]
Results: map[SourceCode:2621: /**
2622: * usb_new_device - perform initial device setup (usbcore-internal)
2623: * @udev: newly addressed device (in ADDRESS state)
2624: *
2625: * This is called with devices which have been detected but not fully
2626: * enumerated. The device descriptor is available, but not descriptors
2627: * for any device configuration. The caller must have locked either
2628: * the parent hub (if udev is a normal device) or else the
2629: * usb_bus_idr_lock (if udev is a root hub). The parent's pointer to
2630: * udev has already been installed, but udev is not yet visible through
2631: * sysfs or other filesystem code.
2632: *
2633: * This call is synchronous, and may not be used in an interrupt context.
2634: *
2635: * Only the hub driver or root-hub registrar should ever call this.
2636: *
2637: * Return: Whether the device is configured properly or not. Zero if the
2638: * interface was registered with the driver core; else a negative errno
2639: * value.
2640: *
2641: */
2642: int usb_new_device(struct usb_device *udev)
2643: {
2644: int err;
2645:
2646: if (udev->parent) {
2647: /* Initialize non-root-hub device wakeup to disabled;
2648: * device (un)configuration controls wakeup capable
2649: * sysfs power/wakeup controls wakeup enabled/disabled
2650: */
2651: device_init_wakeup(&udev->dev, 0);
2652: }
2653:
2654: /* Tell the runtime-PM framework the device is active */
2655: pm_runtime_set_active(&udev->dev);
2656: pm_runtime_get_noresume(&udev->dev);
2657: pm_runtime_use_autosuspend(&udev->dev);
2658: pm_runtime_enable(&udev->dev);
2659:
2660: /* By default, forbid autosuspend for all devices. It will be
2661: * allowed for hubs during binding.
2662: */
2663: usb_disable_autosuspend(udev);
2664:
2665: err = usb_enumerate_device(udev); /* Read descriptors */
2666: if (err < 0)
2667: goto fail;
2668: dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
2669: udev->devnum, udev->bus->busnum,
2670: (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
2671: /* export the usbdev device-node for libusb */
2672: udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
2673: (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
2674:
2675: /* Tell the world! */
2676: announce_device(udev);
2677:
2678: if (udev->serial)
2679: add_device_randomness(udev->serial, strlen(udev->serial));
2680: if (udev->product)
2681: add_device_randomness(udev->product, strlen(udev->product));
2682: if (udev->manufacturer)
2683: add_device_randomness(udev->manufacturer,
2684: strlen(udev->manufacturer));
2685:
2686: device_enable_async_suspend(&udev->dev);
2687:
2688: /* check whether the hub or firmware marks this port as non-removable */
2689: set_usb_port_removable(udev);
2690:
2691: /* Register the device. The device driver is responsible
2692: * for configuring the device and invoking the add-device
2693: * notifier chain (used by usbfs and possibly others).
2694: */
2695: err = device_add(&udev->dev);
2696: if (err) {
2697: dev_err(&udev->dev, "can't device_add, error %d\n", err);
2698: goto fail;
2699: }
2700:
2701: /* Create link files between child device and usb port device. */
2702: if (udev->parent) {
2703: struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
2704: int port1 = udev->portnum;
2705: struct usb_port *port_dev = hub->ports[port1 - 1];
2706:
2707: err = sysfs_create_link(&udev->dev.kobj,
2708: &port_dev->dev.kobj, "port");
2709: if (err)
2710: goto out_del_dev;
2711:
2712: err = sysfs_create_link(&port_dev->dev.kobj,
2713: &udev->dev.kobj, "device");
2714: if (err) {
2715: sysfs_remove_link(&udev->dev.kobj, "port");
2716: goto out_del_dev;
2717: }
2718:
2719: if (!test_and_set_bit(port1, hub->child_usage_bits))
2720: pm_runtime_get_sync(&port_dev->dev);
2721:
2722: typec_attach(port_dev->connector, &udev->dev);
2723: }
2724:
2725: (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
2726: usb_mark_last_busy(udev);
2727: pm_runtime_put_sync_autosuspend(&udev->dev);
2728: return err;
2729:
2730: out_del_dev:
2731: device_del(&udev->dev);
2732: fail:
2733: usb_set_device_state(udev, USB_STATE_NOTATTACHED);
2734: pm_runtime_disable(&udev->dev);
2735: pm_runtime_set_suspended(&udev->dev);
2736: return err;
2737: }
SourceFile:drivers/usb/core/hub.c]
|
| 22/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 32462
output: 35
thoughts: 820
Thoughts: **Analyzing the Recursive USB Device Addition**
Okay, so looking at this stack trace, it's pretty clear we've got a recursive problem. `device_add` is the culprit. We see it called from `usb_new_device`, which makes sense. But then it gets really interesting: the stack winds through `bus_probe_device`, `device_initial_probe`, `__device_attach`, all the way to `usb_probe_device` and ultimately `usb_probe` within `yealink.c`. And then, *bam*, it's back to `device_add` again. That looks suspicious, this stack is like a Mobius strip.
I quickly ruled out a loop in `yealink.c`'s `usb_probe`; it looks clean. So, the question becomes, what's calling `device_add` *after* `usb_probe`? My initial thought is it is caused by the `input_register_device` function. I think it is calling `input_attach_handler`, and it may be looping.
Let's check `input_register_device`, what exactly is going on here? `list_for_each_entry(handler, &input_handler_list, node)` calls `input_attach_handler(dev, handler)`. The number of `input_handler_list` entries might be the issue. So I need to go back and check.
Okay, not `input_register_device`, `evdev_connect` is clean as well. It's time to dig deeper. I need to understand what's happening within `cdev_add` and ultimately `kobj_map`. Let's focus on `kobj_map` now and what values of `n` are we feeding this loop. I need to understand this loop's parameters: what `range` is being passed? What are the implications of the modulo operation? I suspect an issue with the major/minor numbers, but I need to confirm.
I'm going to check `cdev_add` next.
|
| 23/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/char_dev.c Name:cdev_add]
Results: map[SourceCode: 466: /**
467: * cdev_add() - add a char device to the system
468: * @p: the cdev structure for the device
469: * @dev: the first device number for which this device is responsible
470: * @count: the number of consecutive minor numbers corresponding to this
471: * device
472: *
473: * cdev_add() adds the device represented by @p to the system, making it
474: * live immediately. A negative error code is returned on failure.
475: */
476: int cdev_add(struct cdev *p, dev_t dev, unsigned count)
477: {
478: int error;
479:
480: p->dev = dev;
481: p->count = count;
482:
483: if (WARN_ON(dev == WHITEOUT_DEV)) {
484: error = -EBUSY;
485: goto err;
486: }
487:
488: error = kobj_map(cdev_map, dev, count, NULL,
489: exact_match, exact_lock, p);
490: if (error)
491: goto err;
492:
493: kobject_get(p->kobj.parent);
494:
495: return 0;
496:
497: err:
498: kfree_const(p->kobj.name);
499: p->kobj.name = NULL;
500: return error;
501: }
SourceFile:fs/char_dev.c]
|
| 24/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 34274
output: 37
thoughts: 68
Thoughts: Here's my summary, as though I'm thinking through this code:
**Diving into the Device Addition**
Okay, so we're in `evdev_connect`, right? First, we're initializing the character device (`cdev`) structure for our evdev device. Standard stuff. Then, we hit `cdev_device_add`. This is the core operation, the bit that actually makes the device available to the userspace. Let's make sure that is done correctly.
|
| 25/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/char_dev.c Name:cdev_device_add]
Results: map[SourceCode: 518: /**
519: * cdev_device_add() - add a char device and it's corresponding
520: * struct device, linkink
521: * @dev: the device structure
522: * @cdev: the cdev structure
523: *
524: * cdev_device_add() adds the char device represented by @cdev to the system,
525: * just as cdev_add does. It then adds @dev to the system using device_add
526: * The dev_t for the char device will be taken from the struct device which
527: * needs to be initialized first. This helper function correctly takes a
528: * reference to the parent device so the parent will not get released until
529: * all references to the cdev are released.
530: *
531: * This helper uses dev->devt for the device number. If it is not set
532: * it will not add the cdev and it will be equivalent to device_add.
533: *
534: * This function should be used whenever the struct cdev and the
535: * struct device are members of the same structure whose lifetime is
536: * managed by the struct device.
537: *
538: * NOTE: Callers must assume that userspace was able to open the cdev and
539: * can call cdev fops callbacks at any time, even if this function fails.
540: */
541: int cdev_device_add(struct cdev *cdev, struct device *dev)
542: {
543: int rc = 0;
544:
545: if (dev->devt) {
546: cdev_set_parent(cdev, &dev->kobj);
547:
548: rc = cdev_add(cdev, dev->devt, 1);
549: if (rc)
550: return rc;
551: }
552:
553: rc = device_add(dev);
554: if (rc && dev->devt)
555: cdev_del(cdev);
556:
557: return rc;
558: }
SourceFile:fs/char_dev.c]
|
| 26/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 35155
output: 36
thoughts: 211
Thoughts: **Debugging a Potential RCU Stall in USB Device Handling**
Okay, let's see. `count` is at 1, `n` is also 1. The `kobj_map` isn't looping, which is good. The nagging question is still: why am I seeing this RCU stall? My first hunch is that the `hub_event` handler is just overloaded with events, but let's not jump to conclusions. Let's dig deeper. Time to pull up that syzkaller reproducer and see what's happening.
Ah, it's calling `syz_usb_connect`. It's setting the speed to `USB_SPEED_FULL`, a device length of `0x2d`, and feeding in a device descriptor. Simple USB device connection. Wait... that's it? Just the connection? If the device itself has a ton of interfaces, or if the driver creates a slew of input devices, maybe this initial connection is taking an unexpectedly long time? It's conceivable.
Hold on a second, I remember that `yealink` driver... It was doing something interesting. I recall it's doing this loop:
```c
for (i = 0; i < ARRAY_SIZE(lcdMap); i++)
setChar(yld, i, ' ');
```
Right, I need to check out what's going on within that `setChar` function in `yealink.c`. That might be the culprit. Let's see...
|
| 27/2 |
2026/05/30 05:58 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:setChar]
Results: map[SourceCode: 127: /* Display a char,
128: * char '\9' and '\n' are placeholders and do not overwrite the original text.
129: * A space will always hide an icon.
130: */
131: static int setChar(struct yealink_dev *yld, int el, int chr)
132: {
133: int i, a, m, val;
134:
135: if (el >= ARRAY_SIZE(lcdMap))
136: return -EINVAL;
137:
138: if (chr == '\t' || chr == '\n')
139: return 0;
140:
141: yld->lcdMap[el] = chr;
142:
143: if (lcdMap[el].type == '.') {
144: a = lcdMap[el].u.p.a;
145: m = lcdMap[el].u.p.m;
146: if (chr != ' ')
147: yld->master.b[a] |= m;
148: else
149: yld->master.b[a] &= ~m;
150: return 0;
151: }
152:
153: val = map_to_seg7(&map_seg7, chr);
154: for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {
155: m = lcdMap[el].u.s[i].m;
156:
157: if (m == 0)
158: continue;
159:
160: a = lcdMap[el].u.s[i].a;
161: if (val & 1)
162: yld->master.b[a] |= m;
163: else
164: yld->master.b[a] &= ~m;
165: val = val >> 1;
166: }
167: return 0;
168: };
SourceFile:drivers/input/misc/yealink.c]
|
| 28/2 |
2026/05/30 05:58 |
llm |
expert |
0m
Model:
Tokens:
input: 36356
output: 36
thoughts: 6
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deeply familiar with the relevant technology:
**Assessing the Scope of `lcdMap`**
Hmm, `lcdMap`... that's a key structure in this system. Before I proceed any further, I need to understand its current size. What's the scale of the mapping we're dealing with? Is it small and manageable, or potentially quite large? This directly impacts my design choices. If it's a small lookup table, I might opt for a straightforward approach. However, if it has the potential to grow significantly, I'll need to consider efficiency and potential memory constraints very carefully. I have to think about potential performance impacts as the system scales. I need to know the size so I can start considering the best method of dealing with it. The memory footprint also matters. Ok, let's look into it.
|
| 29/2 |
2026/05/30 05:59 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:lcdMap]
Results: map[SourceCode: 70: static const struct lcd_segment_map {
71: char type;
72: union {
73: struct pictogram_map {
74: u8 a,m;
75: char name[10];
76: } p;
77: struct segment_map {
78: u8 a,m;
79: } s[7];
80: } u;
81: } lcdMap[] = {
82: #include "yealink.h"
83: };
SourceFile:drivers/input/misc/yealink.c]
|
| 30/2 |
2026/05/30 05:59 |
llm |
expert |
0m
Model:
Tokens:
input: 36777
output: 37
thoughts: 10
Thoughts: Okay, here's my attempt at summarizing my thought process, assuming I'm a seasoned professional delving into the `yealink.h` header file:
**Diving into Yealink.h**
Alright, time to crack open `yealink.h`. I need to get a handle on the Yealink interface again, likely looking to understand how its interacting with my code. Let's see... what are we dealing with here? I'll probably be scanning for the key structures, enums, and function prototypes. Gotta quickly identify the core data types it defines for communication. I'm especially interested in whether any new changes have been introduced. Need to see if there are any obvious discrepancies, something thatβs unexpected given the last time I touched this. Are there any new APIs, deprecated functions, or, heaven forbid, breaking changes that I need to be aware of? I need to get up to speed fast. Letβs get to it.
|
| 31/2 |
2026/05/30 05:59 |
tool |
read-file |
0m
Args: map[File:drivers/input/misc/yealink.h FirstLine:1 LineCount:100]
Results: map[Contents: 1: /* SPDX-License-Identifier: GPL-2.0-or-later */
2: /*
3: * drivers/usb/input/yealink.h
4: *
5: * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
6: */
7: #ifndef INPUT_YEALINK_H
8: #define INPUT_YEALINK_H
9:
10: /* Using the control channel on interface 3 various aspects of the phone
11: * can be controlled like LCD, LED, dialtone and the ringtone.
12: */
13:
14: struct yld_ctl_packet {
15: u8 cmd; /* command code, see below */
16: u8 size; /* 1-11, size of used data bytes. */
17: __be16 offset; /* internal packet offset */
18: u8 data[11];
19: s8 sum; /* negative sum of 15 preceding bytes */
20: } __attribute__ ((packed));
21:
22: #define USB_PKT_LEN sizeof(struct yld_ctl_packet)
23:
24: /* The following yld_ctl_packet's are available: */
25:
26: /* Init registers
27: *
28: * cmd 0x8e
29: * size 10
30: * offset 0
31: * data 0,0,0,0....
32: */
33: #define CMD_INIT 0x8e
34:
35: /* Request key scan
36: *
37: * cmd 0x80
38: * size 1
39: * offset 0
40: * data[0] on return returns the key number, if it changes there's a new
41: * key pressed.
42: */
43: #define CMD_KEYPRESS 0x80
44:
45: /* Request scancode
46: *
47: * cmd 0x81
48: * size 1
49: * offset key number [0-1f]
50: * data[0] on return returns the scancode
51: */
52: #define CMD_SCANCODE 0x81
53:
54: /* Set LCD
55: *
56: * cmd 0x04
57: * size 1-11
58: * offset 0-23
59: * data segment bits
60: */
61: #define CMD_LCD 0x04
62:
63: /* Set led
64: *
65: * cmd 0x05
66: * size 1
67: * offset 0
68: * data[0] 0 OFF / 1 ON
69: */
70: #define CMD_LED 0x05
71:
72: /* Set ringtone volume
73: *
74: * cmd 0x11
75: * size 1
76: * offset 0
77: * data[0] 0-0xff volume
78: */
79: #define CMD_RING_VOLUME 0x11
80:
81: /* Set ringtone notes
82: *
83: * cmd 0x02
84: * size 1-11
85: * offset 0->
86: * data binary representation LE16(-freq), LE16(duration) ....
87: */
88: #define CMD_RING_NOTE 0x02
89:
90: /* Sound ringtone via the speaker on the back
91: *
92: * cmd 0x03
93: * size 1
94: * offset 0
95: * data[0] 0 OFF / 0x24 ON
96: */
97: #define CMD_RINGTONE 0x03
98:
99: /* Sound dial tone via the ear speaker
100: *
]
|
| 32/2 |
2026/05/30 05:59 |
llm |
expert |
0m
Model:
Tokens:
input: 38256
output: 39
thoughts: 16
Thoughts: Okay, here's my summarized thought process, keeping in mind my expert understanding:
**Initial Realization and Questioning**
Wait a minute... is that right? Did I just see that `yealink.h` header file actually contains the `lcdMap` elements I was looking for? I could have sworn they were defined somewhere else, maybe in a more general library. This is a bit unexpected, but also quite interesting.
|
| 33/2 |
2026/05/30 05:59 |
tool |
read-file |
0m
Args: map[File:drivers/input/misc/yealink.h FirstLine:100 LineCount:100]
Results: map[Contents: 100: *
101: * cmd 0x09
102: * size 1
103: * offset 0
104: * data[0] 0 OFF / 1 ON
105: */
106: #define CMD_DIALTONE 0x09
107:
108: #endif /* INPUT_YEALINK_H */
109:
110:
111: #if defined(_SEG) && defined(_PIC)
112: /* This table maps the LCD segments onto individual bit positions in the
113: * yld_status struct.
114: */
115:
116: /* LCD, each segment must be driven separately.
117: *
118: * Layout:
119: *
120: * |[] [][] [][] [][] in |[][]
121: * |[] M [][] D [][] : [][] out |[][]
122: * store
123: *
124: * NEW REP SU MO TU WE TH FR SA
125: *
126: * [] [] [] [] [] [] [] [] [] [] [] []
127: * [] [] [] [] [] [] [] [] [] [] [] []
128: */
129:
130: /* Line 1
131: * Format : 18.e8.M8.88...188
132: * Icon names : M D : IN OUT STORE
133: */
134: #define LCD_LINE1_OFFSET 0
135: #define LCD_LINE1_SIZE 17
136:
137: /* Note: first g then f => ! ! */
138: /* _SEG( type a b c d e g f ) */
139: _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
140: _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
141: _PIC('.', 22,1 , "M" ),
142: _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
143: _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
144: _PIC('.', 15,8 , "D" ),
145: _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
146: _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
147: _PIC('.', 11,8 , ":" ),
148: _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
149: _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
150: _PIC('.', 7,1 , "IN" ),
151: _PIC('.', 7,2 , "OUT" ),
152: _PIC('.', 7,4 , "STORE" ),
153: _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
154: _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
155: _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
156:
157: /* Line 2
158: * Format : .........
159: * Pict. name : NEW REP SU MO TU WE TH FR SA
160: */
161: #define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
162: #define LCD_LINE2_SIZE 9
163:
164: _PIC('.', 23,2 , "NEW" ),
165: _PIC('.', 23,4 , "REP" ),
166: _PIC('.', 1,8 , "SU" ),
167: _PIC('.', 1,4 , "MO" ),
168: _PIC('.', 1,2 , "TU" ),
169: _PIC('.', 1,1 , "WE" ),
170: _PIC('.', 0,1 , "TH" ),
171: _PIC('.', 0,2 , "FR" ),
172: _PIC('.', 0,4 , "SA" ),
173:
174: /* Line 3
175: * Format : 888888888888
176: */
177: #define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
178: #define LCD_LINE3_SIZE 12
179:
180: _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
181: _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
182: _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
183: _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
184: _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
185: _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
186: _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
187: _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
188: _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
189: _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
190: _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
191: _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
192:
193: /* Line 4
194: *
195: * The LED, DIALTONE and RINGTONE are implemented as icons and use the same
196: * sysfs interface.
197: */
198: #define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
199:
]
|
| 34/2 |
2026/05/30 05:59 |
llm |
expert |
0m
Model:
Tokens:
input: 40898
output: 38
thoughts: 2148
Thoughts: **RCU Stall Analysis**
Okay, so I'm looking at this RCU stall, and my initial thought is, "why now?" I see this syzkaller reproducer and it seems to involve a USB device, but initially, it doesn't seem overly complex. It just sets up a few endpoints. I start thinking about hub events and workqueues, wondering if there's a loop that's not yielding the CPU properly, but quickly realize it should yield. I then go look at `lock_acquire`, and the stack trace makes me think it's a deadlock. But wait, `lock_acquire` is called from `rcu_read_lock`. Okay, it's just lockdep tracking the RCU read lock. The stall happens because the CPU is interrupted by a timer interrupt that calls `rcu_preempt_check_callbacks`, which then detects the stall. The thing is, the CPU was running `lock_acquire` when the timer triggered. The interrupt happened, the task got preempted, and then we have this stall.
Alright, the stack trace shows it's being preempted after the interrupt, inside `__schedule`. So, it's a task preemption, that then gets detected as a stall. The CPU's not scheduling it back, which is causing this problem. I wonder if there's some kind of real-time task hogging the CPU, or maybe a task spinning with preemption disabled, but it appears to be a task stall. The stall message says the task is blocked on a level-0 `rcu_node`.
"Blocked on level-0 rcu_node" means the task was preempted inside an RCU read-side critical section. And then, it hasn't resumed and exited the critical section in a long time. So the task can't exit the critical section. And looking at the stack trace again shows that the process is in the `__schedule` function. It seems runnable, but not getting CPU time. The stack trace shows the RCU stall is inside `rcu_read_lock`! That's what I'm looking for - `class_rcu_constructor`. Time to look at the code!
|
| 35/2 |
2026/05/30 05:59 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/rcupdate.h Name:class_rcu_constructor]
Results: map[SourceCode:1193: DEFINE_LOCK_GUARD_0(rcu, rcu_read_lock(), rcu_read_unlock())
SourceFile:include/linux/rcupdate.h]
|
| 36/2 |
2026/05/30 05:59 |
llm |
expert |
1m
Model:
Tokens:
input: 43613
output: 35
thoughts: 148
Thoughts: **Analyzing a Potential RCU Issue**
Okay, so I see this `DEFINE_LOCK_GUARD_0(rcu, rcu_read_lock(), rcu_read_unlock())` macro. Right, that's a lock guard using RCU primitives. My initial thought is, "Where exactly is this used?" The crucial bit is tracing its usage.
Looking at the stack trace, I see it's calling into `kobj_map` and then `cdev_add`. Hmm... `kobj_map+0x80` specifically. That's interesting, but wait a minute... I'm pretty sure `kobj_map` *doesn't* directly use `guard(rcu)`! I'd better double-check the `kobj_map` function itself to make sure I haven't missed something. It's time to dig into the code again and verify that assumption.
|
| 37/2 |
2026/05/30 06:00 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/map.c Name:kobj_map]
Results: map[SourceCode: 32: int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
33: struct module *module, kobj_probe_t *probe,
34: int (*lock)(dev_t, void *), void *data)
35: {
36: unsigned int n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
37: unsigned int index = MAJOR(dev);
38: unsigned int i;
39: struct probe *p;
40:
41: if (n > 255)
42: n = 255;
43:
44: p = kmalloc_objs(struct probe, n);
45: if (p == NULL)
46: return -ENOMEM;
47:
48: for (i = 0; i < n; i++, p++) {
49: p->owner = module;
50: p->get = probe;
51: p->lock = lock;
52: p->dev = dev;
53: p->range = range;
54: p->data = data;
55: }
56: mutex_lock(domain->lock);
57: for (i = 0, p -= n; i < n; i++, p++, index++) {
58: struct probe **s = &domain->probes[index % 255];
59: while (*s && (*s)->range < range)
60: s = &(*s)->next;
61: p->next = *s;
62: *s = p;
63: }
64: mutex_unlock(domain->lock);
65: return 0;
66: }
SourceFile:drivers/base/map.c]
|
| 38/2 |
2026/05/30 06:00 |
llm |
expert |
0m
Model:
Tokens:
input: 44513
output: 44
thoughts: 408
Thoughts: **Analyzing the Memory Allocation and Stack Trace**
Okay, so I'm looking at this code and immediately I see that `kobj_map` is making a call to `kmalloc_objs`. Right, and `kmalloc_objs` is defined using `kmalloc_array`, which is a standard approach. No surprises there. But, looking at the stack trace... that's where things get interesting.
I see a stack that looks like it's KASAN related, including `kasan_save_stack`, `poison_kmalloc_redzone`, and eventually landing on `kmalloc_noprof` and then `kobj_map`. This confirms the allocation is being done using `kmalloc`, which isn't itself unexpected but the KASAN hooks show a possible memory issue.
Wait a second, `unwind_next_frame` is in the call stack. Hmmm, the architecture-specific unwind code is usually interesting. Oh! Now I remember; `unwind_next_frame` uses `guard(rcu)`. That's an important detail. I should probably dig into the `unwind_next_frame` code to see how it is interacting with the memory allocation. Understanding that RCU guard is crucial, especially in this context, because it's suggesting that the stack unwinding may be happening concurrently with or related to the memory allocation. Time to inspect that function.
|
| 39/2 |
2026/05/30 06:00 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/kernel/unwind_orc.c Name:unwind_next_frame]
Results: map[SourceCode: 484: bool unwind_next_frame(struct unwind_state *state)
485: {
486: unsigned long ip_p, sp, tmp, orig_ip = state->ip, prev_sp = state->sp;
487: enum stack_type prev_type = state->stack_info.type;
488: struct orc_entry *orc;
489: bool indirect = false;
490:
491: if (unwind_done(state))
492: return false;
493:
494: /* Don't let modules unload while we're reading their ORC data. */
495: guard(rcu)();
496:
497: /* End-of-stack check for user tasks: */
498: if (state->regs && user_mode(state->regs))
499: goto the_end;
500:
501: /*
502: * Find the orc_entry associated with the text address.
503: *
504: * For a call frame (as opposed to a signal frame), state->ip points to
505: * the instruction after the call. That instruction's stack layout
506: * could be different from the call instruction's layout, for example
507: * if the call was to a noreturn function. So get the ORC data for the
508: * call instruction itself.
509: */
510: orc = orc_find(state->signal ? state->ip : state->ip - 1);
511: if (!orc) {
512: /*
513: * As a fallback, try to assume this code uses a frame pointer.
514: * This is just a guess, so the rest of the unwind is no longer
515: * considered reliable.
516: */
517: orc = &orc_fp_entry;
518: state->error = true;
519: } else {
520: if (orc->type == ORC_TYPE_UNDEFINED)
521: goto err;
522:
523: if (orc->type == ORC_TYPE_END_OF_STACK)
524: goto the_end;
525: }
526:
527: state->signal = orc->signal;
528:
529: /* Find the previous frame's stack: */
530: switch (orc->sp_reg) {
531: case ORC_REG_SP:
532: sp = state->sp + orc->sp_offset;
533: break;
534:
535: case ORC_REG_BP:
536: sp = state->bp + orc->sp_offset;
537: break;
538:
539: case ORC_REG_SP_INDIRECT:
540: sp = state->sp;
541: indirect = true;
542: break;
543:
544: case ORC_REG_BP_INDIRECT:
545: sp = state->bp + orc->sp_offset;
546: indirect = true;
547: break;
548:
549: case ORC_REG_R10:
550: if (!get_reg(state, offsetof(struct pt_regs, r10), &sp)) {
551: orc_warn_current("missing R10 value at %pB\n",
552: (void *)state->ip);
553: goto err;
554: }
555: break;
556:
557: case ORC_REG_R13:
558: if (!get_reg(state, offsetof(struct pt_regs, r13), &sp)) {
559: orc_warn_current("missing R13 value at %pB\n",
560: (void *)state->ip);
561: goto err;
562: }
563: break;
564:
565: case ORC_REG_DI:
566: if (!get_reg(state, offsetof(struct pt_regs, di), &sp)) {
567: orc_warn_current("missing RDI value at %pB\n",
568: (void *)state->ip);
569: goto err;
570: }
571: break;
572:
573: case ORC_REG_DX:
574: if (!get_reg(state, offsetof(struct pt_regs, dx), &sp)) {
575: orc_warn_current("missing DX value at %pB\n",
576: (void *)state->ip);
577: goto err;
578: }
579: break;
580:
581: default:
582: orc_warn("unknown SP base reg %d at %pB\n",
583: orc->sp_reg, (void *)state->ip);
584: goto err;
585: }
586:
587: if (indirect) {
588: if (!deref_stack_reg(state, sp, &sp))
589: goto err;
590:
591: if (orc->sp_reg == ORC_REG_SP_INDIRECT)
592: sp += orc->sp_offset;
593: }
594:
595: /* Find IP, SP and possibly regs: */
596: switch (orc->type) {
597: case ORC_TYPE_CALL:
598: ip_p = sp - sizeof(long);
599:
600: if (!deref_stack_reg(state, ip_p, &state->ip))
601: goto err;
602:
603: state->ip = unwind_recover_ret_addr(state, state->ip,
604: (unsigned long *)ip_p);
605: state->sp = sp;
606: state->regs = NULL;
607: state->prev_regs = NULL;
608: break;
609:
610: case ORC_TYPE_REGS:
611: if (!deref_stack_regs(state, sp, &state->ip, &state->sp)) {
612: orc_warn_current("can't access registers at %pB\n",
613: (void *)orig_ip);
614: goto err;
615: }
616: /*
617: * There is a small chance to interrupt at the entry of
618: * arch_rethook_trampoline() where the ORC info doesn't exist.
619: * That point is right after the RET to arch_rethook_trampoline()
620: * which was modified return address.
621: * At that point, the @addr_p of the unwind_recover_rethook()
622: * (this has to point the address of the stack entry storing
623: * the modified return address) must be "SP - (a stack entry)"
624: * because SP is incremented by the RET.
625: */
626: state->ip = unwind_recover_rethook(state, state->ip,
627: (unsigned long *)(state->sp - sizeof(long)));
628: state->regs = (struct pt_regs *)sp;
629: state->prev_regs = NULL;
630: state->full_regs = true;
631: break;
632:
633: case ORC_TYPE_REGS_PARTIAL:
634: if (!deref_stack_iret_regs(state, sp, &state->ip, &state->sp)) {
635: orc_warn_current("can't access iret registers at %pB\n",
636: (void *)orig_ip);
637: goto err;
638: }
639: /* See ORC_TYPE_REGS case comment. */
640: state->ip = unwind_recover_rethook(state, state->ip,
641: (unsigned long *)(state->sp - sizeof(long)));
642:
643: if (state->full_regs)
644: state->prev_regs = state->regs;
645: state->regs = (void *)sp - IRET_FRAME_OFFSET;
646: state->full_regs = false;
647: break;
648:
649: default:
650: orc_warn("unknown .orc_unwind entry type %d at %pB\n",
651: orc->type, (void *)orig_ip);
652: goto err;
653: }
654:
655: /* Find BP: */
656: switch (orc->bp_reg) {
657: case ORC_REG_UNDEFINED:
658: if (get_reg(state, offsetof(struct pt_regs, bp), &tmp))
659: state->bp = tmp;
660: break;
661:
662: case ORC_REG_PREV_SP:
663: if (!deref_stack_reg(state, sp + orc->bp_offset, &state->bp))
664: goto err;
665: break;
666:
667: case ORC_REG_BP:
668: if (!deref_stack_reg(state, state->bp + orc->bp_offset, &state->bp))
669: goto err;
670: break;
671:
672: default:
673: orc_warn("unknown BP base reg %d for ip %pB\n",
674: orc->bp_reg, (void *)orig_ip);
675: goto err;
676: }
677:
678: /* Prevent a recursive loop due to bad ORC data: */
679: if (state->stack_info.type == prev_type &&
680: on_stack(&state->stack_info, (void *)state->sp, sizeof(long)) &&
681: state->sp <= prev_sp) {
682: orc_warn_current("stack going in the wrong direction? at %pB\n",
683: (void *)orig_ip);
684: goto err;
685: }
686:
687: return true;
688:
689: err:
690: state->error = true;
691:
692: the_end:
693: state->stack_info.type = STACK_TYPE_UNKNOWN;
694: return false;
695: }
SourceFile:arch/x86/kernel/unwind_orc.c]
|
| 40/2 |
2026/05/30 06:00 |
llm |
expert |
0m
Model:
Tokens:
input: 48429
output: 38
thoughts: 2090
Thoughts: **RCU Stall Investigation: A Deep Dive**
Okay, so I'm looking at this trace and initially, I see `guard(rcu)()` being called inside `unwind_next_frame`, which itself is called in a loop by `arch_stack_walk`. Then I realize this whole thing is triggered by `kasan_save_stack`, which is called on every `kmalloc`. So every memory allocation is unwinding the stack and calling `rcu_read_lock()` and `rcu_read_unlock()` for *every* stack frame, using a local variable with `CLASS_rcu_t`.
This seems potentially costly, but maybe it's fine, as it's meant to be fast. But, if `kasan_save_stack` gets called a *lot*, it could cause problems. That's a good lead. So, I need to figure out how to cause a flood of allocations.
The question then becomes: where are the allocations triggered? The reproducer gives me a hint: it does `syz_usb_connect` and then `syz_usb_ep_write`.
Right, let's parse the USB descriptor information. It's a Yealink device! The descriptor has a configuration, an interface, and an endpoint. Interesting! The endpoint descriptor looks weird, it's length is `09`, but it should be 7. This is a potential red flag. Let's look at the `yealink` driver (`yealink.c`). I remember there's an input device registered in the probe function.
Aha, the `yealink` driver registers an input device, but what if the registration of this device creates a loop? This could explain RCU stalls if something is recursively allocating memory while handling events. So I check `input_register_device`.
Okay, no obvious loop there. `input_attach_handler` and `evdev_connect` seem safe. No obvious problem. The stack trace is just a snapshot. So maybe the stall happens later? Maybe the driver triggers a lot of events? The syzkaller reproducer only connects the device once, so that can't be it. Is the device maybe disconnecting and reconnecting continuously? No. It's just a single connection.
Then I check the `usb_probe` function again, to find the initial URB setup. Okay, the driver is configuring a control transfer. It's setting up the URB with `USB_REQ_SET_CONFIGURATION` and setting up a transfer.
No obvious loop there either. Wait, the driver writes to the LCD. Let's look at `store_line3`...
|
| 41/2 |
2026/05/30 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:store_line3]
Results: map[SourceCode: 652: static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
653: const char *buf, size_t count)
654: {
655: return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);
656: }
SourceFile:drivers/input/misc/yealink.c]
|
| 42/2 |
2026/05/30 06:01 |
llm |
expert |
1m
Model:
Tokens:
input: 51249
output: 37
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment: Verifying Data Storage**
Right, let's take a look at `store_line`. It's crucial, obviously. This variable or function, depending on context, likely represents the core mechanism for⦠well, storing a line of data. My initial thought is to quickly mentally run through the potential bottlenecks. Is it a variable holding a string, a list, or maybe a database call? The name suggests it's related to some kind of persistent storage, which implies further questions: Is there error handling? What's the storage medium? Are we talking about a simple file write, or a more sophisticated approach like a queue with retry mechanisms? Context is everything. I need to understand its role in the greater architecture, particularly the data input, processing, and output pipelines. I'll need to consider performance implications, concurrency issues if applicable, and potential security vulnerabilities related to data persistence. A cursory glance at the surrounding code should provide some context.
|
| 43/2 |
2026/05/30 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:store_line]
Results: map[SourceCode: 617: /* Writing to /sys/../lineX will set the corresponding LCD line.
618: * - Excess characters are ignored.
619: * - If less characters are written than allowed, the remaining digits are
620: * unchanged.
621: * - The '\n' or '\t' char is a placeholder, it does not overwrite the
622: * original content.
623: */
624: static ssize_t store_line(struct device *dev, const char *buf, size_t count,
625: int el, size_t len)
626: {
627: struct yealink_dev *yld = dev_get_drvdata(dev);
628: int i;
629:
630: guard(mutex)(&yld->sysfs_mutex);
631:
632: if (len > count)
633: len = count;
634: for (i = 0; i < len; i++)
635: setChar(yld, el++, buf[i]);
636:
637: return count;
638: }
SourceFile:drivers/input/misc/yealink.c]
|
| 44/2 |
2026/05/30 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 51876
output: 58
thoughts: 54
Thoughts: **Tracing the URB Submission**
Okay, so I'm digging into this code and something's bugging me. I see that `store_line` is calling `setChar`. Interesting. But wait a minute... `store_line` isn't the function actually submitting the USB Request Block (URB). That's a key piece missing. Where *is* that URB being sent? I need to find the call to `usb_submit_urb`. Let's switch gears and go directly to `yealink.c`. That's likely where the device-specific USB interaction happens, so it's the most promising place to locate the `usb_submit_urb` call and figure out when this data is actually sent to the device.
|
| 45/2 |
2026/05/30 06:02 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c IncludeSnippetLines:2 Name:usb_submit_urb SourceTreePrefix:drivers/input/misc/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_complete_key SourceFile:drivers/input/misc/ati_remote2.c SourceLine:446 SourceSnippet: 444: }
445:
446: r = usb_submit_urb(urb, GFP_ATOMIC);
447: if (r)
448: dev_err(&ar2->intf[1]->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_complete_mouse SourceFile:drivers/input/misc/ati_remote2.c SourceLine:417 SourceSnippet: 415: }
416:
417: r = usb_submit_urb(urb, GFP_ATOMIC);
418: if (r)
419: dev_err(&ar2->intf[0]->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_submit_urbs SourceFile:drivers/input/misc/ati_remote2.c SourceLine:213 SourceSnippet: 211: int r;
212:
213: r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
214: if (r) {
215: dev_err(&ar2->intf[0]->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ati_remote2_submit_urbs SourceFile:drivers/input/misc/ati_remote2.c SourceLine:219 SourceSnippet: 217: return r;
218: }
219: r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
220: if (r) {
221: usb_kill_urb(ar2->urb[0]);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_input_open SourceFile:drivers/input/misc/cm109.c SourceLine:566 SourceSnippet: 564:
565: dev->ctl_urb_pending = 1;
566: error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
567: if (!error) {
568: dev->open = 1;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_submit_buzz_toggle SourceFile:drivers/input/misc/cm109.c SourceLine:351 SourceSnippet: 349: dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
350:
351: error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
352: if (error)
353: dev_err(&dev->intf->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_submit_ctl SourceFile:drivers/input/misc/cm109.c SourceLine:380 SourceSnippet: 378: dev->ctl_urb_pending = 1;
379:
380: error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
381: if (error)
382: dev_err(&dev->intf->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_urb_ctl_callback SourceFile:drivers/input/misc/cm109.c SourceLine:467 SourceSnippet: 465: /* ask for key data */
466: dev->irq_urb_pending = 1;
467: error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
468: if (error)
469: dev_err(&dev->intf->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_irq SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1517 SourceSnippet:1515:
1516: exit:
1517: retval = usb_submit_urb(urb, GFP_ATOMIC);
1518: if (retval && retval != -ENODEV)
1519: dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n",
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_start_io SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1737 SourceSnippet:1735: int error;
1736:
1737: error = usb_submit_urb(pcu->urb_ctrl, GFP_KERNEL);
1738: if (error) {
1739: dev_err(pcu->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ims_pcu_start_io SourceFile:drivers/input/misc/ims-pcu.c SourceLine:1745 SourceSnippet:1743: }
1744:
1745: error = usb_submit_urb(pcu->urb_in, GFP_KERNEL);
1746: if (error) {
1747: dev_err(pcu->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:input_open SourceFile:drivers/input/misc/yealink.c SourceLine:522 SourceSnippet: 520: yld->ctl_data->size = 10;
521: yld->ctl_data->sum = 0x100-CMD_INIT-10;
522: if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
523: dev_dbg(&yld->intf->dev,
524: "%s - usb_submit_urb failed with result %d\n",
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_irq_recv SourceFile:drivers/input/misc/keyspan_remote.c SourceLine:398 SourceSnippet: 396:
397: resubmit:
398: retval = usb_submit_urb(urb, GFP_ATOMIC);
399: if (retval)
400: dev_err(&dev->interface->dev,
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:keyspan_open SourceFile:drivers/input/misc/keyspan_remote.c SourceLine:410 SourceSnippet: 408:
409: remote->irq_urb->dev = remote->udev;
410: if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
411: return -EIO;
412:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powermate_irq SourceFile:drivers/input/misc/powermate.c SourceLine:115 SourceSnippet: 113:
114: exit:
115: retval = usb_submit_urb (urb, GFP_ATOMIC);
116: if (retval)
117: dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powermate_probe SourceFile:drivers/input/misc/powermate.c SourceLine:385 SourceSnippet: 383:
384: /* register our interrupt URB with the USB system */
385: if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
386: error = -EIO;
387: goto fail4;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:powermate_sync_state SourceFile:drivers/input/misc/powermate.c SourceLine:189 SourceSnippet: 187: powermate_config_complete, pm);
188:
189: if (usb_submit_urb(pm->config, GFP_ATOMIC))
190: printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
191: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:urb_ctl_callback SourceFile:drivers/input/misc/yealink.c SourceLine:464 SourceSnippet: 462: /* ask for a response */
463: if (!yld->shutdown)
464: ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
465: break;
466: default:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:urb_ctl_callback SourceFile:drivers/input/misc/yealink.c SourceLine:470 SourceSnippet: 468: yealink_do_idle_tasks(yld);
469: if (!yld->shutdown)
470: ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
471: break;
472: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:urb_irq_callback SourceFile:drivers/input/misc/yealink.c SourceLine:442 SourceSnippet: 440:
441: if (!yld->shutdown) {
442: ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
443: if (ret && ret != -EPERM)
444: dev_err(&yld->intf->dev,
]] TruncatedOutput:false]
|
| 46/2 |
2026/05/30 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 54970
output: 42
thoughts: 1039
Thoughts: **Analyzing the Yealink USB Driver URB Submission Logic**
Okay, so I'm looking at this Yealink driver code and immediately I see `usb_submit_urb` being called in `input_open`. That's a bit unusual, normally you'd expect the URB submission to happen during probe or maybe initialization. Let me check... Ah, right, the probe function just sets up the URB, it doesn't submit it.
Now, wait... the URBs are actually submitted in the callbacks! Specifically, `urb_ctl_callback` and `urb_irq_callback`. Okay, that makes more sense. `usb_submit_urb` is called within the context of the URB completion handlers. Let me analyze this a little more carefully.
Aha, here's the kicker! If the `ctl_data->cmd` is *not* `CMD_KEYPRESS` or `CMD_SCANCODE`, `urb_ctl_callback` calls `yealink_do_idle_tasks` and *then* submits the `urb_ctl` again. And `urb_irq_callback` also unconditionally submits the `urb_ctl`. Okay, this looks like a potential problem.
If a USB device like `dummy_hcd` or a raw-gadget, where the URB might complete synchronously (or very quickly), this looks like it could lead to an infinite loop of URB submissions! The completion handler triggers another submission, which in turn triggers another completion... you get the picture.
Specifically, in `urb_ctl_callback`, the call to `yealink_do_idle_tasks` is followed by a submission of `urb_ctl`. This would loop if the URB is handled immediately.
Okay, let me check something. This is a control endpoint, right? Does `dummy_hcd` handle control transfers synchronously, or does it defer them to a timer callback? Generally, the expectation is that they're deferred, but the possibility remains, especially in a simplified environment. If it's a timer callback, then we have a chance to yield and the system might continue, but if synchronous... this is bad.
Wait, `yealink_do_idle_tasks` prepares the *next* command. So we are sending a *new* command each time. Okay.
|
| 47/2 |
2026/05/30 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/yealink.c Name:yealink_do_idle_tasks]
Results: map[SourceCode: 314: /* keep stat_master & stat_copy in sync.
315: */
316: static int yealink_do_idle_tasks(struct yealink_dev *yld)
317: {
318: u8 val;
319: int i, ix, len;
320:
321: ix = yld->stat_ix;
322:
323: memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
324: yld->ctl_data->cmd = CMD_KEYPRESS;
325: yld->ctl_data->size = 1;
326: yld->ctl_data->sum = 0xff - CMD_KEYPRESS;
327:
328: /* If state update pointer wraps do a KEYPRESS first. */
329: if (ix >= sizeof(yld->master)) {
330: yld->stat_ix = 0;
331: return 0;
332: }
333:
334: /* find update candidates: copy != master */
335: do {
336: val = yld->master.b[ix];
337: if (val != yld->copy.b[ix])
338: goto send_update;
339: } while (++ix < sizeof(yld->master));
340:
341: /* nothing todo, wait a bit and poll for a KEYPRESS */
342: yld->stat_ix = 0;
343: /* TODO how can we wait abit. ??
344: * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);
345: */
346: return 0;
347:
348: send_update:
349:
350: /* Setup an appropriate update request */
351: yld->copy.b[ix] = val;
352: yld->ctl_data->data[0] = val;
353:
354: switch(ix) {
355: case offsetof(struct yld_status, led):
356: yld->ctl_data->cmd = CMD_LED;
357: yld->ctl_data->sum = -1 - CMD_LED - val;
358: break;
359: case offsetof(struct yld_status, dialtone):
360: yld->ctl_data->cmd = CMD_DIALTONE;
361: yld->ctl_data->sum = -1 - CMD_DIALTONE - val;
362: break;
363: case offsetof(struct yld_status, ringtone):
364: yld->ctl_data->cmd = CMD_RINGTONE;
365: yld->ctl_data->sum = -1 - CMD_RINGTONE - val;
366: break;
367: case offsetof(struct yld_status, keynum):
368: val--;
369: val &= 0x1f;
370: yld->ctl_data->cmd = CMD_SCANCODE;
371: yld->ctl_data->offset = cpu_to_be16(val);
372: yld->ctl_data->data[0] = 0;
373: yld->ctl_data->sum = -1 - CMD_SCANCODE - val;
374: break;
375: default:
376: len = sizeof(yld->master.s.lcd) - ix;
377: if (len > sizeof(yld->ctl_data->data))
378: len = sizeof(yld->ctl_data->data);
379:
380: /* Combine up to <len> consecutive LCD bytes in a single request
381: */
382: yld->ctl_data->cmd = CMD_LCD;
383: yld->ctl_data->offset = cpu_to_be16(ix);
384: yld->ctl_data->size = len;
385: yld->ctl_data->sum = -CMD_LCD - ix - val - len;
386: for(i=1; i<len; i++) {
387: ix++;
388: val = yld->master.b[ix];
389: yld->copy.b[ix] = val;
390: yld->ctl_data->data[i] = val;
391: yld->ctl_data->sum -= val;
392: }
393: }
394: yld->stat_ix = ix + 1;
395: return 1;
396: }
SourceFile:drivers/input/misc/yealink.c]
|
| 48/2 |
2026/05/30 06:02 |
llm |
expert |
1m
Model:
Tokens:
input: 57899
output: 35
thoughts: 6094
Thoughts: **Analysis of an RCU Stall in USB Device Probing**
Okay, so I've been diving into this RCU stall reported by syzbot, and it looks like a classic case of scheduler starvation. It starts with the `yealink_do_idle_tasks` function setting `yld->ctl_data->cmd = CMD_KEYPRESS`, which triggers a sequence of USB URB submissions: `urb_ctl` -> `urb_irq` -> `urb_ctl` -> `urb_irq`. This is a polling loop, but not necessarily a CPU hog, as `dummy_hcd` uses a timer (every 1ms or 125us) to complete URBs, so not 100% CPU usage. The main issue is that this particular reproduction case in syzbot creates a huge stack trace that leads to the RCU stall.
The real culprit isn't a deadlock or infinite loop within the USB driver itself. Itβs more related to the environment where syzbot runs this reproduction case. The stack trace involves `kobj_map` (in `cdev_add`), which seems slow, but this isn't necessarily a deadlock. It might have a large number of entries, because `syz_usb_connect` is called in a loop, each time attaching a new USB device, each device registers an `evdev` device, causing each device to add more and more entries in the `cdev_map`. The issue, more specifically, is the high overhead of KASAN, lockdep, KMSAN, and ORC unwinding, particularly in the complex path of USB device probing with lots of allocations. With the use of KASAN/lockdep, there are so many operations (e.g. within `usb_probe`, `input_register_device`, and `evdev_connect`), the process just gets slow. In the specific stack trace, we see a focus on `kobj_map` during the USB probing, which is not the root of the problem.
The key to understanding the RCU stall lies in the RCU message itself, specifically the part where we see that `kworker/1:2` is a `state:R running task` on CPUs 0-1, but the "RCU grace period" is never ending. This means this kworker task is never getting scheduled, because there might be CPU hogging by some SCHED_FIFO task, and the main thing causing the issue. Since the kernel task is preempted during an `rcu_read_lock`, but never gets to run again to call `rcu_read_unlock`, the RCU grace period never ends. It's a classic example of scheduler starvation, where a task is blocked because another task has taken over the CPU and prevents the `rcu_read_unlock` call from ever happening. If a SCHED_FIFO task spins in the userspace, RCU does not see the stalling, but since the `kworker` thread can not be scheduled after entering the RCU section, the stall is seen.
Also, it is interesting to understand how the tasks are locked. The line `lock_acquire+0x5e` is confusing because `lock_acquire` disables interrupts, which means the task is never preempted, however, the kernel stack contains the `rcu_read_lock` which is within `PREEMPT_RCU` mode which doesn't disable preemption. So the interrupt is taken during an important section (a kasan save stack that takes a long time), where it gets blocked and it is never able to release the lock. The trace is due to `unwind_next_frame`, when it is within `rcu_read_lock`, being an interrupt, where the kworker task never returns to continue the code.
Thus, the problem here is not an actual kernel bug, but a syzkaller-induced scenario where the scheduler is tricked into starving a task that holds an RCU read lock, creating an RCU stall.
|
| 49/2 |
2026/05/30 06:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/locking/lockdep.c Name:lock_acquire]
Results: map[SourceCode:5821: /*
5822: * We are not always called with irqs disabled - do that here,
5823: * and also avoid lockdep recursion:
5824: */
5825: void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
5826: int trylock, int read, int check,
5827: struct lockdep_map *nest_lock, unsigned long ip)
5828: {
5829: unsigned long flags;
5830:
5831: trace_lock_acquire(lock, subclass, trylock, read, check, nest_lock, ip);
5832:
5833: if (!debug_locks)
5834: return;
5835:
5836: /*
5837: * As KASAN instrumentation is disabled and lock_acquire() is usually
5838: * the first lockdep call when a task tries to acquire a lock, add
5839: * kasan_check_byte() here to check for use-after-free and other
5840: * memory errors.
5841: */
5842: kasan_check_byte(lock);
5843:
5844: if (unlikely(!lockdep_enabled())) {
5845: /* XXX allow trylock from NMI ?!? */
5846: if (lockdep_nmi() && !trylock) {
5847: struct held_lock hlock;
5848:
5849: hlock.acquire_ip = ip;
5850: hlock.instance = lock;
5851: hlock.nest_lock = nest_lock;
5852: hlock.irq_context = 2; // XXX
5853: hlock.trylock = trylock;
5854: hlock.read = read;
5855: hlock.check = check;
5856: hlock.hardirqs_off = true;
5857: hlock.references = 0;
5858:
5859: verify_lock_unused(lock, &hlock, subclass);
5860: }
5861: return;
5862: }
5863:
5864: raw_local_irq_save(flags);
5865: check_flags(flags);
5866:
5867: lockdep_recursion_inc();
5868: __lock_acquire(lock, subclass, trylock, read, check,
5869: irqs_disabled_flags(flags), nest_lock, ip, 0, 0, 0);
5870: lockdep_recursion_finish();
5871: raw_local_irq_restore(flags);
5872: }
SourceFile:kernel/locking/lockdep.c]
|
| 50/2 |
2026/05/30 06:04 |
llm |
expert |
0m
Model:
Tokens:
input: 65677
output: 66
thoughts: 2252
Thoughts: **Analysis of the RCU Stall Warning**
Okay, here's what I've figured out. Line 5872, where `lock_acquire` calls `raw_local_irq_restore(flags)`, is key. This re-enables interrupts. Right after that, the pending timer interrupt fires, perfectly timed! The timer interrupt handler runs, and then the task is preempted *exactly* when `lock_acquire` finishes. This explains the RIP at `lock_acquire+0x5e`, corresponding to `raw_local_irq_restore`. So, the task was preempted right after `lock_acquire`. Since `lock_acquire` was called from `rcu_read_lock`, it means the task is now inside an RCU read-side critical section. Then, it's preempted and put on the runqueue, and it starves for 21 seconds. This is a classic starvation scenario.
But, is there a security risk here? No. This is looking like a false positive caused by syzkaller's workload. Syzkaller creates many tasks, and some of them could be SCHED_FIFO or just overload the system. This starves a `kworker` thread. That `kworker` thread happens to be in an RCU read-side critical section, which prevents the RCU grace period from ending and triggers the RCU stall detector. This isn't a problem with the kernel code itself (the `yealink` driver or `evdev`), but rather an artifact of how the scheduler is handling the load. Thus, this is not exploitable and doesn't represent a real denial-of-service vulnerability. An unprivileged user can't easily cause this on a properly configured system without real-time privileges, and even if they could, it's just a CPU starvation issue which is handled by cgroups/quotas in production.
Can an unprivileged user trigger this by plugging in a malicious USB device? If the USB device makes `hub_event` take a long time, it might cause issues, but the `yealink` driver doesn't loop; it just does some allocations and registers an input device. The long execution time we're seeing is because of KASAN and lockdep overhead, coupled with syzkaller's heavy system load. On a production kernel, without those, the code is very fast. So there is no denial of service here.
Let's double-check, in case this is a bug in other functions:
What if `cdev_add` or `device_add` is blocked on a lock? If that was the case, the task state would be either `D` (TASK_UNINTERRUPTIBLE) or `S` (TASK_INTERRUPTIBLE). But the task state is `R` (TASK_RUNNING), so it's not blocked on a lock. It's runnable.
What if the task is actually running and stuck in an infinite loop? If that was the case, it would be running on the CPU. Then, the timer interrupt would still fire on that CPU. The timer interrupt would check if the current task is in an RCU read-side critical section. If it is, and it hasn't called `rcu_read_unlock` for 21 seconds, then, it would trigger an RCU stall. But wait...if it's running on the CPU, the RCU stall detector would report a *CPU* stall, not a *task* stall.
The message says:
```
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: Tasks blocked on level-0 rcu_node (CPUs 0-1):
P3032/1:b..l
```
"Tasks blocked on level-0 rcu_node" means it's a task stall. A task stall *ONLY* happens if the task was preempted (or voluntarily yielded) while inside an RCU read-side critical section. If the task was continuously running on the CPU without being preempted, it would be a CPU stall. It could also be a task stall if the task was preempted, resumed, and preempted again, but never completed the outermost RCU read-side critical section. But `rcu_read_lock` in `unwind_next_frame` is very short-lived. It calls `rcu_read_lock()`, checks things, then `rcu_read_unlock()`. No looping inside the critical section. So, it must have been preempted *inside* this short critical section, and then *never resumed* for 21 seconds. This is the only explanation that makes sense. And it perfectly matches the RIP being at `raw_
|
| 51/2 |
2026/05/30 06:04 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 52/2 |
2026/05/30 06:04 |
llm |
expert |
0m
Model:
Tokens:
input: 69084
output: 696
thoughts: 14
Thoughts: Okay, here's the summary, rendered as a first-person thought process assuming expertise in the relevant field:
**Finalizing the Response**
Alright, the data is in, the analysis is complete, and the core findings are clear. Now it's time to craft the final response. I've considered all the nuances, the potential counterarguments, and the implications of the results. This isn't just about regurgitating findings; it's about synthesizing them into a coherent and compelling narrative that truly reflects the current state of knowledge, that shows the expert nature I've had to develop. The language must be precise, the arguments logically sound, and the conclusions well-supported. I will consider any relevant insights that need to be made. I will consider any limitations in the data. The objective is to make all this information relevant to the user. I can now write the final response.
|
| 53/1 |
2026/05/30 06:05 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the provided bug report and the kernel source code,
here is the assessment of the issue:
**Root Cause Analysis:**
The bug report indicates an RCU task stall (`rcu_preempt detected stalls on
CPUs/tasks: Tasks blocked on level-0 rcu_node`). This specific type of stall
occurs when a task is preempted while inside an RCU read-side critical section
and fails to resume and call `rcu_read_unlock()` within the RCU grace period
timeout (typically 21 seconds).
Looking at the stack trace, the `kworker` thread was executing `usb_probe` for
the `yealink` driver, which eventually called `kmalloc`. Because KASAN is
enabled, `kmalloc` saves the stack trace (`kasan_save_stack` ->
`stack_trace_save` -> `arch_stack_walk` -> `unwind_next_frame`).
Inside `unwind_next_frame`, the ORC unwinder uses `guard(rcu)()` to protect
against module unloading, which internally calls `rcu_read_lock()` and
`rcu_read_unlock()`.
The instruction pointer (`RIP: lock_acquire+0x5e/0x380`) points exactly to the
`raw_local_irq_restore(flags)` call at the end of `lock_acquire` (which is
called by lockdep during `rcu_read_lock`).
What happened is that the moment interrupts were re-enabled by
`raw_local_irq_restore`, a pending timer interrupt fired
(`asm_sysvec_apic_timer_interrupt`). Upon returning from this interrupt
(`irqentry_exit` -> `preempt_schedule_irq`), the kernel decided to preempt the
`kworker` task.
Because the task was preempted exactly after acquiring the RCU read lock but
before releasing it, it was placed on the RCU node's blocked tasks list. The
task state is shown as `state:R running task`, meaning it was placed on the
runqueue but was starved of CPU time for over 21 seconds. This starvation is a
known artifact of fuzzers like syzkaller, which often overload the system with
heavy workloads or real-time (`SCHED_FIFO`) tasks on small VMs, preventing
normal tasks from getting scheduled.
**Security Impact:**
There is no infinite loop, deadlock, or memory corruption in the `yealink`
driver or the USB subsystem in this trace. The code paths involved (like
`usb_probe`, `input_register_device`, and `cdev_add`) only contain small,
bounded loops. The delay is purely a scheduling artifact caused by
fuzzer-induced CPU starvation combined with the heavy overhead of KASAN and
Lockdep.
In a production kernel (without KASAN/Lockdep and without malicious real-time
tasks hogging the CPU), this code path executes extremely fast. Therefore, this
is a false positive and does not represent a genuine vulnerability or a
Denial-of-Service vector.
**Conclusion:**
- **Exploitable:** No
- **Denial of Service:** No (False positive due to fuzzer-induced CPU
starvation)
- **Trigger Mechanisms:** None (Not a bug)]
|