| 0/0 |
2026/05/18 03:19 |
flow |
assessment-security |
40m
Results: map[DenialOfService:true Explanation:The bug report indicates a `BUG: sleeping function called from invalid context`
and `BUG: scheduling while atomic` in the Marvell Libertas WLAN USB driver
(`libertas`).
### Root Cause Analysis
The crash occurs during the firmware loading phase when a matching USB device is
probed.
1. When the device is probed, `if_usb_probe()` initiates asynchronous firmware
loading via `lbs_get_firmware_async()`, which eventually calls
`if_usb_prog_firmware()`.
2. `if_usb_prog_firmware()` submits an RX URB with the completion handler
`if_usb_receive_fwload()` and then sends the first firmware packet using
`if_usb_send_fw_pkt()`.
3. When the USB device responds to the firmware packet, the host controller
completes the RX URB and calls `if_usb_receive_fwload()` in an interrupt/softirq
context (atomic context).
4. `if_usb_receive_fwload()` processes the response and calls
`if_usb_send_fw_pkt()` to send the next firmware block.
5. `if_usb_send_fw_pkt()` calls `usb_tx_block()`, which unconditionally calls
`usb_kill_urb(cardp->tx_urb)` to ensure any previous TX URB is completed before
reusing it.
6. `usb_kill_urb()` is a blocking function that waits for the URB's use count to
drop to zero and explicitly calls `might_sleep()`. Calling a sleeping function
from a softirq context is invalid and triggers the "sleeping function called
from invalid context" warning. Furthermore, if the TX URB is still in use,
`usb_kill_urb()` will actually call `schedule()`, leading to the "scheduling
while atomic" BUG.
### Security Impact
* **Exploitable:** False. This is a classic "sleeping in atomic context" bug.
While it corrupts the scheduling state (calling `schedule()` with a non-zero
`preempt_count`), it does not cause direct memory corruption (like
use-after-free or out-of-bounds access) that could be leveraged for privilege
escalation or arbitrary code execution.
* **Denial of Service:** True. Calling `schedule()` from a softirq context can
lead to system hangs, deadlocks, or a kernel panic (especially if
`panic_on_warn` or `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled).
* **Peripheral Trigger:** True. The bug is triggered during the device probe
phase, which happens automatically when a USB device matching the driver's USB
IDs (e.g., VID=0x1286 PID=0x2001) is physically plugged into the system. A
malicious USB device can easily trigger this by responding to the first firmware
packet before the TX URB has fully completed, forcing the kernel to sleep in the
softirq context.
* **Unprivileged / User Namespace:** False. Triggering this bug requires either
physical access to plug in a USB device or root privileges to emulate one via
`/dev/raw-gadget`. Unprivileged users cannot trigger this on an already-plugged
device because unbinding/rebinding drivers or resetting USB devices requires
root or special group privileges.
* **Network / Remote / Filesystem:** False. The bug occurs strictly during the
USB device initialization and firmware loading phase, before the network
interface is even registered and brought up. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/18 03:19 |
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;
}
uint64_t r[1] = {0xffffffffffffffff};
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;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_usb_connect arguments: [
// speed: usb_device_speed = 0x5 (8 bytes)
// dev_len: len = 0x36 (8 bytes)
// dev: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {12 01 00 02 31 bf ef 20 86 12 01 20 2d 06 00 00 00 01 09 02 24 00 c5 00 00 80 00 09 04 0d 00 02 94 d2 9a 00 09 05 06 02 00 02 0d 00 06 09 05 82 02 00 02 7f} (length 0x34)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000000100, "... [truncated large byte array] ...", 52);
res = -1;
res = syz_usb_connect(/*speed=USB_SPEED_SUPER*/5, /*dev_len=*/0x36, /*dev=*/0x200000000100, /*conn_descs=*/0);
if (res != -1)
r[0] = res;
// syz_usb_ep_write$ath9k_ep1 arguments: [
// fd: fd_usb_ath9k (resource)
// ep: const = 0x82 (1 bytes)
// len: bytesize = 0x0 (8 bytes)
// data: nil
// ]
syz_usb_ep_write(/*fd=*/r[0], /*ep=*/0x82, /*len=*/0, /*data=*/0);
// syz_usb_ep_write$ath9k_ep1 arguments: [
// fd: fd_usb_ath9k (resource)
// ep: const = 0x82 (1 bytes)
// len: bytesize = 0x0 (8 bytes)
// data: nil
// ]
syz_usb_ep_write(/*fd=*/r[0], /*ep=*/0x82, /*len=*/0, /*data=*/0);
return 0;
}
]
|
| 2/1 |
2026/05/18 03:19 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/60a01a07cdc49e17fe4dd5618e5af91d7fa904fe]
|
| 3/1 |
2026/05/18 03:21 |
action |
kernel-builder |
17m
Results: map[KernelObj:/app/workdir/cache/build/e13ed62343b3bae1f7d11cd07e170f4c5e373a26]
|
| 4/1 |
2026/05/18 03:39 |
action |
codesearch-prepare |
12m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/18 03:51 |
agent |
expert |
7m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true 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 greeping
".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).
Don't make assumptions; verify them with source code access. 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 completly 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 tranfers, that's 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 exploitabability 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 Confidetial 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.
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:
usb8xxx: URB in failure status: -2
BUG: sleeping function called from invalid context at drivers/usb/core/urb.c:705
in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/1
preempt_count: 101, expected: 0
RCU nest depth: 0, expected: 0
no locks held by swapper/1/0.
irq event stamp: 298277
hardirqs last enabled at (298276): [<ffffffff876c80e2>] __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:178 [inline]
hardirqs last enabled at (298276): [<ffffffff876c80e2>] _raw_spin_unlock_irqrestore+0x52/0x80 kernel/locking/spinlock.c:194
hardirqs last disabled at (298277): [<ffffffff876c7df2>] __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:130 [inline]
hardirqs last disabled at (298277): [<ffffffff876c7df2>] _raw_spin_lock_irqsave+0x52/0x60 kernel/locking/spinlock.c:162
softirqs last enabled at (298266): [<ffffffff8177002d>] __do_softirq kernel/softirq.c:656 [inline]
softirqs last enabled at (298266): [<ffffffff8177002d>] invoke_softirq kernel/softirq.c:496 [inline]
softirqs last enabled at (298266): [<ffffffff8177002d>] __irq_exit_rcu+0xed/0x150 kernel/softirq.c:723
softirqs last disabled at (298273): [<ffffffff8177002d>] __do_softirq kernel/softirq.c:656 [inline]
softirqs last disabled at (298273): [<ffffffff8177002d>] invoke_softirq kernel/softirq.c:496 [inline]
softirqs last disabled at (298273): [<ffffffff8177002d>] __irq_exit_rcu+0xed/0x150 kernel/softirq.c:723
Preemption disabled at:
[<0000000000000000>] 0x0
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/18/2026
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
__might_resched.cold+0x1ec/0x232 kernel/sched/core.c:8888
usb_kill_urb+0x8e/0x320 drivers/usb/core/urb.c:705
usb_tx_block+0x91/0x320 drivers/net/wireless/marvell/libertas/if_usb.c:429
if_usb_send_fw_pkt.isra.0+0x2e4/0x550 drivers/net/wireless/marvell/libertas/if_usb.c:366
if_usb_receive_fwload+0x5d3/0x780 drivers/net/wireless/marvell/libertas/if_usb.c:592
__usb_hcd_giveback_urb+0x38d/0x610 drivers/usb/core/hcd.c:1657
usb_hcd_giveback_urb+0x3ca/0x4a0 drivers/usb/core/hcd.c:1741
dummy_timer+0xda1/0x36c0 drivers/usb/gadget/udc/dummy_hcd.c:2005
__run_hrtimer kernel/time/hrtimer.c:1785 [inline]
__hrtimer_run_queues+0x50e/0xa70 kernel/time/hrtimer.c:1849
hrtimer_run_softirq+0x17d/0x350 kernel/time/hrtimer.c:1866
handle_softirqs+0x1de/0x9d0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xed/0x150 kernel/softirq.c:723
irq_exit_rcu+0x9/0x30 kernel/softirq.c:739
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1056 [inline]
sysvec_apic_timer_interrupt+0x8f/0xb0 arch/x86/kernel/apic/apic.c:1056
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
Code: ce b0 01 e9 53 e8 02 00 0f 1f 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 66 90 0f 00 2d 33 87 1c 00 fb f4 <c3> cc cc cc cc 66 2e 0f 1f 84 00 00 00 00 00 66 90 90 90 90 90 90
RSP: 0018:ffffc9000013fe00 EFLAGS: 00000242
RAX: 0000000000048d1f RBX: ffff8881022d9d80 RCX: ffffffff8769f8d5
RDX: 0000000000000000 RSI: ffffffff89024e74 RDI: ffffffff87afd320
RBP: 0000000000000000 R08: 0000000000000001 R09: ffffed103eae672d
R10: ffff8881f573396b R11: 0000000000000000 R12: 0000000000000001
R13: ffffed102045b3b0 R14: 0000000000000001 R15: ffffffff8af01fd0
arch_safe_halt arch/x86/include/asm/paravirt.h:73 [inline]
default_idle+0x9/0x10 arch/x86/kernel/process.c:767
default_idle_call+0x6c/0xb0 kernel/sched/idle.c:122
cpuidle_idle_call kernel/sched/idle.c:199 [inline]
do_idle+0x464/0x590 kernel/sched/idle.c:352
cpu_startup_entry+0x4f/0x60 kernel/sched/idle.c:451
start_secondary+0x21d/0x2d0 arch/x86/kernel/smpboot.c:312
common_startup_64+0x13e/0x148
</TASK>
BUG: scheduling while atomic: swapper/1/0/0x00000102
no locks held by swapper/1/0.
Modules linked in:
irq event stamp: 298277
hardirqs last enabled at (298276): [<ffffffff876c80e2>] __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:178 [inline]
hardirqs last enabled at (298276): [<ffffffff876c80e2>] _raw_spin_unlock_irqrestore+0x52/0x80 kernel/locking/spinlock.c:194
hardirqs last disabled at (298277): [<ffffffff876c7df2>] __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:130 [inline]
hardirqs last disabled at (298277): [<ffffffff876c7df2>] _raw_spin_lock_irqsave+0x52/0x60 kernel/locking/spinlock.c:162
softirqs last enabled at (298266): [<ffffffff8177002d>] __do_softirq kernel/softirq.c:656 [inline]
softirqs last enabled at (298266): [<ffffffff8177002d>] invoke_softirq kernel/softirq.c:496 [inline]
softirqs last enabled at (298266): [<ffffffff8177002d>] __irq_exit_rcu+0xed/0x150 kernel/softirq.c:723
softirqs last disabled at (298273): [<ffffffff8177002d>] __do_softirq kernel/softirq.c:656 [inline]
softirqs last disabled at (298273): [<ffffffff8177002d>] invoke_softirq kernel/softirq.c:496 [inline]
softirqs last disabled at (298273): [<ffffffff8177002d>] __irq_exit_rcu+0xed/0x150 kernel/softirq.c:723
Preemption disabled at:
[<0000000000000000>] 0x0
----------------
Code disassembly (best guess), 1 bytes skipped:
0: b0 01 mov $0x1,%al
2: e9 53 e8 02 00 jmp 0x2e85a
7: 0f 1f 00 nopl (%rax)
a: 90 nop
b: 90 nop
c: 90 nop
d: 90 nop
e: 90 nop
f: 90 nop
10: 90 nop
11: 90 nop
12: 90 nop
13: 90 nop
14: 90 nop
15: 90 nop
16: 90 nop
17: 90 nop
18: 90 nop
19: 90 nop
1a: f3 0f 1e fa endbr64
1e: 66 90 xchg %ax,%ax
20: 0f 00 2d 33 87 1c 00 verw 0x1c8733(%rip) # 0x1c875a
27: fb sti
28: f4 hlt
* 29: c3 ret <-- trapping instruction
2a: cc int3
2b: cc int3
2c: cc int3
2d: cc int3
2e: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
35: 00 00 00
38: 66 90 xchg %ax,%ax
3a: 90 nop
3b: 90 nop
3c: 90 nop
3d: 90 nop
3e: 90 nop
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;
}
uint64_t r[1] = {0xffffffffffffffff};
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;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_usb_connect arguments: [
// speed: usb_device_speed = 0x5 (8 bytes)
// dev_len: len = 0x36 (8 bytes)
// dev: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {12 01 00 02 31 bf ef 20 86 12 01 20 2d 06 00 00 00 01 09 02 24 00 c5 00 00 80 00 09 04 0d 00 02 94 d2 9a 00 09 05 06 02 00 02 0d 00 06 09 05 82 02 00 02 7f} (length 0x34)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000000100, "... [truncated large byte array] ...", 52);
res = -1;
res = syz_usb_connect(/*speed=USB_SPEED_SUPER*/5, /*dev_len=*/0x36, /*dev=*/0x200000000100, /*conn_descs=*/0);
if (res != -1)
r[0] = res;
// syz_usb_ep_write$ath9k_ep1 arguments: [
// fd: fd_usb_ath9k (resource)
// ep: const = 0x82 (1 bytes)
// len: bytesize = 0x0 (8 bytes)
// data: nil
// ]
syz_usb_ep_write(/*fd=*/r[0], /*ep=*/0x82, /*len=*/0, /*data=*/0);
// syz_usb_ep_write$ath9k_ep1 arguments: [
// fd: fd_usb_ath9k (resource)
// ep: const = 0x82 (1 bytes)
// len: bytesize = 0x0 (8 bytes)
// data: nil
// ]
syz_usb_ep_write(/*fd=*/r[0], /*ep=*/0x82, /*len=*/0, /*data=*/0);
return 0;
}
Reply: The bug report indicates a `BUG: sleeping function called from invalid context` and `BUG: scheduling while atomic` in the Marvell Libertas WLAN USB driver (`libertas`).
### Root Cause Analysis
The crash occurs during the firmware loading phase when a matching USB device is probed.
1. When the device is probed, `if_usb_probe()` initiates asynchronous firmware loading via `lbs_get_firmware_async()`, which eventually calls `if_usb_prog_firmware()`.
2. `if_usb_prog_firmware()` submits an RX URB with the completion handler `if_usb_receive_fwload()` and then sends the first firmware packet using `if_usb_send_fw_pkt()`.
3. When the USB device responds to the firmware packet, the host controller completes the RX URB and calls `if_usb_receive_fwload()` in an interrupt/softirq context (atomic context).
4. `if_usb_receive_fwload()` processes the response and calls `if_usb_send_fw_pkt()` to send the next firmware block.
5. `if_usb_send_fw_pkt()` calls `usb_tx_block()`, which unconditionally calls `usb_kill_urb(cardp->tx_urb)` to ensure any previous TX URB is completed before reusing it.
6. `usb_kill_urb()` is a blocking function that waits for the URB's use count to drop to zero and explicitly calls `might_sleep()`. Calling a sleeping function from a softirq context is invalid and triggers the "sleeping function called from invalid context" warning. Furthermore, if the TX URB is still in use, `usb_kill_urb()` will actually call `schedule()`, leading to the "scheduling while atomic" BUG.
### Security Impact
* **Exploitable:** False. This is a classic "sleeping in atomic context" bug. While it corrupts the scheduling state (calling `schedule()` with a non-zero `preempt_count`), it does not cause direct memory corruption (like use-after-free or out-of-bounds access) that could be leveraged for privilege escalation or arbitrary code execution.
* **Denial of Service:** True. Calling `schedule()` from a softirq context can lead to system hangs, deadlocks, or a kernel panic (especially if `panic_on_warn` or `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled).
* **Peripheral Trigger:** True. The bug is triggered during the device probe phase, which happens automatically when a USB device matching the driver's USB IDs (e.g., VID=0x1286 PID=0x2001) is physically plugged into the system. A malicious USB device can easily trigger this by responding to the first firmware packet before the TX URB has fully completed, forcing the kernel to sleep in the softirq context.
* **Unprivileged / User Namespace:** False. Triggering this bug requires either physical access to plug in a USB device or root privileges to emulate one via `/dev/raw-gadget`. Unprivileged users cannot trigger this on an already-plugged device because unbinding/rebinding drivers or resetting USB devices requires root or special group privileges.
* **Network / Remote / Filesystem:** False. The bug occurs strictly during the USB device initialization and firmware loading phase, before the network interface is even registered and brought up.
|
| 6/2 |
2026/05/18 03:51 |
llm |
expert |
0m
Model:
Tokens:
input: 14738
output: 54
thoughts: 469
Thoughts: **Analyzing a USB Interrupt Context Bug**
Okay, so I'm looking at this bug report and the core issue is pretty clear: we've got a `BUG: sleeping function called from invalid context` error, specifically at `drivers/usb/core/urb.c:705`. The trace points directly to `usb_kill_urb`, and it's being called from within the `dummy_timer` function, which, as an expert, I immediately recognize as running in interrupt context (likely a softirq) β thatβs a big red flag.
The call chain is crucial here: `usb_kill_urb` within `usb_tx_block`, then `if_usb_send_fw_pkt`, and finally into `if_usb_receive_fwload`. Now, `if_usb_receive_fwload` is the completion handler for a USB URB. The completion handler's invoked by `__usb_hcd_giveback_urb`, and `dummy_timer` calls that function directly. So the URB completion is running in an interrupt context (softirq). The problem is, and as I know from experience, `usb_kill_urb` can potentially sleep, which is strictly prohibited within an interrupt context. That's a textbook violation of kernel rules.
My immediate next step is to dig into `drivers/net/wireless/marvell/libertas/if_usb.c` and figure out *why* `usb_kill_urb` is even being called in this completion handler context. Something is fundamentally wrong with the timing or the logic in this interrupt handler. I need to understand what's triggering the kill of the URB and if we can restructure that part of the code to avoid calling a sleeping function from an interrupt.
|
| 7/2 |
2026/05/18 03:51 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c IncludeLines:true Name:if_usb_receive_fwload]
Results: map[SourceCode: 494: static void if_usb_receive_fwload(struct urb *urb)
495: {
496: struct if_usb_card *cardp = urb->context;
497: struct sk_buff *skb = cardp->rx_skb;
498: struct fwsyncheader *syncfwheader;
499: struct bootcmdresp bootcmdresp;
500:
501: if (urb->status) {
502: lbs_deb_usbd(&cardp->udev->dev,
503: "URB status is failed during fw load\n");
504: kfree_skb(skb);
505: return;
506: }
507:
508: if (cardp->fwdnldover) {
509: __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
510:
511: if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
512: tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
513: pr_info("Firmware ready event received\n");
514: wake_up(&cardp->fw_wq);
515: } else {
516: lbs_deb_usb("Waiting for confirmation; got %x %x\n",
517: le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
518: if_usb_submit_rx_urb_fwload(cardp);
519: }
520: kfree_skb(skb);
521: return;
522: }
523: if (cardp->bootcmdresp <= 0) {
524: memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
525: sizeof(bootcmdresp));
526:
527: if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
528: kfree_skb(skb);
529: if_usb_submit_rx_urb_fwload(cardp);
530: cardp->bootcmdresp = BOOT_CMD_RESP_OK;
531: lbs_deb_usbd(&cardp->udev->dev,
532: "Received valid boot command response\n");
533: return;
534: }
535: if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
536: if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
537: bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
538: bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
539: if (!cardp->bootcmdresp)
540: pr_info("Firmware already seems alive; resetting\n");
541: cardp->bootcmdresp = -1;
542: } else {
543: pr_info("boot cmd response wrong magic number (0x%x)\n",
544: le32_to_cpu(bootcmdresp.magic));
545: }
546: } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) &&
547: (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) &&
548: (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) {
549: pr_info("boot cmd response cmd_tag error (%d)\n",
550: bootcmdresp.cmd);
551: } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) {
552: pr_info("boot cmd response result error (%d)\n",
553: bootcmdresp.result);
554: } else {
555: cardp->bootcmdresp = 1;
556: lbs_deb_usbd(&cardp->udev->dev,
557: "Received valid boot command response\n");
558: }
559: kfree_skb(skb);
560: if_usb_submit_rx_urb_fwload(cardp);
561: return;
562: }
563:
564: syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET,
565: sizeof(struct fwsyncheader), GFP_ATOMIC);
566: if (!syncfwheader) {
567: lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
568: kfree_skb(skb);
569: return;
570: }
571:
572: if (!syncfwheader->cmd) {
573: lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
574: lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
575: le32_to_cpu(syncfwheader->seqnum));
576: cardp->CRC_OK = 1;
577: } else {
578: lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n");
579: cardp->CRC_OK = 0;
580: }
581:
582: kfree_skb(skb);
583:
584: /* Give device 5s to either write firmware to its RAM or eeprom */
585: mod_timer(&cardp->fw_timeout, jiffies + (HZ*5));
586:
587: if (cardp->fwfinalblk) {
588: cardp->fwdnldover = 1;
589: goto exit;
590: }
591:
592: if_usb_send_fw_pkt(cardp);
593:
594: exit:
595: if_usb_submit_rx_urb_fwload(cardp);
596:
597: kfree(syncfwheader);
598: }
SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c]
|
| 8/2 |
2026/05/18 03:51 |
llm |
expert |
0m
Model:
Tokens:
input: 17512
output: 55
thoughts: 32
Thoughts: Okay, here's my interpretation of that code snippet, delivered as if it's my own thought process:
**USB Firmware Loading Sequence Analysis**
Alright, let's see, I'm tracing the firmware loading process here. Looking at `if_usb_receive_fwload`, it's the function handling the incoming firmware update request, naturally. Aha! On line 592, the critical step happens β `if_usb_send_fw_pkt(cardp)` is invoked. So, within the `if_usb_receive_fwload` function, once the initial loading request is processed, the system then calls `if_usb_send_fw_pkt`, passing along the `cardp` which I can infer is some sort of card-specific context or pointer. This implies that the firmware is actually being transmitted in packets via USB using the `if_usb_send_fw_pkt` function. I would bet that `cardp` contains the connection info needed by the `if_usb_send_fw_pkt` to transmit. I wonder if there is a circular buffer of packets to transmit in `cardp`!
|
| 9/2 |
2026/05/18 03:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c IncludeLines:true Name:if_usb_send_fw_pkt]
Results: map[SourceCode: 327: /**
328: * if_usb_send_fw_pkt - download FW
329: * @cardp: pointer to &struct if_usb_card
330: * returns: 0
331: */
332: static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
333: {
334: struct fwdata *fwdata = cardp->ep_out_buf;
335: const uint8_t *firmware = cardp->fw->data;
336:
337: /* If we got a CRC failure on the last block, back
338: up and retry it */
339: if (!cardp->CRC_OK) {
340: cardp->totalbytes = cardp->fwlastblksent;
341: cardp->fwseqnum--;
342: }
343:
344: lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
345: cardp->totalbytes);
346:
347: /* struct fwdata (which we sent to the card) has an
348: extra __le32 field in between the header and the data,
349: which is not in the struct fwheader in the actual
350: firmware binary. Insert the seqnum in the middle... */
351: memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
352: sizeof(struct fwheader));
353:
354: cardp->fwlastblksent = cardp->totalbytes;
355: cardp->totalbytes += sizeof(struct fwheader);
356:
357: memcpy(fwdata->data, &firmware[cardp->totalbytes],
358: le32_to_cpu(fwdata->hdr.datalength));
359:
360: lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
361: le32_to_cpu(fwdata->hdr.datalength));
362:
363: fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
364: cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
365:
366: usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
367: le32_to_cpu(fwdata->hdr.datalength));
368:
369: if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
370: lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
371: lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n",
372: cardp->fwseqnum, cardp->totalbytes);
373: } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
374: lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n");
375: lbs_deb_usb2(&cardp->udev->dev, "Downloading FW JUMP BLOCK\n");
376:
377: cardp->fwfinalblk = 1;
378: }
379:
380: lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
381: cardp->totalbytes);
382:
383: return 0;
384: }
SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c]
|
| 10/2 |
2026/05/18 03:52 |
llm |
expert |
0m
Model:
Tokens:
input: 18947
output: 51
thoughts: 26
Thoughts: Here's my understanding of the situation, tailored for an expert like yourself:
**Analyzing the USB Firmware Packet Transmission**
Okay, so I'm looking at the `if_usb_send_fw_pkt` function. My main concern right now is how these firmware packets are actually getting sent out over the USB connection. I see on line 366 of the relevant code that the function calls `usb_tx_block`. That's the key: `usb_tx_block` is the core function responsible for the actual transmission. So `if_usb_send_fw_pkt` is likely handling packet preparation, setup, and perhaps error checking or handshaking, but the *actual* data transfer to the USB bus is offloaded to `usb_tx_block`. I'll need to dig deeper into `usb_tx_block` to understand the low-level details of how the data is framed, transferred, and if there's any DMA involved. That will be the next place to look for details about the transaction, and any details there regarding flow control or hardware-specific considerations.
|
| 11/2 |
2026/05/18 03:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c IncludeLines:true Name:usb_tx_block]
Results: map[SourceCode: 411: /**
412: * usb_tx_block - transfer the data to the device
413: * @cardp: pointer to &struct if_usb_card
414: * @payload: pointer to payload data
415: * @nb: data length
416: * returns: 0 for success or negative error code
417: */
418: static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb)
419: {
420: int ret;
421:
422: /* check if device is removed */
423: if (cardp->surprise_removed) {
424: lbs_deb_usbd(&cardp->udev->dev, "Device removed\n");
425: ret = -ENODEV;
426: goto tx_ret;
427: }
428:
429: usb_kill_urb(cardp->tx_urb);
430:
431: usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
432: usb_sndbulkpipe(cardp->udev,
433: cardp->ep_out),
434: payload, nb, if_usb_write_bulk_callback, cardp);
435:
436: cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
437:
438: if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
439: lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
440: } else {
441: lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
442: ret = 0;
443: }
444:
445: tx_ret:
446: return ret;
447: }
SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c]
|
| 12/2 |
2026/05/18 03:52 |
llm |
expert |
0m
Model:
Tokens:
input: 19888
output: 51
thoughts: 196
Thoughts: **Analyzing a Critical Bug in the Libertas Driver**
Okay, so I've been digging into this crash report and it's pointing to a really nasty issue within the `libertas` driver, specifically within `if_usb.c`. The problem seems to be a context issue, and I'm seeing it clearly. Line 429 of `usb_tx_block` calls `usb_kill_urb(cardp->tx_urb)`. The issue is, `usb_kill_urb` is known to sleep, and it definitely shouldn't be called from an interrupt context. That's a textbook recipe for disaster.
The kicker is that `if_usb_receive_fwload` is triggered from `__usb_hcd_giveback_urb`, which, as we all know, runs within interrupt context. So, effectively, `usb_kill_urb` *is* being called from an interrupt context, which explains the crash. A classic context violation. We're getting a BUG because the code isn't respecting the execution environment constraints.
This looks like a fundamental flaw in the driver's handling of USB transfers, specifically the asynchronous cancellation of URBs. Now, the obvious question is, is this exploitable? Can a malicious USB device trigger this bug? From what I see, the syzkaller reproducer is using `syz_usb_connect` to simulate a USB device, and it's targeting a Marvell Libertas USB device, or at least one that matches the USB IDs for this driver. This suggests the answer is, in fact, yes. A carefully crafted USB device could exploit this.
I'm going to quickly check `if_usb_probe` to understand how the driver is initialized, how URBs are set up, and how we are getting into this situation from the start. That should give me a better picture of the vulnerability window.
|
| 13/2 |
2026/05/18 03:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c IncludeLines:true Name:if_usb_probe]
Results: map[SourceCode: 187: /**
188: * if_usb_probe - sets the configuration values
189: * @intf: &usb_interface pointer
190: * @id: pointer to usb_device_id
191: * returns: 0 on success, error code on failure
192: */
193: static int if_usb_probe(struct usb_interface *intf,
194: const struct usb_device_id *id)
195: {
196: struct usb_device *udev;
197: struct usb_host_interface *iface_desc;
198: struct usb_endpoint_descriptor *endpoint;
199: struct lbs_private *priv;
200: struct if_usb_card *cardp;
201: int r = -ENOMEM;
202: int i;
203:
204: udev = interface_to_usbdev(intf);
205:
206: cardp = kzalloc_obj(struct if_usb_card);
207: if (!cardp)
208: goto error;
209:
210: timer_setup(&cardp->fw_timeout, if_usb_fw_timeo, 0);
211: init_waitqueue_head(&cardp->fw_wq);
212:
213: cardp->udev = udev;
214: cardp->model = (uint32_t) id->driver_info;
215: iface_desc = intf->cur_altsetting;
216:
217: lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
218: " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
219: le16_to_cpu(udev->descriptor.bcdUSB),
220: udev->descriptor.bDeviceClass,
221: udev->descriptor.bDeviceSubClass,
222: udev->descriptor.bDeviceProtocol);
223:
224: for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
225: endpoint = &iface_desc->endpoint[i].desc;
226: if (usb_endpoint_is_bulk_in(endpoint)) {
227: cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
228: cardp->ep_in = usb_endpoint_num(endpoint);
229:
230: lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in);
231: lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size);
232:
233: } else if (usb_endpoint_is_bulk_out(endpoint)) {
234: cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize);
235: cardp->ep_out = usb_endpoint_num(endpoint);
236:
237: lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out);
238: lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size);
239: }
240: }
241: if (!cardp->ep_out_size || !cardp->ep_in_size) {
242: lbs_deb_usbd(&udev->dev, "Endpoints not found\n");
243: goto dealloc;
244: }
245: if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
246: lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
247: goto dealloc;
248: }
249: if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
250: lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
251: goto dealloc;
252: }
253: cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL);
254: if (!cardp->ep_out_buf) {
255: lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n");
256: goto dealloc;
257: }
258:
259: priv = lbs_add_card(cardp, &intf->dev);
260: if (IS_ERR(priv)) {
261: r = PTR_ERR(priv);
262: goto err_add_card;
263: }
264:
265: cardp->priv = priv;
266:
267: priv->hw_host_to_card = if_usb_host_to_card;
268: priv->enter_deep_sleep = NULL;
269: priv->exit_deep_sleep = NULL;
270: priv->reset_deep_sleep_wakeup = NULL;
271: priv->is_polling = false;
272: #ifdef CONFIG_OLPC
273: if (machine_is_olpc())
274: priv->reset_card = if_usb_reset_olpc_card;
275: #endif
276:
277: cardp->boot2_version = udev->descriptor.bcdDevice;
278:
279: usb_get_dev(udev);
280: usb_set_intfdata(intf, cardp);
281:
282: r = lbs_get_firmware_async(priv, &udev->dev, cardp->model,
283: fw_table, if_usb_prog_firmware);
284: if (r)
285: goto err_get_fw;
286:
287: return 0;
288:
289: err_get_fw:
290: usb_put_dev(udev);
291: lbs_remove_card(priv);
292: err_add_card:
293: if_usb_reset_device(cardp);
294: dealloc:
295: if_usb_free(cardp);
296: kfree(cardp);
297:
298: error:
299: return r;
300: }
SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c]
|
| 14/2 |
2026/05/18 03:52 |
llm |
expert |
0m
Model:
Tokens:
input: 22482
output: 54
thoughts: 32
Thoughts: Okay, here's my interpretation of that code flow, expressed as if it's my own thought process:
**USB Firmware Loading Process**
Right, so when a USB device is probed, `if_usb_probe` kicks things off. My job is to handle the USB initialization, and that means loading the firmware. `if_usb_probe` immediately hands the firmware loading task over to `lbs_get_firmware_async`. I know that this is an asynchronous call, which is sensible given the time it takes to get and load firmware. Eventually β probably in a callback after the firmware is retrieved β `lbs_get_firmware_async` will get around to calling `if_usb_prog_firmware`. That's where the actual heavy lifting happens: flashing the firmware to the device. So, the key is the asynchronous workflow from probe to the actual firmware programming. Nice and efficient.
|
| 15/2 |
2026/05/18 03:52 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c IncludeLines:true Name:if_usb_prog_firmware]
Results: map[SourceCode: 821: static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
822: const struct firmware *fw,
823: const struct firmware *unused)
824: {
825: struct if_usb_card *cardp = priv->card;
826: int i = 0;
827: static int reset_count = 10;
828:
829: if (ret) {
830: pr_err("failed to find firmware (%d)\n", ret);
831: goto done;
832: }
833:
834: cardp->fw = fw;
835: if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
836: ret = -EINVAL;
837: goto done;
838: }
839:
840: /* Cancel any pending usb business */
841: usb_kill_urb(cardp->rx_urb);
842: usb_kill_urb(cardp->tx_urb);
843:
844: cardp->fwlastblksent = 0;
845: cardp->fwdnldover = 0;
846: cardp->totalbytes = 0;
847: cardp->fwfinalblk = 0;
848: cardp->bootcmdresp = 0;
849:
850: restart:
851: if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
852: lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
853: ret = -EIO;
854: goto done;
855: }
856:
857: cardp->bootcmdresp = 0;
858: do {
859: int j = 0;
860: i++;
861: if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
862: /* wait for command response */
863: do {
864: j++;
865: msleep_interruptible(100);
866: } while (cardp->bootcmdresp == 0 && j < 10);
867: } while (cardp->bootcmdresp == 0 && i < 5);
868:
869: if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) {
870: /* Return to normal operation */
871: ret = -EOPNOTSUPP;
872: usb_kill_urb(cardp->rx_urb);
873: usb_kill_urb(cardp->tx_urb);
874: if (if_usb_submit_rx_urb(cardp) < 0)
875: ret = -EIO;
876: goto done;
877: } else if (cardp->bootcmdresp <= 0) {
878: if (--reset_count >= 0) {
879: if_usb_reset_device(cardp);
880: goto restart;
881: }
882: ret = -EIO;
883: goto done;
884: }
885:
886: i = 0;
887:
888: cardp->totalbytes = 0;
889: cardp->fwlastblksent = 0;
890: cardp->CRC_OK = 1;
891: cardp->fwdnldover = 0;
892: cardp->fwseqnum = -1;
893: cardp->totalbytes = 0;
894: cardp->fwfinalblk = 0;
895:
896: /* Send the first firmware packet... */
897: if_usb_send_fw_pkt(cardp);
898:
899: /* ... and wait for the process to complete */
900: wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
901:
902: timer_delete_sync(&cardp->fw_timeout);
903: usb_kill_urb(cardp->rx_urb);
904:
905: if (!cardp->fwdnldover) {
906: pr_info("failed to load fw, resetting device!\n");
907: if (--reset_count >= 0) {
908: if_usb_reset_device(cardp);
909: goto restart;
910: }
911:
912: pr_info("FW download failure, time = %d ms\n", i * 100);
913: ret = -EIO;
914: goto done;
915: }
916:
917: cardp->priv->fw_ready = 1;
918: if_usb_submit_rx_urb(cardp);
919:
920: if (lbs_start_card(priv))
921: goto done;
922:
923: if_usb_setup_firmware(priv);
924:
925: /*
926: * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware.
927: */
928: priv->wol_criteria = EHS_REMOVE_WAKEUP;
929: if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL))
930: priv->ehs_remove_supported = false;
931:
932: done:
933: cardp->fw = NULL;
934: }
SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c]
|
| 16/2 |
2026/05/18 03:52 |
llm |
expert |
0m
Model:
Tokens:
input: 24486
output: 51
thoughts: 847
Thoughts: **Analyzing a USB Firmware Loading Vulnerability**
Okay, so I've been digging into this code, and it looks like there's a serious flaw in how the `libertas` driver handles firmware loading over USB. It starts with `if_usb_prog_firmware`, which initiates the process. First, it submits an RX URB using `if_usb_submit_rx_urb_fwload(cardp)`. Then it sends the first firmware packet via `if_usb_send_fw_pkt(cardp)` and waits for the operation to complete using `wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);`.
The critical part is the RX URB completion handler, `if_usb_receive_fwload`. When the USB device acknowledges the first packet, this handler gets called. Inside, it checks the response. If it's not the final block of firmware data, it calls `if_usb_send_fw_pkt(cardp)` to send the *next* packet. The issue arises within `if_usb_send_fw_pkt`. It calls `usb_tx_block`, which then calls `usb_kill_urb(cardp->tx_urb)`.
The problem? `if_usb_receive_fwload` is running in the *softirq context* because it's called from a URB completion handler. And `usb_kill_urb` *sleeps*. Specifically, it calls `wait_for_completion(&urb->kill_wait)`. This is a clear violation of kernel rules. You simply can't sleep in softirq context. This is exactly what triggers the BUG report: "sleeping function called from invalid context."
Now, this firmware loading happens automatically when the device is probed. So, by simply plugging in *any* malicious USB device, or even a legitimate one that triggers this specific code path (and, yes, it seems that even a legitimate device would), it could potentially trigger this bug during probe. As I understand it, `if_usb_prog_firmware` sends the first packet, the device responds with an acknowledgment, and that acknowledgment triggers the RX completion handler, which then calls code that calls the sleeping `usb_kill_urb`.
The reason `usb_tx_block` calls `usb_kill_urb(cardp->tx_urb)` is to ensure that any previous TX URB is completed before it reuses the URB. Makes sense in theory, but doing this from a completion handler is completely invalid.
Is this exploitable? In a production kernel, it would likely print a warning and continue. However, the `usb_kill_urb` function *will* still actually sleep, which will trigger the scheduling away while in atomic context.
This is a **Denial of Service (DoS)** vulnerability. It's essentially a scheduling conflict. The attempt to schedule in atomic context messes up the system state and could cause a panic if `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled which it is here, hence the BUG report. The bug report initially shows "BUG: sleeping function called from invalid context", and subsequently "BUG: scheduling while atomic: swapper/1/0/0x00000102". Even if `might_sleep()` is compiled out, `schedule()` is still called. `schedule()` then checks `preempt_count()`. If `preempt_count()` is non-zero (which it is in softirq), then it will print "BUG: scheduling while atomic" and might panic if `panic_on_warn` is set, or just continue. If it continues, it will actually schedule another task, which can lead to stack corruption or deadlocks. So, again, it's a DoS.
This bug can be triggered by a peripheral device. Any USB device that matches the driver's USB IDs can trigger this during probe, which is the `libertas` driver (Marvell 8xxx Libertas WLAN driver). I'm going to check the USB IDs for this driver.
|
| 17/2 |
2026/05/18 03:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c IncludeLines:true Name:if_usb_table]
Results: map[SourceCode: 57: static const struct usb_device_id if_usb_table[] = {
58: /* Enter the device signature inside */
59: { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 },
60: { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 },
61: {} /* Terminating entry */
62: };
SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c]
|
| 18/2 |
2026/05/18 03:53 |
llm |
expert |
0m
Model:
Tokens:
input: 26431
output: 43
thoughts: 149
Thoughts: **Analyzing the USB Firmware Download Bug**
Okay, so I'm tracing this bug, and it looks like connecting a USB device with either VID=0x1286, PID=0x2001 or VID=0x05a3, PID=0x8388 initiates the probe. That makes sense, standard device identification. The probe then requests the firmware, and once it's loaded, or if the firmware is already available on the filesystem, the firmware download begins.
The core issue seems to be in the download process itself. The first packet gets sent, and when the device acknowledges, the RX completion handler is responsible for sending the next packet. The interaction between these two elements is what triggers the bug.
Now, a potential avenue of investigation is `usb_kill_urb`. This function is critical when dealing with USB operations, and it's possible it doesn't behave as expected. Specifically, *does* `usb_kill_urb` actually sleep if the URB is already completed? This could be a race condition. I need to dive into the code for `usb_kill_urb` and see how it handles completed URBs to confirm if this is the cause of the problem.
|
| 19/2 |
2026/05/18 03:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/urb.c IncludeLines:true Name:usb_kill_urb]
Results: map[SourceCode: 676: /**
677: * usb_kill_urb - cancel a transfer request and wait for it to finish
678: * @urb: pointer to URB describing a previously submitted request,
679: * may be NULL
680: *
681: * This routine cancels an in-progress request. It is guaranteed that
682: * upon return all completion handlers will have finished and the URB
683: * will be totally idle and available for reuse. These features make
684: * this an ideal way to stop I/O in a disconnect() callback or close()
685: * function. If the request has not already finished or been unlinked
686: * the completion handler will see urb->status == -ENOENT.
687: *
688: * While the routine is running, attempts to resubmit the URB will fail
689: * with error -EPERM. Thus even if the URB's completion handler always
690: * tries to resubmit, it will not succeed and the URB will become idle.
691: *
692: * The URB must not be deallocated while this routine is running. In
693: * particular, when a driver calls this routine, it must insure that the
694: * completion handler cannot deallocate the URB.
695: *
696: * This routine may not be used in an interrupt context (such as a bottom
697: * half or a completion handler), or when holding a spinlock, or in other
698: * situations where the caller can't schedule().
699: *
700: * This routine should not be called by a driver after its disconnect
701: * method has returned.
702: */
703: void usb_kill_urb(struct urb *urb)
704: {
705: might_sleep();
706: if (!(urb && urb->dev && urb->ep))
707: return;
708: atomic_inc(&urb->reject);
709: /*
710: * Order the write of urb->reject above before the read
711: * of urb->use_count below. Pairs with the barriers in
712: * __usb_hcd_giveback_urb() and usb_hcd_submit_urb().
713: */
714: smp_mb__after_atomic();
715:
716: usb_hcd_unlink_urb(urb, -ENOENT);
717: wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
718:
719: atomic_dec(&urb->reject);
720: }
SourceFile:drivers/usb/core/urb.c]
|
| 20/2 |
2026/05/18 03:53 |
llm |
expert |
0m
Model:
Tokens:
input: 27666
output: 27
thoughts: 1272
Thoughts: **Analyzing a USB Firmware Loading Vulnerability**
Okay, let's break this down. `usb_kill_urb` unconditionally calls `might_sleep()`. That's the starting point. If `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled, this is a guaranteed BUG print. Even if it's not enabled, it's going to call `wait_event`, checking if `urb->use_count` is zero. I need to figure out when `urb->use_count` is zero in the context of `if_usb_receive_fwload`.
The flow is: TX URB submitted in `if_usb_send_fw_pkt` (called from `if_usb_prog_firmware`). Then, `if_usb_prog_firmware` waits for the RX URB completion. When the RX URB completes, `if_usb_receive_fwload` gets called. Now, here's the kicker: at *this* point, the TX URB might or might not be finished. If the TX URB is done, `use_count` is zero, `wait_event` *doesn't* sleep, but we *still* get the `might_sleep()` warning if `CONFIG_DEBUG_ATOMIC_SLEEP` is on, or a non-sleep call to the scheduler. If the TX URB *isn't* done, `use_count` is 1, `wait_event` *does* sleep, but it's now a "scheduling while atomic" BUG because it's in softirq context. This is fundamentally bad.
Thinking about it more... `usb_tx_block` is called from `if_usb_send_fw_pkt`. And, critically, `if_usb_send_fw_pkt` is called *both* from `if_usb_prog_firmware` (process context) and from `if_usb_receive_fwload` (softirq context). Bingo. So, in the `if_usb_receive_fwload` path, it's calling `usb_tx_block`, which calls `usb_kill_urb` on the TX URB. Since `if_usb_receive_fwload` runs in response to the *previous* TX block, that TX block *should* be done. But, the completion handler for the TX URB, `if_usb_write_bulk_callback`, *might not* have run yet. If it *has* run, `use_count` is 0. If it *hasn't*, `use_count` is 1, and `wait_event` sleeps. And again, `might_sleep()` is ALWAYS called.
So, in a production kernel, the `might_sleep()` is a no-op, but the `wait_event` *could* sleep if `use_count` is 1. If it's zero, nothing happens. If it's one, we get a "scheduling while atomic" crash/DoS. Now, can we control whether `use_count` is 1? Yes, an attacker with a malicious USB device can make the RX response come back before the host controller has fully processed the TX completion, or if they have bus control, can delay the TX completion to achieve this race. In fact, if the device responds immediately upon receiving the packet, it's possible the RX completion runs *before* the TX completion on the host side, depending on the HCD implementation. If the RX completion runs first, `use_count` will be 1, we get the sleep in softirq, crash/DoS. Even if it doesn't sleep, `usb_kill_urb` from softirq is just fundamentally wrong.
Is this privilege escalation? No, it's a DoS (system hang or panic). So: Exploitable: False, DoS: True. It's not accessible from unprivileged processes (requires plugging in a USB device). User namespaces are irrelevant here. No VM guest trigger, unless the guest has USB access. Not host side in confidential computing. No Ethernet or remote trigger. This *is* a peripheral trigger (USB device), and no malicious filesystem trigger.
Wait... where else can we trigger this? The bug is in `if_usb_receive_fwload`, which is the completion handler for the RX URB. That URB is submitted in `if_usb_submit_rx_urb_fwload`, called from `if_usb_prog_firmware` and from `if_usb_receive_fwload`. This is *only* for firmware loading. So, firmware loading happens in `if_usb_prog_firmware`, which gets called asynchronously from `if_usb_probe` via `lbs_get_firm
|
| 21/2 |
2026/05/18 03:54 |
tool |
codesearch-find-references |
0m
Args: map[Name:if_usb_send_fw_pkt]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:if_usb_prog_firmware SourceFile:drivers/net/wireless/marvell/libertas_tf/if_usb.c SourceLine:872] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:if_usb_receive_fwload SourceFile:drivers/net/wireless/marvell/libertas_tf/if_usb.c SourceLine:570]] TruncatedOutput:false]
|
| 22/2 |
2026/05/18 03:54 |
llm |
expert |
0m
Model:
Tokens:
input: 30138
output: 50
thoughts: 65
Thoughts: **Investigating a Potential Bug Discrepancy**
Okay, so the reported bug references a file path that seems slightly off. It's pointing to `drivers/net/wireless/marvell/libertas/if_usb.c`, but the relevant references, the code I'd expect to be associated with this, are actually in `drivers/net/wireless/marvell/libertas_tf/if_usb.c`. Hmm, this is a bit concerning, because it suggests a potential version mismatch, or perhaps a duplicated function between driver versions. Is this a code clone, or a difference in kernel versions?
The first thing I need to do is quickly compare the code in both paths to see if the vulnerability actually exists in the `libertas_tf` version as well. If it's a simple path discrepancy, that's one thing. If the code is different, I need to understand if the bug is present in one, both, or neither and the nature of any differences. I'm going to start by running a diff. Let's get this sorted out fast.
|
| 23/2 |
2026/05/18 03:55 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c Name:if_usb_send_fw_pkt]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:if_usb_prog_firmware SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c SourceLine:897] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:if_usb_receive_fwload SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c SourceLine:592]] TruncatedOutput:false]
|
| 24/2 |
2026/05/18 03:55 |
llm |
expert |
1m
Model:
Tokens:
input: 30627
output: 49
thoughts: 1018
Thoughts: **Analysis of the "Sleeping in Atomic Context" USB Driver Bug**
Okay, so I've traced this bug down. It's triggered from both `if_usb_prog_firmware` and `if_usb_receive_fwload` within the USB drivers. The problem boils down to `if_usb_receive_fwload` being an URB completion handler, which means it runs in softirq context. This handler calls `usb_tx_block`, which then calls `usb_kill_urb`. The crux of the issue is that `usb_kill_urb` calls `wait_event`, leading to a potential `schedule()` call while in an atomic context β classic "sleeping in atomic context" problem.
My initial thought is, can this be exploited for anything beyond a Denial of Service? Let's break down `usb_kill_urb`: It increments `urb->reject`, unlinks the URB, and then *waits* using `wait_event` until `urb->use_count` hits zero. In a softirq, this `wait_event` *will* call `schedule()`. As we know, calling `schedule()` from a softirq is generally a really bad idea, right? Usually, you end up with a kernel panic if `panic_on_warn` is set, or it schedules a new task on the same CPU. While the bug report doesn't mention spinlocks, softirqs *aren't* designed to be preempted by `schedule()`. The `preempt_count` will still have the softirq bit set. If another task runs with that bit set, it can throw off so many kernel subsystems that it will eventually crash or deadlock. However, this is not a memory corruption bug. No use-after-free, no buffer overflow, nothing exploitable to gain privilege escalation.
Now, let's double-check about memory corruption. Inside `usb_tx_block`, we see `usb_kill_urb(cardp->tx_urb)` followed by the re-use of the URB with `usb_fill_bulk_urb` and finally `usb_submit_urb`. If `usb_kill_urb` *didn't* wait, we'd have a race condition that could lead to URB corruption. But no, `usb_kill_urb` *does* wait, specifically using `wait_event`, which blocks until `use_count` is zero. So, the code will *wait* for the URB to complete before it starts using it again. No memory corruption there.
Okay, let's summarize the impact:
* **Exploitable: False** (no privilege escalation)
* **Denial of Service: True** (system crash/hang)
* **Unprivileged: False** (requires physical USB access or root for raw-gadget)
* **User Namespace: False**
* **VM Guest Trigger: False** (unless USB device is passed through)
* **VM Host Trigger: False**
* **Network Trigger: False**
* **Remote Trigger: False**
* **Peripheral Trigger: True** (requires physical USB device)
* **Filesystem Trigger: False**
Let's check the potential scope of this bug beyond a standard USB connection. Considering the `libertas` driver β it's a WLAN driver and registers a network interface. Could network traffic trigger it? No, because the bug is in `if_usb_receive_fwload`, only used during firmware loading. Firmware loading *precedes* the network interface being up. After the firmware is loaded, `if_usb_prog_firmware` calls `lbs_start_card(priv)`, which then registers the network device. The `if_usb_receive_fwload` function is *no longer* used. So, no, it can't be triggered by network traffic after the firmware is loaded.
We've confirmed `if_usb_receive_fwload` is *only* called during firmware loading. Conclusion: This bug isn't a widespread vulnerability; its impact is limited to denial-of-service, and it's most likely to affect specific USB devices during firmware update operations.
|
| 25/2 |
2026/05/18 03:56 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/net/wireless/marvell/libertas/if_usb.c Name:if_usb_receive_fwload]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:if_usb_submit_rx_urb_fwload SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c SourceLine:486]] TruncatedOutput:false]
|
| 26/2 |
2026/05/18 03:56 |
llm |
expert |
1m
Model:
Tokens:
input: 32688
output: 26
thoughts: 258
Thoughts: **Analyzing the Firmware Loading Flow**
Okay, so I'm digging into this `if_usb_submit_rx_urb_fwload` function. Looks like it's pretty isolated. It's only used within `if_usb_submit_rx_urb_fwload` itself, and that function is only called from `if_usb_prog_firmware` and `if_usb_receive_fwload`. That's a strong indicator that this specific URB submission is *solely* for the firmware loading sequence, which makes a lot of sense, considering the function's name.
The question then becomes: is this the *only* way the firmware gets loaded? My initial instinct is to look for other triggering mechanisms. I know `lbs_get_firmware_async` is called from `if_usb_probe`, which seems like a more generic driver initialization. But are there *other* callers of `lbs_get_firmware_async` that might bypass the usual `if_usb_submit_rx_urb_fwload` flow? This seems like the key question here, as `lbs_get_firmware_async` could potentially be a backdoor. I need to trace *all* possible firmware loading paths to confirm this is the *only* place the rx URB is submitted during firmware loading.
|
| 27/2 |
2026/05/18 03:57 |
tool |
codesearch-find-references |
0m
Args: map[Name:lbs_get_firmware_async]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:if_sdio_prog_firmware SourceFile:drivers/net/wireless/marvell/libertas/if_sdio.c SourceLine:736] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:if_usb_probe SourceFile:drivers/net/wireless/marvell/libertas/if_usb.c SourceLine:282]] TruncatedOutput:false]
|
| 28/2 |
2026/05/18 03:57 |
llm |
expert |
1m
Model:
Tokens:
input: 33398
output: 66
thoughts: 968
Thoughts: **Analysis of the USB Driver Firmware Loading Vulnerability**
Okay, so looking at this, it's clear the issue centers around `if_usb_probe` and `if_sdio_prog_firmware`, and specifically, we're dealing with the USB driver here. The `if_usb_probe` function is only executed during the device probing phase β that's when a device is plugged in. This immediately pins this down as a Peripheral Trigger.
Let's run through the questions to confirm our assessment: Exploitable? Nope, this is a sleep in atomic context β it causes a warning, potentially a crash or hang (DoS), but it won't lead to memory corruption or privilege escalation. Denial of Service? Yep, definitely a DoS. Filesystem, Network, Remote, VMGuest, VMHost triggers? Nope to all those, clearly not. Peripheral Trigger? Absolutely. Unprivileged? No. UserNamespace? No.
Hold on, let me quickly make sure about `usb_kill_urb`. Could it possibly be called with `use_count == 0`, and not actually sleep, but still trigger a `might_sleep()` warning? Yes, `might_sleep()` prints the warning regardless. But the bug report... right, it shows `"scheduling while atomic"`. That confirms it's actually calling `schedule()`. The `wait_event` condition was false (meaning `use_count != 0`), so it *did* sleep. This definitively confirms the DoS scenario.
Okay, now, is there any way an unprivileged user can trigger this on an already plugged-in device? Unbinding and rebinding a driver generally requires root, through writing to `/sys/bus/usb/drivers/.../unbind` and `bind`. And resetting the USB device via usbfs (`USBDEVFS_RESET`) requires write access to the USB device node, which is usually root or a special group. Nope, an unprivileged user is blocked.
Hmm, what about firmware loading failures and retries? Let's check the code... `if (!cardp->fwdnldover) { ... --reset_count >= 0 ... }`. The device resets and retries up to 10 times. That's all part of the probe, still.
Could a malicious USB device somehow exploit this to gain code execution? When `schedule()` is called from softirq, the kernel state is inconsistent. If an attacker can control what executes next, or if the softirq had a lock that another CPU later acquires, there could be a deadlock. But that's still DoS. Can it lead to memory corruption? If the softirq is interrupted and another task runs, the softirq will eventually resume. But the bug report said `"no locks held by swapper/1/0"`, so no immediate lock deadlock. However, the `preempt_count` is wrong. I don't see a direct path to memory corruption here. It's the standard "scheduling while atomic" bug. So, Exploitable is still False.
Alright, let's write this up precisely. The bug happens when `usb_kill_urb()` gets called from `usb_tx_block()`, which is called by `if_usb_send_fw_pkt()`, which is called by `if_usb_receive_fwload()`. `if_usb_receive_fwload()` is an URB completion handler, and hence atomic. And `usb_kill_urb()` can sleep and *always* calls `might_sleep()`, and that's the problem. It can be triggered by a malicious USB device during firmware loading, which is probe time. The impact is DoS β kernel warning, scheduling while atomic, potential panic or hang. Not exploitable for privilege escalation. And, definitely requires physical access to plug the USB device. It is a Peripheral Trigger.
|
| 29/2 |
2026/05/18 03:58 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 30/2 |
2026/05/18 03:58 |
llm |
expert |
1m
Model:
Tokens:
input: 35317
output: 734
thoughts: 13
Thoughts: Okay, here's the summary, formatted as you requested:
**Final Reasoning: Now We Put It All Together**
Alright, the data is in, the calculations are complete. No more adjustments needed β the outcome is fixed. Now comes the part I always enjoy: synthesizing everything into a coherent, defensible narrative. It's time to build the case, to lay out the final reasoning that connects the dots and justifies the conclusion. This isn't just a regurgitation of the data; itβs about drawing the essential linkages, weighing the nuances, and presenting a compelling argument that demonstrates a complete understanding of the topic at hand. It's about showing *why* the results are what they are, not just *that* they are. This is where the real expertise comes through β connecting evidence to the established body of knowledge, anticipating potential counterarguments, and ensuring the final analysis is both rigorous and easily understood. Let's get to work.
|
| 31/1 |
2026/05/18 03:59 |
action |
format-explanation |
0m
Results: map[Explanation:The bug report indicates a `BUG: sleeping function called from invalid context`
and `BUG: scheduling while atomic` in the Marvell Libertas WLAN USB driver
(`libertas`).
### Root Cause Analysis
The crash occurs during the firmware loading phase when a matching USB device is
probed.
1. When the device is probed, `if_usb_probe()` initiates asynchronous firmware
loading via `lbs_get_firmware_async()`, which eventually calls
`if_usb_prog_firmware()`.
2. `if_usb_prog_firmware()` submits an RX URB with the completion handler
`if_usb_receive_fwload()` and then sends the first firmware packet using
`if_usb_send_fw_pkt()`.
3. When the USB device responds to the firmware packet, the host controller
completes the RX URB and calls `if_usb_receive_fwload()` in an interrupt/softirq
context (atomic context).
4. `if_usb_receive_fwload()` processes the response and calls
`if_usb_send_fw_pkt()` to send the next firmware block.
5. `if_usb_send_fw_pkt()` calls `usb_tx_block()`, which unconditionally calls
`usb_kill_urb(cardp->tx_urb)` to ensure any previous TX URB is completed before
reusing it.
6. `usb_kill_urb()` is a blocking function that waits for the URB's use count to
drop to zero and explicitly calls `might_sleep()`. Calling a sleeping function
from a softirq context is invalid and triggers the "sleeping function called
from invalid context" warning. Furthermore, if the TX URB is still in use,
`usb_kill_urb()` will actually call `schedule()`, leading to the "scheduling
while atomic" BUG.
### Security Impact
* **Exploitable:** False. This is a classic "sleeping in atomic context" bug.
While it corrupts the scheduling state (calling `schedule()` with a non-zero
`preempt_count`), it does not cause direct memory corruption (like
use-after-free or out-of-bounds access) that could be leveraged for privilege
escalation or arbitrary code execution.
* **Denial of Service:** True. Calling `schedule()` from a softirq context can
lead to system hangs, deadlocks, or a kernel panic (especially if
`panic_on_warn` or `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled).
* **Peripheral Trigger:** True. The bug is triggered during the device probe
phase, which happens automatically when a USB device matching the driver's USB
IDs (e.g., VID=0x1286 PID=0x2001) is physically plugged into the system. A
malicious USB device can easily trigger this by responding to the first firmware
packet before the TX URB has fully completed, forcing the kernel to sleep in the
softirq context.
* **Unprivileged / User Namespace:** False. Triggering this bug requires either
physical access to plug in a USB device or root privileges to emulate one via
`/dev/raw-gadget`. Unprivileged users cannot trigger this on an already-plugged
device because unbinding/rebinding drivers or resetting USB devices requires
root or special group privileges.
* **Network / Remote / Filesystem:** False. The bug occurs strictly during the
USB device initialization and firmware loading phase, before the network
interface is even registered and brought up.]
|