| 0/0 |
2026/07/29 07:32 |
flow |
patching |
2h22m
Results: map[AckedBy:[] Fixes:map[AuthorEmail:andreyknvl@google.com AuthorName:Andrey Konovalov Hash:f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10 Title:usb: gadget: add raw-gadget interface] KernelBranch:master KernelCommit:f5098b6bae761e346ebcd9da7f95622c04733cff KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:usb: raw-gadget: fix use-after-free during gadget unbind
A use-after-free vulnerability occurs in the raw-gadget driver when the
underlying USB Device Controller (UDC) is unbound while concurrent ioctls
are in progress.
When the UDC is unbound, gadget_unbind() is called. However, this function
fails to clear the dev->gadget pointer and update dev->state. It only
clears the driver data. After gadget_unbind() returns, the UDC core frees
the struct usb_gadget and its associated device name string.
If a concurrent ioctl (e.g., USB_RAW_IOCTL_EP0_READ) is executing, it may
pass the state checks because dev->state was not updated, and proceed to
use the dangling dev->gadget pointer. If usb_ep_queue() fails (e.g., due to
the UDC being disconnected), the error path attempts to print an error
message using dev_err(&dev->gadget->dev, ...), which dereferences the freed
device name string, resulting in a KASAN slab-use-after-free crash:
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648
[inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
...
Call Trace:
<TASK>
...
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
...
To fix this, gadget_unbind() must clear dev->gadget and update dev->state
to STATE_DEV_FAILED under dev->lock.
To prevent race conditions where an ioctl has already passed the checks but
hasn't yet called usb_ep_queue(), a synchronization mechanism is
introduced. A udc_usage counter tracks ongoing UDC calls. gadget_unbind()
waits for this counter to drop to zero before returning.
To avoid deadlocks where ioctls are waiting for I/O completion,
gadget_unbind() explicitly dequeues all pending requests before waiting for
udc_usage to drop. This forces the UDC core to complete the requests
immediately, waking up the waiting ioctls so they can decrement udc_usage
and signal the completion.
Additionally, if an ioctl is interrupted by a signal, it checks if
dev->state == STATE_DEV_FAILED. If so, it knows gadget_unbind() is handling
the dequeue and simply waits for the completion handler to finish. Finally,
proper cleanup is added in gadget_bind() if the connect event fails to
queue, and dev_err() calls are updated to use dev->dev instead of
dev->gadget->dev to avoid dereferencing the gadget pointer in error paths. PatchDiff:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..bce4f8d3f 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,47 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +671,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +757,37 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -750,6 +799,9 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
@@ -956,11 +1008,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1165,37 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
@@ -1136,6 +1207,9 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
Recipients:[map[Email:andreyknvl@gmail.com Name:Andrey Konovalov To:false] map[Email:gregkh@linuxfoundation.org Name:Greg Kroah-Hartman To:true] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:krishnagopi487@gmail.com Name:Gopi Krishna Menon To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:linux-usb@vger.kernel.org Name: To:true] map[Email:andreyknvl@google.com Name:Andrey Konovalov To:true]] ReportedBy:[] ReviewedBy:[] TestedBy:[]]
|
| 1/1 |
2026/07/29 07:32 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:f5098b6bae761e346ebcd9da7f95622c04733cff KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/07/29 07:32 |
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;
}
struct vusb_descriptor {
uint8_t req_type;
uint8_t desc_type;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_descriptors {
uint32_t len;
struct vusb_descriptor* generic;
struct vusb_descriptor* descs[0];
} __attribute__((packed));
struct vusb_response {
uint8_t type;
uint8_t req;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_responses {
uint32_t len;
struct vusb_response* generic;
struct vusb_response* resps[0];
} __attribute__((packed));
static bool lookup_control_response(const struct vusb_descriptors* descs, const struct vusb_responses* resps,
struct usb_ctrlrequest* ctrl, char** response_data, uint32_t* response_length)
{
int descs_num = 0;
int resps_num = 0;
if (descs)
descs_num = (descs->len - offsetof(struct vusb_descriptors, descs)) / sizeof(descs->descs[0]);
if (resps)
resps_num = (resps->len - offsetof(struct vusb_responses, resps)) / sizeof(resps->resps[0]);
uint8_t req = ctrl->bRequest;
uint8_t req_type = ctrl->bRequestType & USB_TYPE_MASK;
uint8_t desc_type = ctrl->wValue >> 8;
if (req == USB_REQ_GET_DESCRIPTOR) {
int i;
for (i = 0; i < descs_num; i++) {
struct vusb_descriptor* desc = descs->descs[i];
if (!desc)
continue;
if (desc->req_type == req_type && desc->desc_type == desc_type) {
*response_length = desc->len;
if (*response_length != 0)
*response_data = &desc->data[0];
else
*response_data = NULL;
return true;
}
}
if (descs && descs->generic) {
*response_data = &descs->generic->data[0];
*response_length = descs->generic->len;
return true;
}
} else {
int i;
for (i = 0; i < resps_num; i++) {
struct vusb_response* resp = resps->resps[i];
if (!resp)
continue;
if (resp->type == req_type && resp->req == req) {
*response_length = resp->len;
if (*response_length != 0)
*response_data = &resp->data[0];
else
*response_data = NULL;
return true;
}
}
if (resps && resps->generic) {
*response_data = &resps->generic->data[0];
*response_length = resps->generic->len;
return true;
}
}
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_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_interface(int fd, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
for (int i = 0; i < index->ifaces_num; i++) {
if (index->ifaces[i].bInterfaceNumber == bInterfaceNumber &&
index->ifaces[i].bAlternateSetting == bAlternateSetting)
return i;
}
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_control_io(volatile long a0, volatile long a1, volatile long a2)
{
int fd = a0;
const struct vusb_descriptors* descs = (const struct vusb_descriptors*)a1;
const struct vusb_responses* resps = (const struct vusb_responses*)a2;
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = USB_MAX_PACKET_SIZE;
int rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL) {
return -1;
}
char* response_data = NULL;
uint32_t response_length = 0;
if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) {
if (!lookup_control_response(descs, resps, &event.ctrl, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
return -1;
}
} else {
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD ||
event.ctrl.bRequest == USB_REQ_SET_INTERFACE) {
int iface_num = event.ctrl.wIndex;
int alt_set = event.ctrl.wValue;
int iface_index = lookup_interface(fd, iface_num, alt_set);
if (iface_index < 0) {
} else {
set_interface(fd, iface_index);
}
}
response_length = event.ctrl.wLength;
}
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;
if ((event.ctrl.bRequestType & USB_DIR_IN) && !event.ctrl.wLength) {
response_length = USB_MAX_PACKET_SIZE;
}
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) && event.ctrl.wLength) {
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 0;
}
uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*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=*/0x1000, /*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$cdc_ecm arguments: [
// speed: usb_device_speed = 0x0 (8 bytes)
// dev_len: len = 0x4d (8 bytes)
// dev: ptr[in, usb_device_descriptor_cdc_ecm] {
// usb_device_descriptor_cdc_ecm {
// inner: usb_device_descriptor_verbose_t[flags[usb_versions, int16], USB_CLASS_COMM, 0, 0, flags[usb_device_max_packet_sizes, int8], 0x525, 0xa4a1, 64, array[usb_config_descriptor_cdc_ecm, 1]] {
// bLength: const = 0x12 (1 bytes)
// bDescriptorType: const = 0x1 (1 bytes)
// bcdUSB: usb_versions = 0x0 (2 bytes)
// bDeviceClass: const = 0x2 (1 bytes)
// bDeviceSubClass: const = 0x0 (1 bytes)
// bDeviceProtocol: const = 0x0 (1 bytes)
// bMaxPacketSize0: usb_device_max_packet_sizes = 0x40 (1 bytes)
// idVendor: const = 0x525 (2 bytes)
// idProduct: const = 0xa4a1 (2 bytes)
// bcdDevice: const = 0x40 (2 bytes)
// iManufacturer: const = 0x0 (1 bytes)
// iProduct: const = 0x0 (1 bytes)
// iSerialNumber: const = 0xffffffffffff8001 (1 bytes)
// bNumConfigurations: len = 0x1 (1 bytes)
// configs: array[usb_config_descriptor_cdc_ecm] {
// usb_config_descriptor_cdc_ecm {
// inner: usb_config_descriptor_verbose_t[const[1, int8], len[interfaces, int8], int8, flags[usb_config_attributes, int8], int8, void, array[usb_interface_descriptor_cdc_ecm, 1]] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x2 (1 bytes)
// wTotalLength: len = 0x3b (2 bytes)
// bNumInterfaces: len = 0x1 (1 bytes)
// bConfigurationValue: const = 0x1 (1 bytes)
// iConfiguration: int8 = 0x0 (1 bytes)
// bmAttributes: usb_config_attributes = 0x0 (1 bytes)
// bMaxPower: int8 = 0x0 (1 bytes)
// extra: buffer: {} (length 0x0)
// interfaces: array[usb_interface_descriptor_cdc_ecm] {
// usb_interface_descriptor_cdc_ecm {
// inner: usb_interface_descriptor_verbose_t[const[0, int8], int8, int8[2:3], const[USB_CLASS_COMM, int8], const[USB_CDC_SUBCLASS_ETHERNET, int8], const[USB_CDC_PROTO_NONE, int8], int8, usb_cdc_header_ecm, usb_endpoint_descriptors_cdc_ecm] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x4 (1 bytes)
// bInterfaceNumber: const = 0x0 (1 bytes)
// bAlternateSetting: int8 = 0x0 (1 bytes)
// bNumEndpoints: int8 = 0x12 (1 bytes)
// bInterfaceClass: const = 0x2 (1 bytes)
// bInterfaceSubClass: const = 0x6 (1 bytes)
// bInterfaceProtocol: const = 0x0 (1 bytes)
// iInterface: int8 = 0x0 (1 bytes)
// extra: usb_cdc_header_ecm {
// union: usb_cdc_union_desc_t[0, 0] {
// bLength: len = 0x5 (1 bytes)
// bDescriptorType: const = 0x24 (1 bytes)
// bDescriptorSubType: const = 0x6 (1 bytes)
// bMasterInterface0: const = 0x0 (1 bytes)
// bSlaveInterface0: const = 0x0 (1 bytes)
// slave_interfaces: buffer: {} (length 0x0)
// }
// header: usb_cdc_header_desc {
// bLength: len = 0x5 (1 bytes)
// bDescriptorType: const = 0x24 (1 bytes)
// bDescriptorSubType: const = 0x0 (1 bytes)
// bcdCDC: int16 = 0x0 (2 bytes)
// }
// ether: usb_cdc_ether_desc {
// bLength: len = 0xd (1 bytes)
// bDescriptorType: const = 0x24 (1 bytes)
// bDescriptorSubType: const = 0xf (1 bytes)
// iMACAddress: const = 0x1 (1 bytes)
// bmEthernetStatistics: int32 = 0x0 (4 bytes)
// wMaxSegmentSize: int16 = 0x0 (2 bytes)
// wNumberMCFilters: int16 = 0x0 (2 bytes)
// bNumberPowerFilters: int8 = 0x0 (1 bytes)
// }
// other: array[usb_cdc_header_ecm_other] {
// }
// }
// endpoints: usb_endpoint_descriptors_cdc_ecm {
// notify: array[usb_endpoint_descriptor_cdc_ecm_notify] {
// }
// in: usb_endpoint_descriptor_cdc_ecm_in {
// inner: usb_endpoint_descriptor_verbose_t[const[USB_ENDPOINT_CDC_ECM_IN_ADDRESS, int8], const[USB_ENDPOINT_CDC_ECM_DATA_ATTRIBUTES, int8], flags[usb_endpoint_max_packet_sizes, int16], int8, int8, int8, void] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x5 (1 bytes)
// bEndpointAddress: const = 0x82 (1 bytes)
// bmAttributes: const = 0x2 (1 bytes)
// wMaxPacketSize: usb_endpoint_max_packet_sizes = 0x200 (2 bytes)
// bInterval: int8 = 0x0 (1 bytes)
// bRefresh: int8 = 0x0 (1 bytes)
// bSynchAddress: int8 = 0x0 (1 bytes)
// extra: buffer: {} (length 0x0)
// }
// }
// out: usb_endpoint_descriptor_cdc_ecm_out {
// inner: usb_endpoint_descriptor_verbose_t[const[USB_ENDPOINT_CDC_ECM_OUT_ADDRESS, int8], const[USB_ENDPOINT_CDC_ECM_DATA_ATTRIBUTES, int8], flags[usb_endpoint_max_packet_sizes, int16], int8, int8, int8, void] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x5 (1 bytes)
// bEndpointAddress: const = 0x3 (1 bytes)
// bmAttributes: const = 0x2 (1 bytes)
// wMaxPacketSize: usb_endpoint_max_packet_sizes = 0x200 (2 bytes)
// bInterval: int8 = 0x0 (1 bytes)
// bRefresh: int8 = 0x0 (1 bytes)
// bSynchAddress: int8 = 0x0 (1 bytes)
// extra: buffer: {} (length 0x0)
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb_cdc_ecm
*(uint8_t*)0x200000000000 = 0x12;
*(uint8_t*)0x200000000001 = 1;
*(uint16_t*)0x200000000002 = 0;
*(uint8_t*)0x200000000004 = 2;
*(uint8_t*)0x200000000005 = 0;
*(uint8_t*)0x200000000006 = 0;
*(uint8_t*)0x200000000007 = 0x40;
*(uint16_t*)0x200000000008 = 0x525;
*(uint16_t*)0x20000000000a = 0xa4a1;
*(uint16_t*)0x20000000000c = 0x40;
*(uint8_t*)0x20000000000e = 0;
*(uint8_t*)0x20000000000f = 0;
*(uint8_t*)0x200000000010 = 1;
*(uint8_t*)0x200000000011 = 1;
*(uint8_t*)0x200000000012 = 9;
*(uint8_t*)0x200000000013 = 2;
*(uint16_t*)0x200000000014 = 0x3b;
*(uint8_t*)0x200000000016 = 1;
*(uint8_t*)0x200000000017 = 1;
*(uint8_t*)0x200000000018 = 0;
*(uint8_t*)0x200000000019 = 0;
*(uint8_t*)0x20000000001a = 0;
*(uint8_t*)0x20000000001b = 9;
*(uint8_t*)0x20000000001c = 4;
*(uint8_t*)0x20000000001d = 0;
*(uint8_t*)0x20000000001e = 0;
*(uint8_t*)0x20000000001f = 0x12;
*(uint8_t*)0x200000000020 = 2;
*(uint8_t*)0x200000000021 = 6;
*(uint8_t*)0x200000000022 = 0;
*(uint8_t*)0x200000000023 = 0;
*(uint8_t*)0x200000000024 = 5;
*(uint8_t*)0x200000000025 = 0x24;
*(uint8_t*)0x200000000026 = 6;
*(uint8_t*)0x200000000027 = 0;
*(uint8_t*)0x200000000028 = 0;
*(uint8_t*)0x200000000029 = 5;
*(uint8_t*)0x20000000002a = 0x24;
*(uint8_t*)0x20000000002b = 0;
*(uint16_t*)0x20000000002c = 0;
*(uint8_t*)0x20000000002e = 0xd;
*(uint8_t*)0x20000000002f = 0x24;
*(uint8_t*)0x200000000030 = 0xf;
*(uint8_t*)0x200000000031 = 1;
*(uint32_t*)0x200000000032 = 0;
*(uint16_t*)0x200000000036 = 0;
*(uint16_t*)0x200000000038 = 0;
*(uint8_t*)0x20000000003a = 0;
*(uint8_t*)0x20000000003b = 9;
*(uint8_t*)0x20000000003c = 5;
*(uint8_t*)0x20000000003d = 0x82;
*(uint8_t*)0x20000000003e = 2;
*(uint16_t*)0x20000000003f = 0x200;
*(uint8_t*)0x200000000041 = 0;
*(uint8_t*)0x200000000042 = 0;
*(uint8_t*)0x200000000043 = 0;
*(uint8_t*)0x200000000044 = 9;
*(uint8_t*)0x200000000045 = 5;
*(uint8_t*)0x200000000046 = 3;
*(uint8_t*)0x200000000047 = 2;
*(uint16_t*)0x200000000048 = 0x200;
*(uint8_t*)0x20000000004a = 0;
*(uint8_t*)0x20000000004b = 0;
*(uint8_t*)0x20000000004c = 0;
res = -1;
res = syz_usb_connect(/*speed=*/0, /*dev_len=*/0x4d, /*dev=*/0x200000000000, /*conn_descs=*/0);
if (res != -1)
r[0] = res;
// syz_usb_control_io$cdc_ecm arguments: [
// fd: fd_usb_cdc_ecm (resource)
// descs: nil
// resps: nil
// ]
syz_usb_control_io(/*fd=*/r[0], /*descs=*/0, /*resps=*/0);
// mkdir arguments: [
// path: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 00} (length 0x6)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000100, "./sys\000", 6);
syscall(__NR_mkdir, /*path=*/0x200000000100ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// mount arguments: [
// src: nil
// dst: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 00} (length 0x6)
// }
// type: ptr[in, buffer] {
// buffer: {73 79 73 66 73 00} (length 0x6)
// }
// flags: mount_flags = 0x0 (8 bytes)
// data: nil
// ]
memcpy((void*)0x200000000100, "./sys\000", 6);
memcpy((void*)0x200000000200, "sysfs\000", 6);
syscall(__NR_mount, /*src=*/0ul, /*dst=*/0x200000000100ul, /*type=*/0x200000000200ul, /*flags=*/0ul, /*data=*/0ul);
// openat arguments: [
// fd: fd_dir (resource)
// file: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 2f 62 75 73 2f 70 6c 61 74 66 6f 72 6d 2f 64 72 69 76 65 72 73 2f 64 75 6d 6d 79 5f 75 64 63 2f 75 6e 62 69 6e 64 00} (length 0x2c)
// }
// flags: open_flags = 0x1 (4 bytes)
// mode: open_mode = 0x0 (2 bytes)
// ]
// returns fd
memcpy((void*)0x200000000300, "./sys/bus/platform/drivers/dummy_udc/unbind\000", 44);
res = syscall(__NR_openat, /*fd=*/0xffffff9c, /*file=*/0x200000000300ul, /*flags=O_WRONLY*/1, /*mode=*/0);
if (res != -1)
r[1] = res;
// write arguments: [
// fd: fd (resource)
// buf: ptr[in, buffer] {
// buffer: {64 75 6d 6d 79 5f 75 64 63 2e 30} (length 0xb)
// }
// count: len = 0xb (8 bytes)
// ]
memcpy((void*)0x200000000400, "dummy_udc.0", 11);
syscall(__NR_write, /*fd=*/r[1], /*buf=*/0x200000000400ul, /*count=*/0xbul);
// openat arguments: [
// fd: fd_dir (resource)
// file: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 2f 62 75 73 2f 70 6c 61 74 66 6f 72 6d 2f 64 72 69 76 65 72 73 2f 64 75 6d 6d 79 5f 75 64 63 2f 62 69 6e 64 00} (length 0x2a)
// }
// flags: open_flags = 0x1 (4 bytes)
// mode: open_mode = 0x0 (2 bytes)
// ]
// returns fd
memcpy((void*)0x200000000500, "./sys/bus/platform/drivers/dummy_udc/bind\000", 42);
res = syscall(__NR_openat, /*fd=*/0xffffff9c, /*file=*/0x200000000500ul, /*flags=O_WRONLY*/1, /*mode=*/0);
if (res != -1)
r[2] = res;
// write arguments: [
// fd: fd (resource)
// buf: ptr[in, buffer] {
// buffer: {64 75 6d 6d 79 5f 75 64 63 2e 30} (length 0xb)
// }
// count: len = 0xb (8 bytes)
// ]
memcpy((void*)0x200000000400, "dummy_udc.0", 11);
syscall(__NR_write, /*fd=*/r[2], /*buf=*/0x200000000400ul, /*count=*/0xbul);
// mkdir arguments: [
// path: nil
// mode: open_mode = 0x1ff (8 bytes)
// ]
syscall(__NR_mkdir, /*path=*/0ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
return 0;
}
]
|
| 3/1 |
2026/07/29 07:32 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/3735123449433f9e6f9fb5b55ddd9994cbcf15f0]
|
| 4/1 |
2026/07/29 07:32 |
action |
kernel-builder |
13m
Results: map[KernelObj:/app/workdir/cache/build/e3a9d0b6f15d322c13f9336958b7f6eb9fb942d9]
|
| 5/1 |
2026/07/29 07:45 |
action |
crash-reproducer |
6m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:KASAN: slab-use-after-free Read in raw_process_ep0_io ReproducedCrashReport:=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
ReproducedFaultInjection:]
|
| 6/1 |
2026/07/29 07:52 |
action |
codesearch-prepare |
15m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/07/29 08:07 |
agent |
debugger |
5m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
// 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;
}
struct vusb_descriptor {
uint8_t req_type;
uint8_t desc_type;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_descriptors {
uint32_t len;
struct vusb_descriptor* generic;
struct vusb_descriptor* descs[0];
} __attribute__((packed));
struct vusb_response {
uint8_t type;
uint8_t req;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_responses {
uint32_t len;
struct vusb_response* generic;
struct vusb_response* resps[0];
} __attribute__((packed));
static bool lookup_control_response(const struct vusb_descriptors* descs, const struct vusb_responses* resps,
struct usb_ctrlrequest* ctrl, char** response_data, uint32_t* response_length)
{
int descs_num = 0;
int resps_num = 0;
if (descs)
descs_num = (descs->len - offsetof(struct vusb_descriptors, descs)) / sizeof(descs->descs[0]);
if (resps)
resps_num = (resps->len - offsetof(struct vusb_responses, resps)) / sizeof(resps->resps[0]);
uint8_t req = ctrl->bRequest;
uint8_t req_type = ctrl->bRequestType & USB_TYPE_MASK;
uint8_t desc_type = ctrl->wValue >> 8;
if (req == USB_REQ_GET_DESCRIPTOR) {
int i;
for (i = 0; i < descs_num; i++) {
struct vusb_descriptor* desc = descs->descs[i];
if (!desc)
continue;
if (desc->req_type == req_type && desc->desc_type == desc_type) {
*response_length = desc->len;
if (*response_length != 0)
*response_data = &desc->data[0];
else
*response_data = NULL;
return true;
}
}
if (descs && descs->generic) {
*response_data = &descs->generic->data[0];
*response_length = descs->generic->len;
return true;
}
} else {
int i;
for (i = 0; i < resps_num; i++) {
struct vusb_response* resp = resps->resps[i];
if (!resp)
continue;
if (resp->type == req_type && resp->req == req) {
*response_length = resp->len;
if (*response_length != 0)
*response_data = &resp->data[0];
else
*response_data = NULL;
return true;
}
}
if (resps && resps->generic) {
*response_data = &resps->generic->data[0];
*response_length = resps->generic->len;
return true;
}
}
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_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_interface(int fd, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
for (int i = 0; i < index->ifaces_num; i++) {
if (index->ifaces[i].bInterfaceNumber == bInterfaceNumber &&
index->ifaces[i].bAlternateSetting == bAlternateSetting)
return i;
}
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_control_io(volatile long a0, volatile long a1, volatile long a2)
{
int fd = a0;
const struct vusb_descriptors* descs = (const struct vusb_descriptors*)a1;
const struct vusb_responses* resps = (const struct vusb_responses*)a2;
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = USB_MAX_PACKET_SIZE;
int rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL) {
return -1;
}
char* response_data = NULL;
uint32_t response_length = 0;
if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) {
if (!lookup_control_response(descs, resps, &event.ctrl, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
return -1;
}
} else {
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD ||
event.ctrl.bRequest == USB_REQ_SET_INTERFACE) {
int iface_num = event.ctrl.wIndex;
int alt_set = event.ctrl.wValue;
int iface_index = lookup_interface(fd, iface_num, alt_set);
if (iface_index < 0) {
} else {
set_interface(fd, iface_index);
}
}
response_length = event.ctrl.wLength;
}
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;
if ((event.ctrl.bRequestType & USB_DIR_IN) && !event.ctrl.wLength) {
response_length = USB_MAX_PACKET_SIZE;
}
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) && event.ctrl.wLength) {
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 0;
}
uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*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=*/0x1000, /*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$cdc_ecm arguments: [
// speed: usb_device_speed = 0x0 (8 bytes)
// dev_len: len = 0x4d (8 bytes)
// dev: ptr[in, usb_device_descriptor_cdc_ecm] {
// usb_device_descriptor_cdc_ecm {
// inner: usb_device_descriptor_verbose_t[flags[usb_versions, int16], USB_CLASS_COMM, 0, 0, flags[usb_device_max_packet_sizes, int8], 0x525, 0xa4a1, 64, array[usb_config_descriptor_cdc_ecm, 1]] {
// bLength: const = 0x12 (1 bytes)
// bDescriptorType: const = 0x1 (1 bytes)
// bcdUSB: usb_versions = 0x0 (2 bytes)
// bDeviceClass: const = 0x2 (1 bytes)
// bDeviceSubClass: const = 0x0 (1 bytes)
// bDeviceProtocol: const = 0x0 (1 bytes)
// bMaxPacketSize0: usb_device_max_packet_sizes = 0x40 (1 bytes)
// idVendor: const = 0x525 (2 bytes)
// idProduct: const = 0xa4a1 (2 bytes)
// bcdDevice: const = 0x40 (2 bytes)
// iManufacturer: const = 0x0 (1 bytes)
// iProduct: const = 0x0 (1 bytes)
// iSerialNumber: const = 0xffffffffffff8001 (1 bytes)
// bNumConfigurations: len = 0x1 (1 bytes)
// configs: array[usb_config_descriptor_cdc_ecm] {
// usb_config_descriptor_cdc_ecm {
// inner: usb_config_descriptor_verbose_t[const[1, int8], len[interfaces, int8], int8, flags[usb_config_attributes, int8], int8, void, array[usb_interface_descriptor_cdc_ecm, 1]] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x2 (1 bytes)
// wTotalLength: len = 0x3b (2 bytes)
// bNumInterfaces: len = 0x1 (1 bytes)
// bConfigurationValue: const = 0x1 (1 bytes)
// iConfiguration: int8 = 0x0 (1 bytes)
// bmAttributes: usb_config_attributes = 0x0 (1 bytes)
// bMaxPower: int8 = 0x0 (1 bytes)
// extra: buffer: {} (length 0x0)
// interfaces: array[usb_interface_descriptor_cdc_ecm] {
// usb_interface_descriptor_cdc_ecm {
// inner: usb_interface_descriptor_verbose_t[const[0, int8], int8, int8[2:3], const[USB_CLASS_COMM, int8], const[USB_CDC_SUBCLASS_ETHERNET, int8], const[USB_CDC_PROTO_NONE, int8], int8, usb_cdc_header_ecm, usb_endpoint_descriptors_cdc_ecm] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x4 (1 bytes)
// bInterfaceNumber: const = 0x0 (1 bytes)
// bAlternateSetting: int8 = 0x0 (1 bytes)
// bNumEndpoints: int8 = 0x12 (1 bytes)
// bInterfaceClass: const = 0x2 (1 bytes)
// bInterfaceSubClass: const = 0x6 (1 bytes)
// bInterfaceProtocol: const = 0x0 (1 bytes)
// iInterface: int8 = 0x0 (1 bytes)
// extra: usb_cdc_header_ecm {
// union: usb_cdc_union_desc_t[0, 0] {
// bLength: len = 0x5 (1 bytes)
// bDescriptorType: const = 0x24 (1 bytes)
// bDescriptorSubType: const = 0x6 (1 bytes)
// bMasterInterface0: const = 0x0 (1 bytes)
// bSlaveInterface0: const = 0x0 (1 bytes)
// slave_interfaces: buffer: {} (length 0x0)
// }
// header: usb_cdc_header_desc {
// bLength: len = 0x5 (1 bytes)
// bDescriptorType: const = 0x24 (1 bytes)
// bDescriptorSubType: const = 0x0 (1 bytes)
// bcdCDC: int16 = 0x0 (2 bytes)
// }
// ether: usb_cdc_ether_desc {
// bLength: len = 0xd (1 bytes)
// bDescriptorType: const = 0x24 (1 bytes)
// bDescriptorSubType: const = 0xf (1 bytes)
// iMACAddress: const = 0x1 (1 bytes)
// bmEthernetStatistics: int32 = 0x0 (4 bytes)
// wMaxSegmentSize: int16 = 0x0 (2 bytes)
// wNumberMCFilters: int16 = 0x0 (2 bytes)
// bNumberPowerFilters: int8 = 0x0 (1 bytes)
// }
// other: array[usb_cdc_header_ecm_other] {
// }
// }
// endpoints: usb_endpoint_descriptors_cdc_ecm {
// notify: array[usb_endpoint_descriptor_cdc_ecm_notify] {
// }
// in: usb_endpoint_descriptor_cdc_ecm_in {
// inner: usb_endpoint_descriptor_verbose_t[const[USB_ENDPOINT_CDC_ECM_IN_ADDRESS, int8], const[USB_ENDPOINT_CDC_ECM_DATA_ATTRIBUTES, int8], flags[usb_endpoint_max_packet_sizes, int16], int8, int8, int8, void] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x5 (1 bytes)
// bEndpointAddress: const = 0x82 (1 bytes)
// bmAttributes: const = 0x2 (1 bytes)
// wMaxPacketSize: usb_endpoint_max_packet_sizes = 0x200 (2 bytes)
// bInterval: int8 = 0x0 (1 bytes)
// bRefresh: int8 = 0x0 (1 bytes)
// bSynchAddress: int8 = 0x0 (1 bytes)
// extra: buffer: {} (length 0x0)
// }
// }
// out: usb_endpoint_descriptor_cdc_ecm_out {
// inner: usb_endpoint_descriptor_verbose_t[const[USB_ENDPOINT_CDC_ECM_OUT_ADDRESS, int8], const[USB_ENDPOINT_CDC_ECM_DATA_ATTRIBUTES, int8], flags[usb_endpoint_max_packet_sizes, int16], int8, int8, int8, void] {
// bLength: const = 0x9 (1 bytes)
// bDescriptorType: const = 0x5 (1 bytes)
// bEndpointAddress: const = 0x3 (1 bytes)
// bmAttributes: const = 0x2 (1 bytes)
// wMaxPacketSize: usb_endpoint_max_packet_sizes = 0x200 (2 bytes)
// bInterval: int8 = 0x0 (1 bytes)
// bRefresh: int8 = 0x0 (1 bytes)
// bSynchAddress: int8 = 0x0 (1 bytes)
// extra: buffer: {} (length 0x0)
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb_cdc_ecm
*(uint8_t*)0x200000000000 = 0x12;
*(uint8_t*)0x200000000001 = 1;
*(uint16_t*)0x200000000002 = 0;
*(uint8_t*)0x200000000004 = 2;
*(uint8_t*)0x200000000005 = 0;
*(uint8_t*)0x200000000006 = 0;
*(uint8_t*)0x200000000007 = 0x40;
*(uint16_t*)0x200000000008 = 0x525;
*(uint16_t*)0x20000000000a = 0xa4a1;
*(uint16_t*)0x20000000000c = 0x40;
*(uint8_t*)0x20000000000e = 0;
*(uint8_t*)0x20000000000f = 0;
*(uint8_t*)0x200000000010 = 1;
*(uint8_t*)0x200000000011 = 1;
*(uint8_t*)0x200000000012 = 9;
*(uint8_t*)0x200000000013 = 2;
*(uint16_t*)0x200000000014 = 0x3b;
*(uint8_t*)0x200000000016 = 1;
*(uint8_t*)0x200000000017 = 1;
*(uint8_t*)0x200000000018 = 0;
*(uint8_t*)0x200000000019 = 0;
*(uint8_t*)0x20000000001a = 0;
*(uint8_t*)0x20000000001b = 9;
*(uint8_t*)0x20000000001c = 4;
*(uint8_t*)0x20000000001d = 0;
*(uint8_t*)0x20000000001e = 0;
*(uint8_t*)0x20000000001f = 0x12;
*(uint8_t*)0x200000000020 = 2;
*(uint8_t*)0x200000000021 = 6;
*(uint8_t*)0x200000000022 = 0;
*(uint8_t*)0x200000000023 = 0;
*(uint8_t*)0x200000000024 = 5;
*(uint8_t*)0x200000000025 = 0x24;
*(uint8_t*)0x200000000026 = 6;
*(uint8_t*)0x200000000027 = 0;
*(uint8_t*)0x200000000028 = 0;
*(uint8_t*)0x200000000029 = 5;
*(uint8_t*)0x20000000002a = 0x24;
*(uint8_t*)0x20000000002b = 0;
*(uint16_t*)0x20000000002c = 0;
*(uint8_t*)0x20000000002e = 0xd;
*(uint8_t*)0x20000000002f = 0x24;
*(uint8_t*)0x200000000030 = 0xf;
*(uint8_t*)0x200000000031 = 1;
*(uint32_t*)0x200000000032 = 0;
*(uint16_t*)0x200000000036 = 0;
*(uint16_t*)0x200000000038 = 0;
*(uint8_t*)0x20000000003a = 0;
*(uint8_t*)0x20000000003b = 9;
*(uint8_t*)0x20000000003c = 5;
*(uint8_t*)0x20000000003d = 0x82;
*(uint8_t*)0x20000000003e = 2;
*(uint16_t*)0x20000000003f = 0x200;
*(uint8_t*)0x200000000041 = 0;
*(uint8_t*)0x200000000042 = 0;
*(uint8_t*)0x200000000043 = 0;
*(uint8_t*)0x200000000044 = 9;
*(uint8_t*)0x200000000045 = 5;
*(uint8_t*)0x200000000046 = 3;
*(uint8_t*)0x200000000047 = 2;
*(uint16_t*)0x200000000048 = 0x200;
*(uint8_t*)0x20000000004a = 0;
*(uint8_t*)0x20000000004b = 0;
*(uint8_t*)0x20000000004c = 0;
res = -1;
res = syz_usb_connect(/*speed=*/0, /*dev_len=*/0x4d, /*dev=*/0x200000000000, /*conn_descs=*/0);
if (res != -1)
r[0] = res;
// syz_usb_control_io$cdc_ecm arguments: [
// fd: fd_usb_cdc_ecm (resource)
// descs: nil
// resps: nil
// ]
syz_usb_control_io(/*fd=*/r[0], /*descs=*/0, /*resps=*/0);
// mkdir arguments: [
// path: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 00} (length 0x6)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000100, "./sys\000", 6);
syscall(__NR_mkdir, /*path=*/0x200000000100ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// mount arguments: [
// src: nil
// dst: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 00} (length 0x6)
// }
// type: ptr[in, buffer] {
// buffer: {73 79 73 66 73 00} (length 0x6)
// }
// flags: mount_flags = 0x0 (8 bytes)
// data: nil
// ]
memcpy((void*)0x200000000100, "./sys\000", 6);
memcpy((void*)0x200000000200, "sysfs\000", 6);
syscall(__NR_mount, /*src=*/0ul, /*dst=*/0x200000000100ul, /*type=*/0x200000000200ul, /*flags=*/0ul, /*data=*/0ul);
// openat arguments: [
// fd: fd_dir (resource)
// file: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 2f 62 75 73 2f 70 6c 61 74 66 6f 72 6d 2f 64 72 69 76 65 72 73 2f 64 75 6d 6d 79 5f 75 64 63 2f 75 6e 62 69 6e 64 00} (length 0x2c)
// }
// flags: open_flags = 0x1 (4 bytes)
// mode: open_mode = 0x0 (2 bytes)
// ]
// returns fd
memcpy((void*)0x200000000300, "./sys/bus/platform/drivers/dummy_udc/unbind\000", 44);
res = syscall(__NR_openat, /*fd=*/0xffffff9c, /*file=*/0x200000000300ul, /*flags=O_WRONLY*/1, /*mode=*/0);
if (res != -1)
r[1] = res;
// write arguments: [
// fd: fd (resource)
// buf: ptr[in, buffer] {
// buffer: {64 75 6d 6d 79 5f 75 64 63 2e 30} (length 0xb)
// }
// count: len = 0xb (8 bytes)
// ]
memcpy((void*)0x200000000400, "dummy_udc.0", 11);
syscall(__NR_write, /*fd=*/r[1], /*buf=*/0x200000000400ul, /*count=*/0xbul);
// openat arguments: [
// fd: fd_dir (resource)
// file: ptr[in, buffer] {
// buffer: {2e 2f 73 79 73 2f 62 75 73 2f 70 6c 61 74 66 6f 72 6d 2f 64 72 69 76 65 72 73 2f 64 75 6d 6d 79 5f 75 64 63 2f 62 69 6e 64 00} (length 0x2a)
// }
// flags: open_flags = 0x1 (4 bytes)
// mode: open_mode = 0x0 (2 bytes)
// ]
// returns fd
memcpy((void*)0x200000000500, "./sys/bus/platform/drivers/dummy_udc/bind\000", 42);
res = syscall(__NR_openat, /*fd=*/0xffffff9c, /*file=*/0x200000000500ul, /*flags=O_WRONLY*/1, /*mode=*/0);
if (res != -1)
r[2] = res;
// write arguments: [
// fd: fd (resource)
// buf: ptr[in, buffer] {
// buffer: {64 75 6d 6d 79 5f 75 64 63 2e 30} (length 0xb)
// }
// count: len = 0xb (8 bytes)
// ]
memcpy((void*)0x200000000400, "dummy_udc.0", 11);
syscall(__NR_write, /*fd=*/r[2], /*buf=*/0x200000000400ul, /*count=*/0xbul);
// mkdir arguments: [
// path: nil
// mode: open_mode = 0x1ff (8 bytes)
// ]
syscall(__NR_mkdir, /*path=*/0ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
return 0;
}
Reply: The root cause of this crash is a use-after-free (UAF) vulnerability in the `raw_gadget` driver. It occurs because the driver fails to properly clean up its internal state and synchronize concurrent operations when the underlying USB Device Controller (UDC) is unbound.
### Detailed Sequence of Events
1. **Gadget Initialization and Binding:**
The reproducer opens `/dev/raw-gadget` and initializes it. When the gadget binds to the `dummy_udc` controller, the UDC core invokes the `gadget_bind()` callback. This function saves the `struct usb_gadget` pointer into `dev->gadget` and eventually sets `dev->state = STATE_DEV_RUNNING`.
2. **UDC Unbinding:**
The reproducer then writes to `/sys/bus/platform/drivers/dummy_udc/unbind` via sysfs. This forces the UDC core to unbind the gadget driver, which invokes the `gadget_unbind()` callback in `raw_gadget.c`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
Notice that `gadget_unbind()` **fails to clear `dev->gadget`** and **fails to update `dev->state`**. It only clears the driver data. After this function returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device` (including the device name string allocated via `kvasprintf`).
3. **Dangling Pointer Dereference:**
The reproducer then issues a `USB_RAW_IOCTL_EP0_READ` (or `WRITE`) ioctl, which lands in `raw_process_ep0_io()`:
```c
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, void *data, bool in)
{
// ...
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { // Passes, state was never updated
// ...
}
if (!dev->gadget) { // Passes, dev->gadget is a dangling pointer
// ...
}
// ...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);
// ...
```
Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
4. **The Crash:**
Since the UDC is disconnected/freed, `usb_ep_queue()` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, ...)`. The `dev_err()` macro attempts to print the device name (`dev->gadget->dev.kobj.name`), which points to the freed 16-byte string, resulting in the KASAN `slab-use-after-free` crash observed in the trace.
### How to Fix
To fix this bug, `gadget_unbind()` must clear the `dev->gadget` pointer and update `dev->state` (e.g., to `STATE_DEV_FAILED`) while holding `dev->lock`.
However, simply clearing the pointer is not enough due to race conditions. If an ioctl thread has already passed the `!dev->gadget` check and released `dev->lock`, but hasn't yet called `usb_ep_queue()` (which is called lockless), `gadget_unbind()` could return and free the gadget right under the ioctl's feet.
To prevent this race, `raw_gadget` needs a synchronization mechanism to track ongoing UDC calls and wait for them to finish before returning from `gadget_unbind()`. A common pattern for this (as seen in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`) is to introduce a `udc_usage` counter:
- Increment `udc_usage` under `dev->lock` before releasing the lock to call `usb_ep_queue()`.
- Decrement `udc_usage` under `dev->lock` after `usb_ep_queue()` returns.
- In `gadget_unbind()`, wait for `udc_usage` to drop to `0` before returning.
|
| 8/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 21498
output: 26
thoughts: 333
Thoughts: **KASAN Slab-Use-After-Free Investigation**
Okay, so I've got a KASAN slab-use-after-free situation, specifically within `string_nocheck`, and it looks like it's being triggered by a call from the `string` function in `lib/vsprintf.c`. The crash itself is occurring during an `snprintf` operation inside `set_dev_info` in `drivers/base/core.c` at line 4984.
The call trace provides some important clues. It starts at `raw_process_ep0_io`, and works its way up through `raw_ioctl_ep0_read` (which is inlined), and then `raw_ioctl` in the `drivers/usb/gadget/legacy/raw_gadget.c` file. Interesting.
Now, a crucial piece of this puzzle clicks. I remember that `set_dev_info` is actually called indirectly through a chain: `dev_vprintk_emit` -> `dev_printk_emit` -> `_dev_err`. And I know that `_dev_err` is in the call trace, which now leads me to suspect that the problem is rooted in an error printing scenario and that the kernel is printing an error.
The call trace confirms that:
```
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
```
I should start by examining `raw_process_ep0_io` in `drivers/usb/gadget/legacy/raw_gadget.c` at line 730 to understand how this error path is entered from `raw_process_ep0_io` and determine where the sprintf occurs. Time to dive into the code.
|
| 9/2 |
2026/07/29 08:07 |
tool |
codesearch-definition-source |
0m
Args: map[Name:raw_process_ep0_io]
Results: map[SourceCode: 683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 10/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 23500
output: 21
thoughts: 531
Thoughts: **UAF Investigation: USB Gadget Driver**
Okay, so I'm looking at a crash specifically in `dev_err` on line 730, triggered by `usb_ep_queue` returning an error. It looks like the UAF (Use-After-Free) is occurring on `dev->gadget->dev`. My initial thought is to trace back the object and when it's freed.
Hmm, so `dev->gadget` is accessed before this happens. The freed object itself is a small one, only 16 bytes. Based on past experience, this immediately suggests a dynamically allocated string, likely created using `kvasprintf`. I know `kvasprintf` is used in `kobject_set_name_vargs` which is called by `dev_set_name` during the `usb_add_gadget` process.
Ah, `dev->gadget->dev` is a `struct device`, and the crash is happening within `string_nocheck`, indicating a string printing operation, and `dev_err` prints the device name! The device name, as I suspected, is allocated by `kvasprintf` and stored in `dev->kobj.name`. This means if the device itself or the name string gets freed, we get a UAF during that print.
Alright, the freed object, as stated, is 16 bytes, and `kvasprintf` is the likely culprit for the string allocation. I know `kobject_cleanup` calls `kobject_release`, then `kobject_put`, then `device_release_driver_internal`, and finally `unbind_store` which likely frees this string.
Now, the reproducer reveals the unbind and bind sequence of the dummy_udc driver via `openat` and `write` syscalls. It's writing "dummy_udc.0" to unbind, and then "dummy_udc.0" to bind. This makes perfect sense; the unbind calls `usb_del_gadget`, which in turn should free the gadget.
So... `raw_gadget` still has a dangling pointer to the freed `dev->gadget` after the unbind/bind. I need to investigate the `raw_gadget` binding and unbinding code, as that seems to be the critical issue. This is definitely a race condition between `raw_gadget` and the USB gadget driver unbinding.
|
| 11/2 |
2026/07/29 08:07 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 707: /* This gets rid of all the nasties in af_inet. -DaveM */
708: static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr,
709: int addr_len)
710: {
711: struct inet_sock *inet = inet_sk(sk);
712: struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
713: struct net *net = sock_net(sk);
714: u32 tb_id = RT_TABLE_LOCAL;
715: int ret = -EINVAL;
716: int chk_addr_ret;
717:
718: lock_sock(sk);
719: if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_in))
720: goto out;
721:
722: if (sk->sk_bound_dev_if)
723: tb_id = l3mdev_fib_table_by_index(net,
724: sk->sk_bound_dev_if) ? : tb_id;
725:
726: chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id);
727:
728: ret = -EADDRNOTAVAIL;
729: if (!inet_addr_valid_or_nonlocal(net, inet, addr->sin_addr.s_addr,
730: chk_addr_ret))
731: goto out;
732:
733: inet->inet_saddr = addr->sin_addr.s_addr;
734: WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr);
735: if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
736: inet->inet_saddr = 0; /* Use device */
737: sk_dst_reset(sk);
738: ret = 0;
739: out:
740: release_sock(sk);
741: return ret;
742: }
SourceFile:net/ipv4/raw.c]
|
| 12/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 25241
output: 34
thoughts: 31
Thoughts: Okay, here's my interpretation of that thought process, as if I were having it:
**Digging into a USB Gadget Issue**
Alright, something isn't quite right with this `raw_bind` function. That name feels⦠off. Let me just quickly confirm, yep, that feels like the wrong place to be looking.
Okay, instead of getting hung up on the initial search, it's time to refine this. If `raw_bind` is the problem, chances are it's part of a broader gadget mechanism within the USB subsystem, and specifically the legacy portions, since that's where I found it. Let me just search within the `drivers/usb/gadget/legacy/` directory to see if I can find it, specifically, let's look for files that involve binding or configuration.
Let's search for "gadget bind" within `drivers/usb/gadget/legacy/raw_gadget.c` to narrow down our focus and see what that specific gadget is up to. Let's see what we can find.
|
| 13/2 |
2026/07/29 08:07 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Entities:[map[Kind:function Name:dev_free] map[Kind:function Name:dev_new] map[Kind:function Name:fill_ep_caps] map[Kind:function Name:fill_ep_limits] map[Kind:function Name:gadget_bind] map[Kind:function Name:gadget_disconnect] map[Kind:function Name:gadget_ep0_complete] map[Kind:function Name:gadget_ep_complete] map[Kind:function Name:gadget_reset] map[Kind:function Name:gadget_resume] map[Kind:function Name:gadget_setup] map[Kind:function Name:gadget_suspend] map[Kind:function Name:gadget_unbind] map[Kind:function Name:get_ep_addr] map[Kind:function Name:raw_alloc_io_data] map[Kind:function Name:raw_event_queue_add] map[Kind:function Name:raw_event_queue_destroy] map[Kind:function Name:raw_event_queue_fetch] map[Kind:function Name:raw_event_queue_init] map[Kind:function Name:raw_ioctl] map[Kind:function Name:raw_ioctl_configure] map[Kind:function Name:raw_ioctl_ep0_read] map[Kind:function Name:raw_ioctl_ep0_stall] map[Kind:function Name:raw_ioctl_ep0_write] map[Kind:function Name:raw_ioctl_ep_disable] map[Kind:function Name:raw_ioctl_ep_enable] map[Kind:function Name:raw_ioctl_ep_read] map[Kind:function Name:raw_ioctl_ep_set_clear_halt_wedge] map[Kind:function Name:raw_ioctl_ep_write] map[Kind:function Name:raw_ioctl_eps_info] map[Kind:function Name:raw_ioctl_event_fetch] map[Kind:function Name:raw_ioctl_init] map[Kind:function Name:raw_ioctl_run] map[Kind:function Name:raw_ioctl_vbus_draw] map[Kind:function Name:raw_misc_device_exit] map[Kind:function Name:raw_misc_device_init] map[Kind:function Name:raw_open] map[Kind:function Name:raw_process_ep0_io] map[Kind:function Name:raw_process_ep_io] map[Kind:function Name:raw_queue_event] map[Kind:function Name:raw_release] map[Kind:struct Name:raw_dev] map[Kind:struct Name:raw_ep] map[Kind:struct Name:raw_event_queue] map[Kind:global_variable Name:__UNIQUE_ID_addressable_raw_misc_device_init_903] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_779] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_780] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_781] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_782] map[Kind:global_variable Name:__exitcall_raw_misc_device_exit] map[Kind:global_variable Name:driver_id_numbers] map[Kind:global_variable Name:raw_fops] map[Kind:global_variable Name:raw_misc_device] map[Kind:enum Name:dev_state] map[Kind:enum Name:ep_state]]]
|
| 14/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 26519
output: 22
thoughts: 17
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own immediate considerations:
**Quick Scan: `gadget_unbind` and `gadget_bind`**
Alright, time to get my bearings. The task at hand likely hinges on the interaction, or lack thereof, between `gadget_unbind` and `gadget_bind`. I need to immediately assess their roles within the broader context β what's the underlying architecture? Are we dealing with hardware, software, or a mixed bag? Understanding the binding/unbinding semantics is crucial. Are these simple register operations, or are we navigating complex resource allocation and deallocation involving interrupts, DMA, or even security contexts?
First, I'll mentally run through the standard cases. Are there any expected error conditions? Potential race conditions between the two functions are my initial point of interest. What are the calling conventions? Stack layout? Any assumptions about the system's state before and after these calls? Are there any common coding patterns or paradigms where these functions are typically employed β something to look out for. And what about atomicity? Do we have to worry about inter-thread/process access to whatever data or resource these functions are acting on?
Beyond that, I'll need to remember the specific architecture. What's the target platform? CPU, OS? Are we looking at a classic kernel module scenario, or something more exotic like a userspace driver? The devil is always in the details, so I need to keep the specifics in mind. Any clues about the expected timing or frequency of these calls? If they're called very frequently, performance is likely a key consideration; if they're rare, then correctness may be paramount. Let's see... I'll start by making a list of the variables involved, followed by their relationships to each other.
|
| 15/2 |
2026/07/29 08:07 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 16/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 27112
output: 22
thoughts: 7
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Navigating the `gadget_bind` Landscape**
Alright, let's break this down. When I see `gadget_bind`, my mind immediately goes to the potential for dynamic data binding and UI updates. I'm likely thinking about how it interfaces with the underlying data model, whatever that might be. Is it a reactive system? Does it use observables, or something more traditional like setters and getters with change detection? The specifics of the implementation are key here. I'm wondering about the scope of the binding. Is it one-way, two-way, or something more complex with custom event handling?
I'm evaluating the performance implications too, especially if this is in a performance-critical area. How does the binding mechanism scale with large datasets or frequent updates? Are there any potential memory leaks or performance bottlenecks I should be wary of? I'll probably be looking for examples of where this is used, the type of data being bound, and any edge cases that might have been addressed.
Then I am thinking about how `gadget_bind` handles data transformations. Does it support custom formatters, converters, or validation rules? Being able to seamlessly transform data on the fly is a core function. I'm thinking about whether there are any built-in functions for common scenarios, or if I'd need to write my own. I'm always on the lookout for a clean, declarative approach to these transformations, so I'm not writing sprawling procedural code.
I'm also considering the architecture. Does this integrate cleanly with the rest of the application? I'm asking about its dependencies, the extent of its footprint, and if there are any conflicts with existing libraries or frameworks. Is this designed to be isolated, or is it expected to have a close connection to the view layer? Compatibility, and its future evolution, is critical.
|
| 17/2 |
2026/07/29 08:07 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 281: static int gadget_bind(struct usb_gadget *gadget,
282: struct usb_gadget_driver *driver)
283: {
284: int ret = 0, i = 0;
285: struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
286: struct usb_request *req;
287: struct usb_ep *ep;
288: unsigned long flags;
289:
290: if (strcmp(gadget->name, dev->udc_name) != 0)
291: return -ENODEV;
292:
293: set_gadget_data(gadget, dev);
294: req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
295: if (!req) {
296: dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
297: set_gadget_data(gadget, NULL);
298: return -ENOMEM;
299: }
300:
301: spin_lock_irqsave(&dev->lock, flags);
302: dev->req = req;
303: dev->req->context = dev;
304: dev->req->complete = gadget_ep0_complete;
305: dev->gadget = gadget;
306: gadget_for_each_ep(ep, dev->gadget) {
307: dev->eps[i].ep = ep;
308: dev->eps[i].addr = get_ep_addr(ep->name);
309: dev->eps[i].state = STATE_EP_DISABLED;
310: i++;
311: }
312: dev->eps_num = i;
313: spin_unlock_irqrestore(&dev->lock, flags);
314:
315: dev_dbg(&gadget->dev, "gadget connected\n");
316: ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
317: if (ret < 0) {
318: dev_err(&gadget->dev, "failed to queue connect event\n");
319: set_gadget_data(gadget, NULL);
320: return ret;
321: }
322:
323: /* Matches kref_put() in gadget_unbind(). */
324: kref_get(&dev->count);
325: return ret;
326: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 18/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 28330
output: 41
thoughts: 190
Thoughts: **Debugging a USB Gadget Driver**
Okay, so I'm diving into this USB gadget driver, and I've hit a snag. My initial thought was that `gadget_unbind` wasn't properly cleaning up the `dev->gadget` pointer. I see it's missing from the source code. But, on closer inspection, it looks like `gadget_unbind` *does* unset the `gadget`, it sets `dev->count` to free the `dev` struct which `gadget_unbind` is referencing.
That's a bit problematic. If `dev->gadget` isn't NULL, then `raw_process_ep0_io` could potentially access `dev->gadget` after it's been freed! This could lead to a really nasty use-after-free situation, which is exactly what I'm trying to avoid.
Time to meticulously trace where else `dev->gadget` might be set or cleared to make sure this isn't happening. Let's see if this `dev->gadget` pointer is even set elsewhere.
|
| 19/2 |
2026/07/29 08:07 |
tool |
grepper |
0m
Error:
Args: map[Expression:dev->gadget = NULL PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 20/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 28809
output: 40
thoughts: 30
Thoughts: Okay, here's my attempt at summarizing the thought process, as if it were my own, assuming I'm an expert in embedded systems/USB gadget drivers:
**Hunting Down the USB Gadget Initialization**
Alright, I need to understand how the raw gadget driver initializes. The quickest way to get a handle on that is usually to trace the assignment of the `gadget` structure within the driver. So, I'm thinking, "Let's crack open `drivers/usb/gadget/legacy/raw_gadget.c` and search specifically for the line where the `dev->gadget =` assignment happens." That's the key. That line is the critical link I'm after, the crucial pointer to figuring out the setup sequence. Once I find that assignment, I can trace backwards and forward from there to understand the driver's core logic. It's a classic approach, but often the most efficient in this kind of scenario.
|
| 21/2 |
2026/07/29 08:07 |
tool |
grepper |
0m
Args: map[Expression:dev->gadget = PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=281=static int gadget_bind(struct usb_gadget *gadget,
--
drivers/usb/gadget/legacy/raw_gadget.c-304- dev->req->complete = gadget_ep0_complete;
drivers/usb/gadget/legacy/raw_gadget.c:305: dev->gadget = gadget;
drivers/usb/gadget/legacy/raw_gadget.c-306- gadget_for_each_ep(ep, dev->gadget) {
]
|
| 22/2 |
2026/07/29 08:07 |
llm |
debugger |
0m
Model:
Tokens:
input: 29233
output: 22
thoughts: 34
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing:
**Analyzing Potential Null Pointer Dereference**
Right, so I'm looking at this code, and the crucial point is that `dev->gadget` is never explicitly set to `NULL`. This immediately flags a potential issue. I know this code, the context, I understand the likely scenario. We have to be careful about null pointer dereferences.
Now, hold on a second... Could `dev->state` somehow be modified within `gadget_unbind`? That function is key, the unbinding is happening right now, at this point, the state transitions are the key here. If the state changes within that function, that could indirectly impact how `dev->gadget` is handled and potentially create a situation where we *think* it might be `NULL` in a subsequent call even if it technically isn't at this very moment. I need to dig deeper into the control flow of `gadget_unbind` to understand its exact effect on `dev->state` and the implications for `dev->gadget`.
|
| 23/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Args: map[Name:gadget_disconnect]
Results: map[SourceCode: 382: static void gadget_disconnect(struct usb_gadget *gadget)
383: {
384: struct raw_dev *dev = get_gadget_data(gadget);
385: int ret;
386:
387: dev_dbg(&gadget->dev, "gadget disconnected\n");
388: ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL);
389: if (ret < 0)
390: dev_err(&gadget->dev, "failed to queue disconnect event\n");
391: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 24/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 29731
output: 39
thoughts: 216
Thoughts: **Analyzing USB Device State Transitions**
Okay, so I'm focusing on this `gadget_unbind` function, and something's not quite clicking. It seems like the unbind process *doesn't* directly modify the `dev->state` variable. Interesting. This means that even after `gadget_unbind`, the device's state might remain `STATE_DEV_RUNNING`. Let's dig deeper into the logic to confirm this.
I need to see what happens when the endpoint zero I/O is being processed, so I'll check `raw_process_ep0_io`. Here it is. The code does a quick state and gadget check right at the beginning. If `dev->state` is not `STATE_DEV_RUNNING` or `dev->gadget` is NULL, it exits. So if the device *is* running (`STATE_DEV_RUNNING`) *and* a gadget is still bound (even temporarily), it continues. This implies `gadget_unbind` does not change `dev->state`.
Now, the important question arises: when *does* `dev->state` transition to another state? I need to trace back where the `dev->state` variable is modified. This is key to understanding the complete picture of how the device transitions between different operational states and how it relates to the unbinding process. Let's trace all assignments to that variable.
|
| 25/2 |
2026/07/29 08:08 |
tool |
grepper |
0m
Args: map[Expression:dev->state = PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=234=static int raw_queue_event(struct raw_dev *dev,
--
drivers/usb/gadget/legacy/raw_gadget.c-242- spin_lock_irqsave(&dev->lock, flags);
drivers/usb/gadget/legacy/raw_gadget.c:243: dev->state = STATE_DEV_FAILED;
drivers/usb/gadget/legacy/raw_gadget.c-244- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=427=static int raw_open(struct inode *inode, struct file *fd)
--
drivers/usb/gadget/legacy/raw_gadget.c-438- fd->private_data = dev;
drivers/usb/gadget/legacy/raw_gadget.c:439: dev->state = STATE_DEV_OPENED;
drivers/usb/gadget/legacy/raw_gadget.c-440- dev->dev = raw_misc_device.this_device;
--
drivers/usb/gadget/legacy/raw_gadget.c=444=static int raw_release(struct inode *inode, struct file *fd)
--
drivers/usb/gadget/legacy/raw_gadget.c-451- spin_lock_irqsave(&dev->lock, flags);
drivers/usb/gadget/legacy/raw_gadget.c:452: dev->state = STATE_DEV_CLOSED;
drivers/usb/gadget/legacy/raw_gadget.c-453- if (!dev->gadget) {
--
drivers/usb/gadget/legacy/raw_gadget.c=480=static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-561-
drivers/usb/gadget/legacy/raw_gadget.c:562: dev->state = STATE_DEV_INITIALIZED;
drivers/usb/gadget/legacy/raw_gadget.c-563- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=579=static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-592- }
drivers/usb/gadget/legacy/raw_gadget.c:593: dev->state = STATE_DEV_REGISTERING;
drivers/usb/gadget/legacy/raw_gadget.c-594- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c-601- "fail, usb_gadget_register_driver returned %d\n", ret);
drivers/usb/gadget/legacy/raw_gadget.c:602: dev->state = STATE_DEV_FAILED;
drivers/usb/gadget/legacy/raw_gadget.c-603- goto out_unlock;
--
drivers/usb/gadget/legacy/raw_gadget.c-605- dev->gadget_registered = true;
drivers/usb/gadget/legacy/raw_gadget.c:606: dev->state = STATE_DEV_RUNNING;
drivers/usb/gadget/legacy/raw_gadget.c-607- /* Matches kref_put() in raw_release(). */
--
drivers/usb/gadget/legacy/raw_gadget.c=615=static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-645- spin_lock_irqsave(&dev->lock, flags);
drivers/usb/gadget/legacy/raw_gadget.c:646: dev->state = STATE_DEV_FAILED;
drivers/usb/gadget/legacy/raw_gadget.c-647- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=683=static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
--
drivers/usb/gadget/legacy/raw_gadget.c-712- ret = -ENODEV;
drivers/usb/gadget/legacy/raw_gadget.c:713: dev->state = STATE_DEV_FAILED;
drivers/usb/gadget/legacy/raw_gadget.c-714- goto out_unlock;
--
drivers/usb/gadget/legacy/raw_gadget.c-717- ret = -ENODEV;
drivers/usb/gadget/legacy/raw_gadget.c:718: dev->state = STATE_DEV_FAILED;
drivers/usb/gadget/legacy/raw_gadget.c-719- goto out_unlock;
]
|
| 26/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 31572
output: 22
thoughts: 536
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, let's break this down. It seems we have a use-after-free situation on our hands. My thought process is circling around how the `dev->state` and `dev->gadget` fields are handled within the `raw_dev` structure, specifically in the context of USB gadget operations.
First, I see that the `dev->state` is carefully managed. It gets set to `STATE_DEV_RUNNING` when the device starts (`raw_ioctl_run`), `STATE_DEV_CLOSED` on release (`raw_release`), and `STATE_DEV_FAILED` on errors. These are all expected behaviors. But now, here's where it gets interesting: the crucial thing I am focusing on is that it's NOT changed in `gadget_unbind`! This is the key.
Then, I'm reminding myself, `gadget_unbind` gets called when a gadget driver is unregistered, or when the UDC is unbound. In the reproducer, the user triggers the unbinding by writing to `/sys/bus/platform/drivers/dummy_udc/unbind`. So, we're unbinding the UDC which unbinds the gadget driver, which invokes `gadget_unbind`.
Looking at the code for `gadget_unbind` reveals the problem. It cleans up, releasing the `dev` structure's reference count, using `kref_put`. But crucially, *it doesn't clear `dev->gadget`*.
Now, the user tries to call `USB_RAW_IOCTL_EP0_READ` or `USB_RAW_IOCTL_EP0_WRITE`. `raw_process_ep0_io` gets called, and it checks for `dev->state` and `dev->gadget`. Now comes the fun part, or rather the bad part. The `dev->state` is still `STATE_DEV_RUNNING` from before the unbind, and `dev->gadget` is not null (as it wasn't cleared!). This is a race condition I have seen before.
So we proceed, and then we have:
```c
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
```
Here's where it's obvious: `dev->gadget` now points to a freed memory location β the gadget object that was unbound during the UDC unbind (the `dummy_udc`). Therefore, `dev->gadget->dev` is a use-after-free scenario. This is definitely a critical vulnerability.
Now, I am going to double-check that this is indeed the case. If we set `dev->gadget = NULL` in `gadget_unbind`, we must also hold `dev->lock` to prevent any other concurrent accesses.
|
| 27/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 28/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 32951
output: 23
thoughts: 501
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability**
Okay, so I'm digging into this code, and it looks like `gadget_unbind` is called from the UDC core. My initial thought was, "Hey, if we set `dev->gadget = NULL` within `gadget_unbind`, we should be able to prevent that pesky UAF." But I need to be more careful. Is `dev->gadget` used anywhere else without proper locking?
Let's look at `raw_process_ep0_io`. Ah, there it is: I see `spin_lock_irqsave` and the usual checks for device state and `!dev->gadget`. Excellent. However, I notice that the code unlocks the spinlock *before* calling `usb_ep_queue`, and `dev->gadget` is accessed *inside* `usb_ep_queue`. Crap.
If `gadget_unbind` sets `dev->gadget = NULL` under the `dev->lock`, it *won't* prevent the UAF if `gadget_unbind` runs *after* `spin_unlock_irqrestore` but *before* `usb_ep_queue`! That's a classic race condition.
Wait, `usb_ep_queue` is called without `dev->lock`. Is this even safe? Well, it is, but the gadget could be unbound concurrently. Hang on a second, when does `gadget_unbind` actually get called? It's called when the gadget driver is unregistered. The `raw_gadget` code doesn't unregister the gadget driver until `raw_release`! But wait, from the reproducer, it unbinds the UDC via sysfs! `/sys/bus/platform/drivers/dummy_udc/unbind`.
Yes, that's it! Unbinding via sysfs causes the UDC to unbind the gadget driver. The UDC calls `driver->unbind(gadget)`, which is indeed `gadget_unbind`. *Then* it frees the gadget. This means if `raw_process_ep0_io` is running concurrently, it could access the freed gadget, resulting in the UAF.
Okay, let me check how other gadget drivers handle the UDC unbind. I'll look at the `gadgetfs` driver. Let's see what the legacy inode driver does.
|
| 29/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Args: map[Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 30/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 34432
output: 22
thoughts: 55
Thoughts: **Analyzing Gadgetfs and Raw Gadget Initialization**
Okay, so gadgetfs is setting the device state to `STATE_DEV_UNBOUND` and then it's patiently waiting for `dev->udc_usage` to become greater than zero. That's the expected behavior, I can see what they are trying to achieve. But this makes me think, what about the `raw_gadget` situation? I need to check how it deals with device binding. It's time to go back and take a fresh look at `raw_gadget.c`. I need to compare their approaches and ensure I'm not overlooking a critical difference in how they handle UDC usage and state transitions. There might be some subtle but important detail that I can leverage in my own work.
|
| 31/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 32/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 34839
output: 22
thoughts: 67
Thoughts: Okay, let's break this down.
**Analyzing Potential Concurrency Issues and State Transitions in USB Gadget Driver Code**
Right, so the first thing that jumps out at me is that `raw_gadget` is clearly not implementing any kind of locking mechanism around `dev->gadget`. That's a red flag. If multiple threads or processes can access `dev->gadget` concurrently without synchronization, we're asking for trouble: data races, corrupted structures, the whole shebang. Gotta make sure we address that.
Then, there's a subtle concern about state management during unbinding. I'm noticing that the `dev->state` variable isn't being updated inside `gadget_unbind`. Hmm. That seems counterintuitive and potentially problematic. It might leave the system in an inconsistent state, depending on how `dev->state` is used elsewhere. I need to understand the full lifecycle and state transitions to be sure.
Hold on... Am I reading this right? It looks like `gadget_disconnect` gets called *before* `gadget_unbind`? That sequencing feels a little backwards. `disconnect` would seemingly be concerned with lower level transport and binding with higher level drivers. If that's the case, it might lead to race conditions. I really need to understand the implications of this order. I'm going to have to dive into the `gadget_disconnect` function to see exactly what's happening there and how it interacts with the gadget's unbinding process, now. It's time to dig deeper and see what the disconnect function is doing.
|
| 33/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Args: map[Name:gadget_disconnect]
Results: map[SourceCode: 382: static void gadget_disconnect(struct usb_gadget *gadget)
383: {
384: struct raw_dev *dev = get_gadget_data(gadget);
385: int ret;
386:
387: dev_dbg(&gadget->dev, "gadget disconnected\n");
388: ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL);
389: if (ret < 0)
390: dev_err(&gadget->dev, "failed to queue disconnect event\n");
391: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 34/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 35473
output: 36
thoughts: 497
Thoughts: **Analyzing a Potential Race Condition in USB Gadget Unbinding**
Okay, so `gadget_disconnect` seems pretty straightforward; it just queues an event. It doesn't appear to be touching `dev->state` directly. Now, a thought crosses my mind: would setting `dev->state` to `STATE_DEV_FAILED` within `gadget_unbind` actually solve the issue? Let's take a closer look at what `raw_process_ep0_io` does. I need to understand its behavior in this scenario.
Specifically, that snippet from `raw_process_ep0_io`: it checks `dev->state` under a lock, and bails out if it's not `STATE_DEV_RUNNING`. If `gadget_unbind` sets `dev->state` to `STATE_DEV_FAILED`, it seems like `raw_process_ep0_io` would correctly detect the failure if it *acquires* the lock *after* `gadget_unbind` has run.
But here's a crucial question: What happens if `raw_process_ep0_io` acquires the lock *before* `gadget_unbind` gets a chance to run?
Now, looking at the code, it acquires the lock, checks `dev->state`, and then sets `dev->ep0_urb_queued = true` *inside* the locked section. Then, it *releases* the lock and *then* calls `usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL)`. If `gadget_unbind` runs between the `spin_unlock_irqrestore` and `usb_ep_queue`, itβs going to free `dev->gadget`. Thus, `usb_ep_queue` would be accessing freed memory! Oh, wait. `usb_ep_queue` is actually called *without* the lock, which is the crux of the issue.
I'm starting to remember how `gadgetfs` handles this type of problem, and it has some similar complexities. They use `dev->udc_usage`. Let's see:
```c
spin_lock_irq (&dev->lock);
dev->state = STATE_DEV_UNBOUND;
while (dev->udc_usage > 0) {
spin_unlock_irq(&dev->lock);
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
spin_unlock_irq (&dev->lock);
```
Okay, so it sets the state to `STATE_DEV_UNBOUND` and then waits for the `udc_usage` counter to drop to zero. That's the key. Now, the big question is... where is `udc_usage` incremented? I need to find all the places where this counter is touched to fully grasp how this is working and if there is similar logic in our own module. This is the crucial point for race avoidance.
|
| 35/2 |
2026/07/29 08:08 |
tool |
grepper |
0m
Args: map[Expression:udc_usage PathPrefix:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:drivers/usb/gadget/legacy/inode.c=122=struct dev_data {
--
drivers/usb/gadget/legacy/inode.c-124- refcount_t count;
drivers/usb/gadget/legacy/inode.c:125: int udc_usage;
drivers/usb/gadget/legacy/inode.c-126- enum ep0_state state; /* P: lock */
--
drivers/usb/gadget/legacy/inode.c=913=ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-945- if ((retval = setup_req (ep, req, 0)) == 0) {
drivers/usb/gadget/legacy/inode.c:946: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-947- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-949- spin_lock_irq (&dev->lock);
drivers/usb/gadget/legacy/inode.c:950: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-951- }
--
drivers/usb/gadget/legacy/inode.c-991- len = min (len, (size_t)dev->req->actual);
drivers/usb/gadget/legacy/inode.c:992: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-993- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-998- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:999: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1000- clean_req (dev->gadget->ep0, dev->req);
--
drivers/usb/gadget/legacy/inode.c=1125=ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-1142- dev->state = STATE_DEV_CONNECTED;
drivers/usb/gadget/legacy/inode.c:1143: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1144- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1154- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1155: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1156- if (retval < 0) {
--
drivers/usb/gadget/legacy/inode.c=1251=static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
--
drivers/usb/gadget/legacy/inode.c-1261- } else if (gadget->ops->ioctl) {
drivers/usb/gadget/legacy/inode.c:1262: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1263- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1267- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1268: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1269- }
--
drivers/usb/gadget/legacy/inode.c=1333=gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
--
drivers/usb/gadget/legacy/inode.c-1497-
drivers/usb/gadget/legacy/inode.c:1498: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1499- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1502- spin_lock (&dev->lock);
drivers/usb/gadget/legacy/inode.c:1503: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1504- if (value < 0) {
--
drivers/usb/gadget/legacy/inode.c-1535-
drivers/usb/gadget/legacy/inode.c:1536: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1537- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1539- spin_lock(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1540: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1541- spin_unlock(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c=1640=gadgetfs_unbind (struct usb_gadget *gadget)
--
drivers/usb/gadget/legacy/inode.c-1647- dev->state = STATE_DEV_UNBOUND;
drivers/usb/gadget/legacy/inode.c:1648: while (dev->udc_usage > 0) {
drivers/usb/gadget/legacy/inode.c-1649- spin_unlock_irq(&dev->lock);
]
|
| 36/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 38160
output: 22
thoughts: 164
Thoughts: **Analyzing the USB Gadget Framework**
Okay, so I'm diving into this `gadgetfs` code and trying to grasp how it handles UDC interactions. It looks like `udc_usage` is the primary mechanism to determine if a UDC function, such as `usb_ep_queue`, is currently being called. That makes sense, a simple flag to track usage.
But then, I notice `raw_gadget` has its own internal state tracking via `dev->ep0_urb_queued`, `dev->ep0_in_pending`, and so on. That's a little redundant but maybe it's for performance. Hmmm, the calls to `usb_ep_queue` happen *after* the lock is released. That's a potential area for race conditions, it could be a simple mistake, but let me check that again.
Now, if `gadget_unbind` gets called, it seems straightforward initially β it just clears `dev->gadget`. No, wait a minute, it actually sets `dev->gadget = NULL`. It doesn't appear to be waiting for anything to complete before clearing the link to the gadget. That seems risky, doesn't it? If the unbinding happens concurrently with a USB transfer, things could go sideways.
Hold on, I see `raw_gadget` also uses `dev->gadget_registered`. Thatβs another key flag. Let me go back and re-examine how `raw_gadget`'s `gadget_unbind` interacts with these other flags and states. I need to make sure I understand the full sequence of events and how they are handled. The devil is in the details.
|
| 37/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Error:
Args:
|
| 38/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 38842
output: 24
thoughts: 149
Thoughts: **Unbinding Gadgets - A Quick Discrepancy**
Right, I've got it. That `gadget_unbind` function is straightforward enough; it's designed to free up a USB gadget. The critical part is that it calls `kref_put` on `dev->count`, which is good. That's how we manage the device's lifetime. However, wait a minute... what's this `set_gadget_data` call doing? I recall that sets `gadget->dev.driver_data = NULL`. It doesn't look like it nullifies `dev->gadget`. I need to confirm. I'll need to double-check that function.
|
| 39/2 |
2026/07/29 08:08 |
tool |
codesearch-definition-source |
0m
Args: map[Name:set_gadget_data]
Results: map[SourceCode: 491: /* Interface to the device model */
492: static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
493: { dev_set_drvdata(&gadget->dev, data); }
SourceFile:include/linux/usb/gadget.h]
|
| 40/2 |
2026/07/29 08:08 |
llm |
debugger |
0m
Model:
Tokens:
input: 39264
output: 21
thoughts: 1000
Thoughts: **Analyzing a USB Gadget Driver Unbind Race Condition**
Okay, so I'm digging into this USB gadget driver and I've spotted a potential race condition during unbinding. It's clear that `set_gadget_data` sets `gadget->dev.driver_data`, but here's where things get interesting. The crucial issue seems to be that `dev->gadget` is *never* set to NULL within `gadget_unbind` itself. This means that after `gadget_unbind` returns, and assuming the gadget has already been freed, `dev->gadget` is left as a dangling pointer. This ultimately leads to a crash when `raw_process_ep0_io` subsequently tries to use that pointer.
The question then becomes how `raw_gadget` should handle the unbinding process. I think the correct approach should involve setting `dev->state = STATE_DEV_FAILED` and `dev->gadget = NULL` under a lock (`dev->lock`). However, I have to account for the case where `raw_process_ep0_io` is currently invoking `usb_ep_queue`.
If `raw_process_ep0_io` has already checked `dev->gadget` and released the lock, it can potentially proceed to call `usb_ep_queue(dev->gadget->ep0, ...)`. If `gadget_unbind` doesn't wait for the completion of any active calls to `usb_ep_queue`, then the `usb_ep_queue` call might happen on a freed gadget. And remember, `usb_ep_queue` is a function pointer in `gadget->ops`, so a use-after-free here is likely.
So, `gadget_unbind` MUST wait for any pending UDC calls to finish, similar to the `udc_usage` counter used by `gadgetfs`. It might be possible to use an equivalent `udc_usage` counter in `raw_gadget`, or perhaps the driver could just wait for `dev->ep0_urb_queued` to become false?
Wait, hold on... if `dev->ep0_urb_queued` is true, that means an URB is actually queued. But the `usb_ep_queue` call might still be in progress, so waiting on `dev->ep0_urb_queued` might not be enough.
Let me review the `raw_process_ep0_io` sequence:
```c
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
```
So, if `gadget_unbind` runs right here, it sees `dev->ep0_urb_queued == true`. If it waits for `dev->ep0_urb_queued == false`, it could potentially wait forever if `usb_ep_queue` hasn't finished, and the completion won't be called if the gadget is unbound? But, the `usb_ep_queue` function itself might set `dev->ep0_urb_queued = false` if it fails. If it succeeds, the flag is set to false in `gadget_ep0_complete`, but that might never happen if the gadget gets unbound.
Actually, I recall that `usb_gadget_unregister_driver` calls `driver->unbind` which is `gadget_unbind`. Before calling `unbind`, it first calls `usb_gadget_disconnect` which stops the UDC. When the UDC is stopped, all queued requests are completed with `-ESHUTDOWN`. This implies `gadget_ep0_complete` WILL be called. However, what if `usb_ep_queue` is currently in execution? If `usb_ep_queue` is running while the gadget is freed, it could access freed memory and lead to a crash.
The gadget itself is freed by the UDC driver *after* `usb_gadget_unregister_driver` returns. In the reproducer, `dummy_udc` is unbound. The `dummy_udc_remove` function calls `usb_del_gadget`, which in turn calls `usb_gadget_remove_driver`. That function calls `usb_gadget_disconnect` and then invokes the gadget driver's `unbind` function. Only *after* `usb_del_gadget` returns, `dummy_udc_remove` proceeds to free the gadget (via `device_unregister` -> `kfree`). Therefore, the race condition is here: if `gadget->driver->unbind` doesn't wait for `usb_ep_queue` to complete, the `usb
|
| 41/2 |
2026/07/29 08:09 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 160: struct raw_dev {
161: struct kref count;
162: spinlock_t lock;
163:
164: const char *udc_name;
165: struct usb_gadget_driver driver;
166:
167: /* Reference to misc device: */
168: struct device *dev;
169:
170: /* Make driver names unique */
171: int driver_id_number;
172:
173: /* Protected by lock: */
174: enum dev_state state;
175: bool gadget_registered;
176: struct usb_gadget *gadget;
177: struct usb_request *req;
178: bool ep0_in_pending;
179: bool ep0_out_pending;
180: bool ep0_urb_queued;
181: ssize_t ep0_status;
182: struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
183: int eps_num;
184:
185: struct completion ep0_done;
186: struct raw_event_queue queue;
187: };
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 42/2 |
2026/07/29 08:09 |
llm |
debugger |
0m
Model:
Tokens:
input: 41723
output: 25
thoughts: 758
Thoughts: **Analyzing a USB Gadget Driver Concurrency Issue**
Okay, so I'm looking at this `raw_dev` structure, and it doesn't seem to have a `udc_usage` counter, unlike `gadgetfs`. That's a point to note. Then I'm remembering `raw_gadget` does have a `dev->state` field.
The problem, I think, lies in how `gadget_unbind` interacts with `usb_ep_queue`. If `gadget_unbind` sets `dev->state = STATE_DEV_FAILED` and `dev->gadget = NULL`, it would prevent *future* calls to `usb_ep_queue`. But I don't think it waits for any *ongoing* calls. There's no obvious lock held during the `usb_ep_queue` call itself, is there? So, how does `raw_gadget` handle concurrent calls to `gadget_unbind` and `usb_ep_queue`? It doesn't! That's a potential bug.
Let's dig deeper. Looking at `raw_process_ep0_io`:
```c
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
```
If `gadget_unbind` sets `dev->gadget = NULL` while holding `dev->lock`, `raw_process_ep0_io` will fail the `if (!dev->gadget)` check. But here's the race: what if `raw_process_ep0_io` has *already* passed that check, set `dev->ep0_urb_queued = true`, and released the lock? Then `gadget_unbind` acquires the lock, sets `dev->gadget = NULL`, releases the lock, and returns. The gadget is freed. Then `raw_process_ep0_io` proceeds to call `usb_ep_queue(dev->gadget->ep0, ...)`.
Wait a minute, `dev->gadget` is read *after* releasing the lock!
```c
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
```
If `gadget_unbind` has set `dev->gadget = NULL` in the meantime, then this will result in a NULL pointer dereference, which is slightly better than UAF, but still a crash. So, to fix this properly, what can be done? Does `raw_process_ep0_io` need a local copy of the gadget or the endpoint? No, even a local copy would be vulnerable to a UAF if the underlying memory is freed.
Therefore, `gadget_unbind` *must* wait for any active calls to `raw_process_ep0_io` to complete! How can it do this? It could potentially use a `udc_usage` counter, like the `gadgetfs` does, or even an rwsem. I should check how `raw_process_ep_io` implements it. That's the next step.
|
| 43/2 |
2026/07/29 08:09 |
tool |
codesearch-definition-source |
0m
Args: map[Name:raw_process_ep_io]
Results: map[SourceCode:1058: static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
1059: void *data, bool in)
1060: {
1061: int ret = 0;
1062: unsigned long flags;
1063: struct raw_ep *ep;
1064: DECLARE_COMPLETION_ONSTACK(done);
1065:
1066: spin_lock_irqsave(&dev->lock, flags);
1067: if (dev->state != STATE_DEV_RUNNING) {
1068: dev_dbg(dev->dev, "fail, device is not running\n");
1069: ret = -EINVAL;
1070: goto out_unlock;
1071: }
1072: if (!dev->gadget) {
1073: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1074: ret = -EBUSY;
1075: goto out_unlock;
1076: }
1077: if (io->ep >= dev->eps_num) {
1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
1079: ret = -EINVAL;
1080: goto out_unlock;
1081: }
1082: ep = &dev->eps[io->ep];
1083: if (ep->state != STATE_EP_ENABLED) {
1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
1085: ret = -EBUSY;
1086: goto out_unlock;
1087: }
1088: if (ep->disabling) {
1089: dev_dbg(&dev->gadget->dev,
1090: "fail, endpoint is already being disabled\n");
1091: ret = -EBUSY;
1092: goto out_unlock;
1093: }
1094: if (ep->urb_queued) {
1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
1096: ret = -EBUSY;
1097: goto out_unlock;
1098: }
1099: if (in != usb_endpoint_dir_in(ep->ep->desc)) {
1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
1101: ret = -EINVAL;
1102: goto out_unlock;
1103: }
1104:
1105: ep->dev = dev;
1106: ep->req->context = &done;
1107: ep->req->complete = gadget_ep_complete;
1108: ep->req->buf = data;
1109: ep->req->length = io->length;
1110: ep->req->zero = usb_raw_io_flags_zero(io->flags);
1111: ep->urb_queued = true;
1112: spin_unlock_irqrestore(&dev->lock, flags);
1113:
1114: ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1115: if (ret) {
1116: dev_err(&dev->gadget->dev,
1117: "fail, usb_ep_queue returned %d\n", ret);
1118: spin_lock_irqsave(&dev->lock, flags);
1119: goto out_queue_failed;
1120: }
1121:
1122: ret = wait_for_completion_interruptible(&done);
1123: if (ret) {
1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1125: usb_ep_dequeue(ep->ep, ep->req);
1126: wait_for_completion(&done);
1127: spin_lock_irqsave(&dev->lock, flags);
1128: if (ep->status == -ECONNRESET)
1129: ep->status = -EINTR;
1130: goto out_interrupted;
1131: }
1132:
1133: spin_lock_irqsave(&dev->lock, flags);
1134:
1135: out_interrupted:
1136: ret = ep->status;
1137: out_queue_failed:
1138: ep->urb_queued = false;
1139: out_unlock:
1140: spin_unlock_irqrestore(&dev->lock, flags);
1141: return ret;
1142: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 44/2 |
2026/07/29 08:09 |
llm |
debugger |
0m
Model:
Tokens:
input: 44689
output: 25
thoughts: 1234
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in Raw Gadget Driver**
Okay, so I'm looking at this code and immediately see a problem in `raw_process_ep_io`, but it looks like the same issue plagues `raw_process_ep0_io` as well. The thing that jumps out is this `ep->urb_queued` flag. It seems like a way to track the URB queue state, but how does it interact with `gadget_unbind`? What happens to the endpoints when the gadget unbinds?
It appears `gadget_unbind` doesn't directly handle the endpoints, which is a red flag. It's called when the gadget driver is unbound, but *before* that happens, `usb_gadget_disconnect` is called, which stops the UDC. This UDC stop then dequeues all requests. This should mean that `wait_for_completion_interruptible` in `raw_process_ep0_io` will return, and the function should finish. But the big question is *what if `usb_ep_queue` is actively executing*?
If `usb_ep_queue` is running concurrently, it could try to access freed memory after `gadget_unbind` has run. That's a classic use-after-free scenario. And to make matters worse, `usb_ep_queue` is called without a lock! I'm thinking... does `gadget_unbind` set `dev->state = STATE_DEV_FAILED` and `dev->gadget = NULL`? If it doesn't wait for `usb_ep_queue` to finish, the UAF is still a possibility.
I know `gadgetfs` uses `udc_usage`. Maybe a similar counter or even an rwsem would be a good approach here? But wait, the critical issue seems to be that `dev->gadget` is NOT being set to NULL in `gadget_unbind`! This is the core problem! If `dev->gadget` is not NULL, `raw_process_ep0_io` can start a *new* `usb_ep_queue` *after* `gadget_unbind` has run! The `dev->state` is still `STATE_DEV_RUNNING`! It's an open door to a UAF!
Let's check if `dev->state` is changed in `gadget_unbind`. Nope, it isn't. So if `gadget_unbind` finishes, the gadget is freed. Then a user-space process calls `USB_RAW_IOCTL_EP0_READ`. `raw_process_ep0_io` checks `dev->state == STATE_DEV_RUNNING` (true). It checks `dev->gadget != NULL` (true, because it was *not* cleared). Then it calls `usb_ep_queue(dev->gadget->ep0, ...)`. That's a 100% use-after-free on a *new* ioctl!
So, the fundamental bug is that `dev->gadget` is not cleared and `dev->state` isn't updated in `gadget_unbind`.
Let's verify this. If `gadget_unbind` sets `dev->state = STATE_DEV_FAILED` and `dev->gadget = NULL` under a lock, will it fix the UAF for *new* ioctls? Yes, definitely. However, what about *concurrent* ioctls?
If an ioctl is currently in flight, between `spin_unlock_irqrestore` and `usb_ep_queue`, it already has a local pointer to `dev->gadget`. Even though there is a local pointer, `usb_ep_queue` will access `dev->gadget->ep0`, so it reads `dev->gadget` again. If `gadget_unbind` sets `dev->gadget = NULL`, that becomes a NULL pointer dereference.
To avoid the NULL dereference, `raw_process_ep0_io` could do:
```c
struct usb_gadget *gadget = dev->gadget;
...
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(gadget->ep0, dev->req, GFP_KERNEL);
```
But then `gadget` might still be freed right away! How does the kernel typically solve this? The gadget driver normally calls `usb_ep_queue`, and is unbound via `driver->unbind`. The UDC core calls `driver->unbind`. If `driver->unbind` returns, the UDC core *assumes* the gadget driver has finished using the gadget. So, `driver->
|
| 45/2 |
2026/07/29 08:09 |
tool |
codesearch-definition-source |
0m
Args: map[Name:gadget_ep0_complete]
Results: map[SourceCode: 249: static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
250: {
251: struct raw_dev *dev = req->context;
252: unsigned long flags;
253:
254: spin_lock_irqsave(&dev->lock, flags);
255: if (req->status)
256: dev->ep0_status = req->status;
257: else
258: dev->ep0_status = req->actual;
259: if (dev->ep0_in_pending)
260: dev->ep0_in_pending = false;
261: else
262: dev->ep0_out_pending = false;
263: spin_unlock_irqrestore(&dev->lock, flags);
264:
265: complete(&dev->ep0_done);
266: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 46/2 |
2026/07/29 08:09 |
llm |
debugger |
0m
Model:
Tokens:
input: 47277
output: 21
thoughts: 896
Thoughts: **Analyzing the ep0 Unbinding Sequence**
Okay, so I'm looking at this `gadget_ep0_complete` function and immediately something jumps out at me: it *doesn't* actually reset the `dev->ep0_urb_queued` flag to `false`. That's a crucial piece of information. So, where *does* that flag get cleared? Ah, right, it's in `raw_process_ep0_io`. Specifically, after the `wait_for_completion_interruptible` call. The important point is that the flag is set to `false` *after* that wait returns, specifically within the same thread that called `raw_process_ep0_io`, and after `wait_for_completion` returns.
So if `gadget_unbind` is waiting for `dev->ep0_urb_queued` to become `false`, it's effectively waiting for the ioctl thread, the one running `raw_process_ep0_io`, to finish up. Is that safe? Let's break it down.
If `gadget_unbind` is called, the UDC core has already called `usb_gadget_disconnect`. This means any queued URBs should be completed with `-ESHUTDOWN`. So, in theory, that `wait_for_completion` should return pretty quickly.
But here's a potential snag: what if `raw_process_ep0_io` is stuck in that `wait_for_completion_interruptible` call and the UDC driver *doesn't* complete the URB? Okay, but the UDC driver *must* complete all URBs when it's disconnected, right? Therefore, that wait will always return, eventually. Then, `raw_process_ep0_io` sets the flag, so we're good. `gadget_unbind` could wait.
However, a new scenario presents itself: what if `raw_process_ep0_io` is just *about* to call `usb_ep_queue`? We have this crucial snippet:
```c
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
// <--- gadget_unbind runs here
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
```
If `gadget_unbind` is waiting for `dev->ep0_urb_queued == false`, it'll be waiting for `usb_ep_queue` to *complete* and for the completion to be signaled. And here's where it gets dangerous. If `gadget_unbind` is running, the UDC is unbinding. If `usb_ep_queue` is called *while* the UDC is unbinding, we might run into a crash because the UDC has already freed resources.
But wait. The UDC driver *can* safely call `usb_ep_queue` until `driver->unbind` returns, right? That's the contract. The UDC driver *must not* free resources until `driver->unbind` returns. So, if `gadget_unbind` waits for `dev->ep0_urb_queued == false`, it effectively blocks the return from `driver->unbind`, which stops the driver from freeing resources at the wrong time. This means that if it waits, `usb_ep_queue` will either safely fail or succeed.
But what happens if `usb_ep_queue` *succeeds*? The URB is queued, but the UDC is already disconnected! Will the UDC complete this newly queued URB? Typically, with a disconnected UDC, `usb_ep_queue` will return either `-ESHUTDOWN` or `-ENODEV`. And if it returns an error, `raw_process_ep0_io` sets the flag to `false` and exits. But what if it succeeds? The UDC *should* complete it. But since the UDC is unbinding, it might not complete it at all.
Let's think. The UDC unbinding process does `usb_gadget_disconnect`, which stops the controller, *then* it calls `driver->unbind`. So, if `usb_ep_queue` is called *after* `usb_gadget_disconnect`, the UDC *should* just reject it. For example, `dummy_udc` does just that! I need to check how `dummy_queue` in `dummy_hcd.c` works. I need to be sure.
|
| 47/2 |
2026/07/29 08:09 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 718: static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
719: gfp_t mem_flags)
720: {
721: struct dummy_ep *ep;
722: struct dummy_request *req;
723: struct dummy *dum;
724: struct dummy_hcd *dum_hcd;
725: unsigned long flags;
726:
727: req = usb_request_to_dummy_request(_req);
728: if (!_req || !list_empty(&req->queue) || !_req->complete)
729: return -EINVAL;
730:
731: ep = usb_ep_to_dummy_ep(_ep);
732: if (!_ep || (!ep->desc && _ep->name != ep0name))
733: return -EINVAL;
734:
735: dum = ep_to_dummy(ep);
736: dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
737: if (!dum->driver || !is_enabled(dum_hcd))
738: return -ESHUTDOWN;
739:
740: #if 0
741: dev_dbg(udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
742: ep, _req, _ep->name, _req->length, _req->buf);
743: #endif
744: _req->status = -EINPROGRESS;
745: _req->actual = 0;
746: spin_lock_irqsave(&dum->lock, flags);
747:
748: /* implement an emulated single-request FIFO */
749: if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
750: !dum->fifo_req_busy &&
751: list_empty(&ep->queue) &&
752: _req->length <= FIFO_SIZE) {
753: req = &dum->fifo_req;
754: dum->fifo_req_busy = 1;
755: req->req = *_req;
756: req->req.buf = dum->fifo_buf;
757: memcpy(dum->fifo_buf, _req->buf, _req->length);
758: req->req.context = dum;
759: req->req.complete = fifo_complete;
760:
761: list_add_tail(&req->queue, &ep->queue);
762: spin_unlock(&dum->lock);
763: _req->actual = _req->length;
764: _req->status = 0;
765: usb_gadget_giveback_request(_ep, _req);
766: spin_lock(&dum->lock);
767: } else
768: list_add_tail(&req->queue, &ep->queue);
769: spin_unlock_irqrestore(&dum->lock, flags);
770:
771: /* real hardware would likely enable transfers here, in case
772: * it'd been left NAKing.
773: */
774: return 0;
775: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 48/2 |
2026/07/29 08:09 |
llm |
debugger |
0m
Model:
Tokens:
input: 50174
output: 26
thoughts: 233
Thoughts: **Analyzing the Dummy Gadget Driver's Shutdown Sequence**
Okay, let's break down this `dummy_queue` function. The initial check is straightforward: we bail out with `-ESHUTDOWN` if the driver isn't present or if the hardware controller, `dum_hcd`, is disabled. That seems sensible. Now, the potential gotcha is what happens during unbinding. The documentation says `dum->driver` is held for the unbind callback.
Wait a second, what about the `is_enabled(dum_hcd)` check? This could be false if the device is disconnected. But, if that returns zero, the request gets *queued*. What happens to queued requests when it goes offline? Do they just sit there, or does something handle them?
Here's the critical point. If `dummy_udc_remove` is called, it eventually calls `usb_del_gadget`. That call cascades into `usb_gadget_remove_driver`, which then calls `usb_gadget_disconnect(gadget)`. And that's where things get interesting. This function calls `udc->gadget->ops->udc_stop(udc->gadget)`.
Aha! `udc_stop` is called *before* the driver's `unbind` routine. That implies that the underlying UDC operations might be stopped *before* the driver completely unbinds, which could lead to resource leaks if we're not careful. I need to trace this in detail, so let's look closer at the `usb_gadget_remove_driver` flow. I'm going to have to check if this is the sequence of events.
|
| 49/2 |
2026/07/29 08:10 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[Name:usb_gadget_remove_driver]
Results: map[SourceCode: SourceFile:]
|
| 50/2 |
2026/07/29 08:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 50813
output: 24
thoughts: 23
Thoughts: Here's a summary of my thought process, as requested:
**Debugging USB Gadget Driver Unregistration**
Okay, so I'm encountering an issue, and my initial hunch points to problems with how the USB gadget driver is being unregistered. Let's see... I need to figure out the exact point of failure, so I'll start by tracing the unregistration process. My brain is immediately drawn to a couple of key suspects: `usb_gadget_unregister_driver` and `usb_del_gadget`. Those are the usual suspects when you have a device that's failing during cleanup. It's time to dig into the kernel code and see what those functions are doing, how they're interacting with the device structure, and where things might be going sideways. Gotta pinpoint the exact culprit function!
|
| 51/2 |
2026/07/29 08:10 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode:1568: /**
1569: * usb_del_gadget - deletes a gadget and unregisters its udc
1570: * @gadget: the gadget to be deleted.
1571: *
1572: * This will unbind @gadget, if it is bound.
1573: * It will not do a final usb_put_gadget().
1574: */
1575: void usb_del_gadget(struct usb_gadget *gadget)
1576: {
1577: struct usb_udc *udc = gadget->udc;
1578: unsigned long flags;
1579:
1580: if (!udc)
1581: return;
1582:
1583: dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
1584:
1585: mutex_lock(&udc_lock);
1586: list_del(&udc->list);
1587: mutex_unlock(&udc_lock);
1588:
1589: kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
1590: sysfs_remove_link(&udc->dev.kobj, "gadget");
1591: device_del(&gadget->dev);
1592: /*
1593: * Set the teardown flag before flushing the work to prevent new work
1594: * from being scheduled while we are cleaning up.
1595: */
1596: spin_lock_irqsave(&gadget->state_lock, flags);
1597: gadget->teardown = true;
1598: spin_unlock_irqrestore(&gadget->state_lock, flags);
1599: flush_work(&gadget->work);
1600: ida_free(&gadget_id_numbers, gadget->id_number);
1601: cancel_work_sync(&udc->vbus_work);
1602: device_unregister(&udc->dev);
1603: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 52/2 |
2026/07/29 08:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 51633
output: 24
thoughts: 84
Thoughts: Okay, let me think this through...
**USB Gadget Deletion Process**
Hmm, so `usb_del_gadget` calls `device_del(&gadget->dev)`. That's the key step. But I need to refresh my memory, where *exactly* does the driver get unbound in this process? I need to trace this... Ah, right, it's coming back to me. `device_del` takes care of the driver unbinding! That's it! Because the gadget *itself* is a device on the gadget bus! It's fundamentally a device object. So, when that device (`gadget->dev`) is removed, the driver core will automatically trigger `gadget_unbind_driver`. That's precisely how the system cleans everything up. Perfect. Now I understand the control flow.
|
| 53/2 |
2026/07/29 08:10 |
tool |
codesearch-definition-source |
0m
Args: map[Name:gadget_unbind_driver]
Results: map[SourceCode:1707: static void gadget_unbind_driver(struct device *dev)
1708: {
1709: struct usb_gadget *gadget = dev_to_usb_gadget(dev);
1710: struct usb_udc *udc = gadget->udc;
1711: struct usb_gadget_driver *driver = udc->driver;
1712:
1713: dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
1714:
1715: udc->allow_connect = false;
1716: cancel_work_sync(&udc->vbus_work);
1717: mutex_lock(&udc->connect_lock);
1718: usb_gadget_disconnect_locked(gadget);
1719: usb_gadget_disable_async_callbacks(udc);
1720: if (gadget->irq)
1721: synchronize_irq(gadget->irq);
1722: mutex_unlock(&udc->connect_lock);
1723:
1724: udc->driver->unbind(gadget);
1725:
1726: mutex_lock(&udc->connect_lock);
1727: usb_gadget_udc_stop_locked(udc);
1728: mutex_unlock(&udc->connect_lock);
1729:
1730: mutex_lock(&udc_lock);
1731: driver->is_bound = false;
1732: udc->driver = NULL;
1733: mutex_unlock(&udc_lock);
1734:
1735: kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
1736: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 54/2 |
2026/07/29 08:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 52441
output: 21
thoughts: 2058
Thoughts: **Analyzing a USB Gadget Driver Unbind Sequence**
Okay, so I'm digging into this `gadget_unbind_driver` function, and it's calling `usb_gadget_disconnect_locked`, which in turn leads to a series of calls: disabling async callbacks, the driver's unbind, and then stopping the UDC. The critical point is that the `udc_stop` call happens *after* the `driver->unbind` function. This means that while the `unbind` function is executing, the UDC is still technically "started," even though it's disconnected.
My concern is what happens if `usb_ep_queue` is called during the execution of `driver->unbind`. Since the UDC is disconnected, any queued requests might not complete until `udc_stop` is called. If a request is queued after `usb_gadget_disconnect`, it could just sit there forever! This could lead to `wait_for_completion` hanging indefinitely in `raw_process_ep0_io`. The core problem seems to be the potential for a Use-After-Free (UAF) situation. Basically, a new ioctl can potentially call `usb_ep_queue` on a freed gadget because `dev->gadget` isn't cleared.
I see that `device_del(&gadget->dev)` and `device_unregister(&udc->dev)` are called, and then the gadget is freed. If `raw_process_ep0_io` is called after the gadget is freed and uses `dev->gadget`, that's a problem. Therefore, I need to make sure to clear `dev->gadget` in `gadget_unbind`. I also need to make sure that nothing attempts to call `usb_ep_queue` after this is cleared.
Let's look more closely at `raw_process_ep0_io`. The critical code is:
```c
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { ... }
if (!dev->gadget) { ... }
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
```
My idea is to change `gadget_unbind` to clear `dev->gadget`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
If `raw_process_ep0_io` grabs the lock, sees `dev->gadget != NULL`, sets `dev->ep0_urb_queued`, and then *releases* the lock before `gadget_unbind` is run, `gadget_unbind` runs, NULL's `dev->gadget`, and *releases* the lock. Then `raw_process_ep0_io` calls `usb_ep_queue(dev->gadget->ep0, ...)`. At this point, the `dev->gadget` is NULL, and I get a NULL pointer dereference, which crashes the system. This is better than a UAF, but still a bug.
To fix that, I should change `raw_process_ep0_io` to:
```c
struct usb_gadget *gadget;
...
spin_lock_irqsave(&dev->lock, flags);
if (!dev->gadget) { ... }
gadget = dev->gadget;
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(gadget->ep0, dev->req, GFP_KERNEL);
```
Now, `gadget` will never be NULL. However, after `gadget_unbind` returns, the `gadget` is freed. So then `usb_ep_queue(gadget->ep0, ...)` is a UAF. Therefore, I MUST wait for `raw
|
| 55/2 |
2026/07/29 08:10 |
tool |
codesearch-definition-source |
0m
Error:
Args:
Results: map[SourceCode: SourceFile:]
|
| 56/2 |
2026/07/29 08:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 55566
output: 21
thoughts: 15
Thoughts: Here's a summary of my thought process, as requested:
**Navigating the GadgetFS Ecosystem**
Okay, so I'm looking at `gadgetfs`. My initial thought is to zero in on a critical area, specifically `ep_io`. It's a key component, naturally. I want to quickly assess its role, its functions within the broader framework of `gadgetfs`. My expertise tells me that this is likely a gateway to understanding the input/output operations, which are absolutely fundamental to the file system's functionality. This is where the magic happens, so to speak. I need to understand how data flows in and out, the underlying mechanisms for communication between the system and any connected USB devices. `ep_io` is a very good starting point for that. This is my next focus.
|
| 57/2 |
2026/07/29 08:10 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 335: static ssize_t
336: ep_io (struct ep_data *epdata, void *buf, unsigned len)
337: {
338: DECLARE_COMPLETION_ONSTACK (done);
339: int value;
340:
341: spin_lock_irq (&epdata->dev->lock);
342: if (likely (epdata->ep != NULL)) {
343: struct usb_request *req = epdata->req;
344:
345: req->context = &done;
346: req->complete = epio_complete;
347: req->buf = buf;
348: req->length = len;
349: value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC);
350: } else
351: value = -ENODEV;
352: spin_unlock_irq (&epdata->dev->lock);
353:
354: if (likely (value == 0)) {
355: value = wait_for_completion_interruptible(&done);
356: if (value != 0) {
357: spin_lock_irq (&epdata->dev->lock);
358: if (likely (epdata->ep != NULL)) {
359: DBG (epdata->dev, "%s i/o interrupted\n",
360: epdata->name);
361: usb_ep_dequeue (epdata->ep, epdata->req);
362: spin_unlock_irq (&epdata->dev->lock);
363:
364: wait_for_completion(&done);
365: if (epdata->status == -ECONNRESET)
366: epdata->status = -EINTR;
367: } else {
368: spin_unlock_irq (&epdata->dev->lock);
369:
370: DBG (epdata->dev, "endpoint gone\n");
371: wait_for_completion(&done);
372: epdata->status = -ENODEV;
373: }
374: }
375: return epdata->status;
376: }
377: return value;
378: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 58/2 |
2026/07/29 08:10 |
llm |
debugger |
0m
Model:
Tokens:
input: 56462
output: 22
thoughts: 3978
Thoughts: **Root Cause Analysis: UAF in raw_gadget During Gadget Unbind**
Okay, so I've been dissecting this issue and here's my understanding: The crux of the problem lies in a use-after-free vulnerability within the `raw_gadget` USB gadget driver. The vulnerability occurs when a USB gadget, managed by `raw_gadget`, is unbound, and then subsequent I/O operations are attempted on that gadget.
Here's the chain of events. First, the gadget driver registers and binds to a USB device, say using `syz_usb_connect`. This sets the gadget pointer and state to `RUNNING`. Then, if the USB device is unbound using sysfs, the `gadget_unbind` function is called. The problem is that `gadget_unbind` does *not* properly synchronize with concurrent I/O operations and doesn't clear important pointers. Specifically:
1. **Race Condition:** The `usb_ep_queue` function (which handles endpoint queueing for USB transfers) is called without holding the `dev->lock`. This means there's a race with `gadget_unbind`.
2. **Improper Gadget Unbind:** `gadget_unbind` is called when the device is unbound. It calls `set_gadget_data(gadget, NULL)` to drop driver data, and it's *supposed* to signal that the gadget is no longer valid. However, it *fails* to clear `dev->gadget`. It also fails to modify `dev->state`.
3. **Use-After-Free:** Later, an ioctl such as `USB_RAW_IOCTL_EP0_READ` (or any other) gets called, and it checks `dev->state` and `dev->gadget`, both of which are still the same as before unbind, which incorrectly evaluates to true. It proceeds to call `usb_ep_queue` on a pointer to `dev->gadget->ep0`, but the gadget has already been freed by `usb_del_gadget`, leading to a use-after-free! The crash occurs in `dev_err` because it attempts to print the device name, accessing a freed memory location.
4. **Error Path is Exploitable:** The kernel crashes in `dev_err` after an error is returned from `usb_ep_queue`. The error is returned due to the UDC already being disabled.
Now, I was initially considering using `dev->ep0_urb_queued` to wait for completion of I/O, but that seems problematic and prone to potential deadlocks. Instead, a synchronization mechanism like a `udc_usage` counter or an `rwsem` seems better to prevent the race condition. The key is to *ensure* that all queued operations finish *before* the gadget is actually freed and before the gadget pointer is nulled. I need to make sure that any concurrent call to `usb_ep_queue` completes *before* the gadget's memory is freed. The `gadgetfs` driver has some mechanisms that could be useful here.
Essentially, I need a fix that ensures proper synchronization during unbinding, preventing the UAF and the resulting kernel crash. The proper fix is to ensure the calls to `usb_ep_queue` have completed before setting `dev->gadget` to `NULL`. This can be done with a `udc_usage` or using `rwsem`.
|
| 59/2 |
2026/07/29 08:11 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 912: static ssize_t
913: ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
914: {
915: struct dev_data *dev = fd->private_data;
916: ssize_t retval;
917: enum ep0_state state;
918:
919: spin_lock_irq (&dev->lock);
920: if (dev->state <= STATE_DEV_OPENED) {
921: retval = -EINVAL;
922: goto done;
923: }
924:
925: /* report fd mode change before acting on it */
926: if (dev->setup_abort) {
927: dev->setup_abort = 0;
928: retval = -EIDRM;
929: goto done;
930: }
931:
932: /* control DATA stage */
933: if ((state = dev->state) == STATE_DEV_SETUP) {
934:
935: if (dev->setup_in) { /* stall IN */
936: VDEBUG(dev, "ep0in stall\n");
937: (void) usb_ep_set_halt (dev->gadget->ep0);
938: retval = -EL2HLT;
939: dev->state = STATE_DEV_CONNECTED;
940:
941: } else if (len == 0) { /* ack SET_CONFIGURATION etc */
942: struct usb_ep *ep = dev->gadget->ep0;
943: struct usb_request *req = dev->req;
944:
945: if ((retval = setup_req (ep, req, 0)) == 0) {
946: ++dev->udc_usage;
947: spin_unlock_irq (&dev->lock);
948: retval = usb_ep_queue (ep, req, GFP_KERNEL);
949: spin_lock_irq (&dev->lock);
950: --dev->udc_usage;
951: }
952: dev->state = STATE_DEV_CONNECTED;
953:
954: /* assume that was SET_CONFIGURATION */
955: if (dev->current_config) {
956: unsigned power;
957:
958: if (gadget_is_dualspeed(dev->gadget)
959: && (dev->gadget->speed
960: == USB_SPEED_HIGH))
961: power = dev->hs_config->bMaxPower;
962: else
963: power = dev->config->bMaxPower;
964: usb_gadget_vbus_draw(dev->gadget, 2 * power);
965: }
966:
967: } else { /* collect OUT data */
968: if ((fd->f_flags & O_NONBLOCK) != 0
969: && !dev->setup_out_ready) {
970: retval = -EAGAIN;
971: goto done;
972: }
973: spin_unlock_irq (&dev->lock);
974: retval = wait_event_interruptible (dev->wait,
975: dev->setup_out_ready != 0);
976:
977: /* FIXME state could change from under us */
978: spin_lock_irq (&dev->lock);
979: if (retval)
980: goto done;
981:
982: if (dev->state != STATE_DEV_SETUP) {
983: retval = -ECANCELED;
984: goto done;
985: }
986: dev->state = STATE_DEV_CONNECTED;
987:
988: if (dev->setup_out_error)
989: retval = -EIO;
990: else {
991: len = min (len, (size_t)dev->req->actual);
992: ++dev->udc_usage;
993: spin_unlock_irq(&dev->lock);
994: if (copy_to_user (buf, dev->req->buf, len))
995: retval = -EFAULT;
996: else
997: retval = len;
998: spin_lock_irq(&dev->lock);
999: --dev->udc_usage;
1000: clean_req (dev->gadget->ep0, dev->req);
1001: /* NOTE userspace can't yet choose to stall */
1002: }
1003: }
1004: goto done;
1005: }
1006:
1007: /* else normal: return event data */
1008: if (len < sizeof dev->event [0]) {
1009: retval = -EINVAL;
1010: goto done;
1011: }
1012: len -= len % sizeof (struct usb_gadgetfs_event);
1013: dev->usermode_setup = 1;
1014:
1015: scan:
1016: /* return queued events right away */
1017: if (dev->ev_next != 0) {
1018: unsigned i, n;
1019:
1020: n = len / sizeof (struct usb_gadgetfs_event);
1021: if (dev->ev_next < n)
1022: n = dev->ev_next;
1023:
1024: /* ep0 i/o has special semantics during STATE_DEV_SETUP */
1025: for (i = 0; i < n; i++) {
1026: if (dev->event [i].type == GADGETFS_SETUP) {
1027: dev->state = STATE_DEV_SETUP;
1028: n = i + 1;
1029: break;
1030: }
1031: }
1032: spin_unlock_irq (&dev->lock);
1033: len = n * sizeof (struct usb_gadgetfs_event);
1034: if (copy_to_user (buf, &dev->event, len))
1035: retval = -EFAULT;
1036: else
1037: retval = len;
1038: if (len > 0) {
1039: /* NOTE this doesn't guard against broken drivers;
1040: * concurrent ep0 readers may lose events.
1041: */
1042: spin_lock_irq (&dev->lock);
1043: if (dev->ev_next > n) {
1044: memmove(&dev->event[0], &dev->event[n],
1045: sizeof (struct usb_gadgetfs_event)
1046: * (dev->ev_next - n));
1047: }
1048: dev->ev_next -= n;
1049: spin_unlock_irq (&dev->lock);
1050: }
1051: return retval;
1052: }
1053: if (fd->f_flags & O_NONBLOCK) {
1054: retval = -EAGAIN;
1055: goto done;
1056: }
1057:
1058: switch (state) {
1059: default:
1060: DBG (dev, "fail %s, state %d\n", __func__, state);
1061: retval = -ESRCH;
1062: break;
1063: case STATE_DEV_UNCONNECTED:
1064: case STATE_DEV_CONNECTED:
1065: spin_unlock_irq (&dev->lock);
1066: DBG (dev, "%s wait\n", __func__);
1067:
1068: /* wait for events */
1069: retval = wait_event_interruptible (dev->wait,
1070: dev->ev_next != 0);
1071: if (retval < 0)
1072: return retval;
1073: spin_lock_irq (&dev->lock);
1074: goto scan;
1075: }
1076:
1077: done:
1078: spin_unlock_irq (&dev->lock);
1079: return retval;
1080: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 60/2 |
2026/07/29 08:11 |
llm |
debugger |
0m
Model:
Tokens:
input: 63673
output: 21
thoughts: 2709
Thoughts: **My Analysis of the USB Gadget Driver's UAF Vulnerability**
Okay, let me lay out my thinking here. I've been tracing the execution path and it seems pretty clear now.
My initial observation was that `clean_req` in `ep0_read` is called *after* `wait_event_interruptible`, but accesses `dev->gadget->ep0`. This immediately raised a flag because `gadgetfs_unbind` could potentially run concurrently. Since `gadgetfs_unbind` waits for `udc_usage == 0`, and this counter seems to be incremented before `copy_to_user`, then decremented after `clean_req` is done, it seems safe. `udc_usage` acts as an indicator that the gadget is busy.
I then considered `raw_gadget`, and thought about using `udc_usage` there. I realized that the `udc_usage` approach is equivalent to `dev->ep0_urb_queued`, which is already used by `raw_gadget` to keep track of a URB being queued on ep0, and similar for other endpoints using `ep->urb_queued`. This is a fine solution, and the driver should wait for those flags to be false when unbinding. I was confident that `usb_gadget_disconnect` will complete all queued URBs because it calls `udc->gadget->ops->pullup(udc->gadget, 0)` which flushes endpoints and completes all URBs with `-ESHUTDOWN`, so `wait_for_completion` will return.
However, the question that I was stuck on was: why does `raw_process_ep0_io` release the spinlock *before* calling `usb_ep_queue`? Then I realized that `raw_gadget` uses `GFP_KERNEL` when queuing the URB. This means that `usb_ep_queue` *can* sleep, so the spinlock *must* be released. `gadgetfs` uses `GFP_ATOMIC`, but `raw_gadget` can't do that.
This release of the lock is the problem! If `gadget_unbind` runs concurrently, it can clear `dev->gadget` after `raw_process_ep0_io` has released the lock and before `usb_ep_queue` is called. That leads to a use-after-free because `usb_ep_queue` accesses a freed gadget. If a local copy of the gadget is used, the local copy is also freed.
So the core problem is that `gadget_unbind` does not clear `dev->gadget`. The new ioctl operations, see `dev->state == STATE_DEV_RUNNING` and a non-null `dev->gadget`, and proceed to call `usb_ep_queue`. The gadget has been freed by the time `usb_ep_queue` is called. The crash occurs when `usb_ep_queue` returns an error (due to the disconnect) and then `dev_err` is called, accessing freed memory.
And I need to note that this is NOT a bug in the code in my view because it explicitly uses the mechanisms for the UDC to tell the driver that the gadget is no longer available.
|
| 61/2 |
2026/07/29 08:11 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 409: static int raw_release(struct socket *sock)
410: {
411: struct sock *sk = sock->sk;
412: struct raw_sock *ro;
413: struct net *net;
414:
415: if (!sk)
416: return 0;
417:
418: ro = raw_sk(sk);
419: net = sock_net(sk);
420:
421: spin_lock(&raw_notifier_lock);
422: while (raw_busy_notifier == ro) {
423: spin_unlock(&raw_notifier_lock);
424: schedule_timeout_uninterruptible(1);
425: spin_lock(&raw_notifier_lock);
426: }
427: list_del(&ro->notifier);
428: spin_unlock(&raw_notifier_lock);
429:
430: rtnl_lock();
431: lock_sock(sk);
432:
433: /* remove current filters & unregister */
434: if (ro->bound) {
435: if (ro->dev) {
436: raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
437: netdev_put(ro->dev, &ro->dev_tracker);
438: } else {
439: raw_disable_allfilters(net, NULL, sk);
440: }
441: }
442:
443: if (ro->count > 1)
444: kfree(ro->filter);
445:
446: ro->ifindex = 0;
447: ro->bound = 0;
448: ro->dev = NULL;
449: ro->count = 0;
450:
451: sock_orphan(sk);
452: sock->sk = NULL;
453:
454: release_sock(sk);
455: rtnl_unlock();
456:
457: sock_prot_inuse_add(net, sk->sk_prot, -1);
458: sock_put(sk);
459:
460: return 0;
461: }
SourceFile:net/can/raw.c]
|
| 62/2 |
2026/07/29 08:11 |
llm |
debugger |
0m
Model:
Tokens:
input: 67821
output: 21
thoughts: 33
Thoughts: Okay, let's see. This `raw_release` function... something feels off. I need to get to the bottom of this. Right, the code in question is likely located within the USB gadget driver framework, and the "legacy" directory is usually where the older code resides. Let me drill down into `drivers/usb/gadget/legacy/raw_gadget.c` and take a closer look at that specific function. I'm going to carefully review the implementation of `raw_release` there to understand its behavior and pinpoint the issue. Time to dig in!
|
| 63/2 |
2026/07/29 08:11 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 409: static int raw_release(struct socket *sock)
410: {
411: struct sock *sk = sock->sk;
412: struct raw_sock *ro;
413: struct net *net;
414:
415: if (!sk)
416: return 0;
417:
418: ro = raw_sk(sk);
419: net = sock_net(sk);
420:
421: spin_lock(&raw_notifier_lock);
422: while (raw_busy_notifier == ro) {
423: spin_unlock(&raw_notifier_lock);
424: schedule_timeout_uninterruptible(1);
425: spin_lock(&raw_notifier_lock);
426: }
427: list_del(&ro->notifier);
428: spin_unlock(&raw_notifier_lock);
429:
430: rtnl_lock();
431: lock_sock(sk);
432:
433: /* remove current filters & unregister */
434: if (ro->bound) {
435: if (ro->dev) {
436: raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
437: netdev_put(ro->dev, &ro->dev_tracker);
438: } else {
439: raw_disable_allfilters(net, NULL, sk);
440: }
441: }
442:
443: if (ro->count > 1)
444: kfree(ro->filter);
445:
446: ro->ifindex = 0;
447: ro->bound = 0;
448: ro->dev = NULL;
449: ro->count = 0;
450:
451: sock_orphan(sk);
452: sock->sk = NULL;
453:
454: release_sock(sk);
455: rtnl_unlock();
456:
457: sock_prot_inuse_add(net, sk->sk_prot, -1);
458: sock_put(sk);
459:
460: return 0;
461: }
SourceFile:net/can/raw.c]
|
| 64/2 |
2026/07/29 08:11 |
llm |
debugger |
0m
Model:
Tokens:
input: 68711
output: 40
thoughts: 7
Thoughts: Here's your summary:
**The Crucial First Step: Defining the Input**
Okay, so I'm getting ready to dive into this. The very first thing, before I can even *think* about any analysis or processing, is that I absolutely *must* nail down the specifics of the input. I need to know precisely which file I'm working with. This isn't just a matter of pointing to a document; it's about the entire workflow. The file name and location need to be absolutely unambiguous so the code will find it. File paths and naming conventions are critically important for this process. I need to make sure I've got that completely right, or this whole process is dead in the water before it even begins. Time to get that specified, then I can move on.
|
| 65/2 |
2026/07/29 08:11 |
tool |
grepper |
0m
Args: map[Expression:static int raw_release PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=427=static int raw_open(struct inode *inode, struct file *fd)
--
drivers/usb/gadget/legacy/raw_gadget.c-443-
drivers/usb/gadget/legacy/raw_gadget.c:444:static int raw_release(struct inode *inode, struct file *fd)
drivers/usb/gadget/legacy/raw_gadget.c-445-{
]
|
| 66/2 |
2026/07/29 08:11 |
llm |
debugger |
0m
Model:
Tokens:
input: 69056
output: 21
thoughts: 27
Thoughts: Okay, let's dive into this. Here's my thought process on this:
**Investigating `raw_release` in `drivers/usb/gadget/legacy/raw_gadget.c`**
Alright, the objective is to understand the function `raw_release` within the USB gadget legacy driver. My initial instinct is to check out the definition of `raw_release` in the specified file, `drivers/usb/gadget/legacy/raw_gadget.c`. I'm going to want to understand what it does, what it's responsible for, how it relates to other parts of the gadget driver, and hopefully, uncover how it functions, especially in relation to memory management or resource cleanup after a device instance is released. Since it's a "release" function, my expectation is it's related to deallocation, but the context within a legacy gadget driver is important. Knowing exactly how the `raw_gadget` works will be key to understanding the full implications of this function. I'll need to look for any calls it makes, any data structures it manipulates, and the surrounding code for clues. I might even need to look at the corresponding probe function to build a complete picture of its lifecycle. Let's get to it!
|
| 67/2 |
2026/07/29 08:11 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 409: static int raw_release(struct socket *sock)
410: {
411: struct sock *sk = sock->sk;
412: struct raw_sock *ro;
413: struct net *net;
414:
415: if (!sk)
416: return 0;
417:
418: ro = raw_sk(sk);
419: net = sock_net(sk);
420:
421: spin_lock(&raw_notifier_lock);
422: while (raw_busy_notifier == ro) {
423: spin_unlock(&raw_notifier_lock);
424: schedule_timeout_uninterruptible(1);
425: spin_lock(&raw_notifier_lock);
426: }
427: list_del(&ro->notifier);
428: spin_unlock(&raw_notifier_lock);
429:
430: rtnl_lock();
431: lock_sock(sk);
432:
433: /* remove current filters & unregister */
434: if (ro->bound) {
435: if (ro->dev) {
436: raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
437: netdev_put(ro->dev, &ro->dev_tracker);
438: } else {
439: raw_disable_allfilters(net, NULL, sk);
440: }
441: }
442:
443: if (ro->count > 1)
444: kfree(ro->filter);
445:
446: ro->ifindex = 0;
447: ro->bound = 0;
448: ro->dev = NULL;
449: ro->count = 0;
450:
451: sock_orphan(sk);
452: sock->sk = NULL;
453:
454: release_sock(sk);
455: rtnl_unlock();
456:
457: sock_prot_inuse_add(net, sk->sk_prot, -1);
458: sock_put(sk);
459:
460: return 0;
461: }
SourceFile:net/can/raw.c]
|
| 68/2 |
2026/07/29 08:11 |
llm |
debugger |
0m
Model:
Tokens:
input: 70086
output: 43
thoughts: 8
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment: A Direct Approach**
Alright, the task at hand seems straightforward. My initial reaction is, *let's dive in*. There's no need for any elaborate pre-processing or strategic planning at this stage. My instinct is to directly engage with the file itself. I'll simply read it. It's the most efficient starting point, providing a rapid overview and context. Perhaps the file's structure or content will immediately point toward the necessary next steps. Efficiency is key; I'll start with the base-level data.
|
| 69/2 |
2026/07/29 08:11 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:444 LineCount:50]
Results: map[Contents: 444: static int raw_release(struct inode *inode, struct file *fd)
445: {
446: int ret = 0;
447: struct raw_dev *dev = fd->private_data;
448: unsigned long flags;
449: bool unregister = false;
450:
451: spin_lock_irqsave(&dev->lock, flags);
452: dev->state = STATE_DEV_CLOSED;
453: if (!dev->gadget) {
454: spin_unlock_irqrestore(&dev->lock, flags);
455: goto out_put;
456: }
457: if (dev->gadget_registered)
458: unregister = true;
459: dev->gadget_registered = false;
460: spin_unlock_irqrestore(&dev->lock, flags);
461:
462: if (unregister) {
463: ret = usb_gadget_unregister_driver(&dev->driver);
464: if (ret != 0)
465: dev_err(dev->dev,
466: "usb_gadget_unregister_driver() failed with %d\n",
467: ret);
468: /* Matches kref_get() in raw_ioctl_run(). */
469: kref_put(&dev->count, dev_free);
470: }
471:
472: out_put:
473: /* Matches dev_new() in raw_open(). */
474: kref_put(&dev->count, dev_free);
475: return ret;
476: }
477:
478: /*----------------------------------------------------------------------*/
479:
480: static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
481: {
482: int ret = 0;
483: int driver_id_number;
484: struct usb_raw_init arg;
485: char *udc_driver_name;
486: char *udc_device_name;
487: char *driver_driver_name;
488: unsigned long flags;
489:
490: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
491: return -EFAULT;
492:
493: switch (arg.speed) {
]
|
| 70/2 |
2026/07/29 08:11 |
llm |
debugger |
0m
Model:
Tokens:
input: 71002
output: 1114
thoughts: 1031
Thoughts: **Root Cause Analysis of the `raw_gadget` UAF**
Okay, let's break this down. My initial thought process was: in `raw_release`, we check `dev->gadget`. If it's not NULL, we unregister the driver. But wait... if the UDC core calls `gadget_unbind` (say, through sysfs unbinding), then `dev->gadget` *isn't* NULL! So, `raw_release` *will* call `usb_gadget_unregister_driver`. And this, in turn, will try to unregister the driver again, even though it is already unbound! However, now I see, `usb_gadget_unregister_driver` can be safely called even if the UDC is unbound. The real problem isn't in unregistering, it is strictly about the potential use-after-free issue during `ioctl` calls because `dev->gadget` is not cleared in `gadget_unbind`.
Now, I understand the root cause clearly.
Here's the problem in detail:
1. **Driver Initialization and Binding:** The `raw_gadget` driver allows userspace to implement a USB gadget. During the binding process, the `gadget_bind` callback is invoked when the gadget is successfully bound to a UDC (USB Device Controller). This callback stores the `struct usb_gadget` pointer in `dev->gadget` and sets `dev->state` to `STATE_DEV_RUNNING`. This is normal.
2. **Unbinding through UDC Core:** Now, the critical scenario. If the UDC is unbound (e.g., as in the reproducer, via the sysfs `/sys/bus/platform/drivers/dummy_udc/unbind`), the UDC core unbinds the gadget driver, which invokes the `gadget_unbind` callback.
3. **The Flaw in `gadget_unbind`:** This is where the bug lies. The `gadget_unbind` callback in `raw_gadget` *fails* to clear the `dev->gadget` pointer. It also fails to update `dev->state` (for example, to `STATE_DEV_FAILED`). It only clears driver specific data via `set_gadget_data(gadget, NULL)`. This means `dev->gadget` is left dangling, pointing to memory that will soon be invalid.
4. **UDC Core Cleanup:** Immediately after `gadget_unbind` returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device`. This includes freeing the device name string that was allocated with `kvasprintf`.
5. **The Race Condition and UAF:** Because `dev->gadget` isn't cleared and `dev->state` is still `STATE_DEV_RUNNING`, subsequent `ioctl` calls from userspace (like `USB_RAW_IOCTL_EP0_READ` or `USB_RAW_IOCTL_EP0_WRITE`) will, unfortunately, pass the state checks in `raw_process_ep0_io` (or `raw_process_ep_io`).
6. **The Trigger:** The ioctl handler then calls a function like `usb_ep_queue(dev->gadget->ep0, ...)`, passing a pointer to the now-freed `gadget`.
7. **The Crash:** In the crash trace, `usb_ep_queue` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);`.
8. **The UAF:** The `dev_err` function attempts to print the device name (`dev->gadget->dev.kobj.name`), which now points to the freed string, resulting in the KASAN slab-use-after-free.
So, in summary:
* **Bug Summary:** A use-after-free vulnerability is caused by a dangling `dev->gadget` pointer in `raw_gadget` after the UDC is unbound.
* **Sequence of Events:** The UAF occurs because `gadget_unbind` fails to clear `dev->gadget` and update `dev->state`. Subsequently, ioctl calls incorrectly pass state checks and attempt to use the freed pointer.
* **The Problem is `gadget_unbind`:** The key code that is missing important steps is the `gadget_unbind` function. Specifically, it needs to clear `dev->gadget` *and* update the `dev->state` variable.
* **The Fix:**
To correct this, the `
|
| 71/1 |
2026/07/29 08:12 |
agent |
history-explorer |
12m
Model:
Instruction:
You are an experienced Linux kernel developer researching prior art for fixing a kernel bug.
You are given a bug explanation. This explanation details the root cause of the bug resulting
from debugging, but does not provide the final fix strategy. Your goal is to explore how
similar bugs were fixed in the past in the same subsystem or files.
CRITICAL: Do NOT attempt to debug the issue further or write a patch for it yourself.
Your ONLY objective is to research and provide the necessary historical context.
Use the git-log tool with the Since parameter set to "3 years" to focus on recent history.
Search for commits that address issues with similar root causes (e.g. similar missing locks,
incorrect refcounting, or similar error path bugs) in the affected files.
Your final reply must summarize your findings: what idioms, locking rules, or common patterns
should be followed when writing a fix for this bug based on how previous similar bugs were addressed.
If you find no relevant past fixes, clearly state that.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
The explanation of the root cause of the bug is:
The root cause of this crash is a use-after-free (UAF) vulnerability in the `raw_gadget` driver. It occurs because the driver fails to properly clean up its internal state and synchronize concurrent operations when the underlying USB Device Controller (UDC) is unbound.
### Detailed Sequence of Events
1. **Gadget Initialization and Binding:**
The reproducer opens `/dev/raw-gadget` and initializes it. When the gadget binds to the `dummy_udc` controller, the UDC core invokes the `gadget_bind()` callback. This function saves the `struct usb_gadget` pointer into `dev->gadget` and eventually sets `dev->state = STATE_DEV_RUNNING`.
2. **UDC Unbinding:**
The reproducer then writes to `/sys/bus/platform/drivers/dummy_udc/unbind` via sysfs. This forces the UDC core to unbind the gadget driver, which invokes the `gadget_unbind()` callback in `raw_gadget.c`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
Notice that `gadget_unbind()` **fails to clear `dev->gadget`** and **fails to update `dev->state`**. It only clears the driver data. After this function returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device` (including the device name string allocated via `kvasprintf`).
3. **Dangling Pointer Dereference:**
The reproducer then issues a `USB_RAW_IOCTL_EP0_READ` (or `WRITE`) ioctl, which lands in `raw_process_ep0_io()`:
```c
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, void *data, bool in)
{
// ...
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { // Passes, state was never updated
// ...
}
if (!dev->gadget) { // Passes, dev->gadget is a dangling pointer
// ...
}
// ...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);
// ...
```
Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
4. **The Crash:**
Since the UDC is disconnected/freed, `usb_ep_queue()` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, ...)`. The `dev_err()` macro attempts to print the device name (`dev->gadget->dev.kobj.name`), which points to the freed 16-byte string, resulting in the KASAN `slab-use-after-free` crash observed in the trace.
### How to Fix
To fix this bug, `gadget_unbind()` must clear the `dev->gadget` pointer and update `dev->state` (e.g., to `STATE_DEV_FAILED`) while holding `dev->lock`.
However, simply clearing the pointer is not enough due to race conditions. If an ioctl thread has already passed the `!dev->gadget` check and released `dev->lock`, but hasn't yet called `usb_ep_queue()` (which is called lockless), `gadget_unbind()` could return and free the gadget right under the ioctl's feet.
To prevent this race, `raw_gadget` needs a synchronization mechanism to track ongoing UDC calls and wait for them to finish before returning from `gadget_unbind()`. A common pattern for this (as seen in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`) is to introduce a `udc_usage` counter:
- Increment `udc_usage` under `dev->lock` before releasing the lock to call `usb_ep_queue()`.
- Decrement `udc_usage` under `dev->lock` after `usb_ep_queue()` returns.
- In `gadget_unbind()`, wait for `udc_usage` to drop to `0` before returning.
Reply: Based on a review of the kernel history for the USB gadget subsystem (specifically within `drivers/usb/gadget/`), there are several established patterns for fixing use-after-free (UAF) and race conditions during the unbind phase.
Here is a summary of the idioms, locking rules, and common patterns used to address similar bugs:
### 1. The Usage Counter Pattern (Legacy Gadgetfs)
As you noted, `drivers/usb/gadget/legacy/inode.c` (`gadgetfs`) uses a `udc_usage` counter. This was introduced in commit `520b72fc64de` ("USB: gadgetfs: Fix crash caused by inadequate synchronization") to fix a very similar crash.
* **How it works:** The driver increments `udc_usage` under a spinlock before releasing the lock to call lockless UDC functions (like `usb_ep_queue`). It decrements it after re-acquiring the lock.
* **The Wait Mechanism:** In `gadgetfs_unbind()`, it uses a polling loop to wait for the counter to drop to zero:
```c
while (dev->udc_usage > 0) {
spin_unlock_irq(&dev->lock);
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
```
* **Takeaway:** While this is the exact precedent in the legacy gadget directory, polling with `usleep_range()` is generally discouraged in modern kernel development if better primitives are available.
### 2. Explicit Synchronization via Completions (Modern Approach)
More recent fixes in the gadget subsystem prefer using `struct completion` to synchronize unbind routines with ongoing asynchronous operations.
* **Example:** Commit `eba2936bbe6b` ("usb: gadget: uvc: fix NULL pointer dereference during unbind race") in `f_uvc.c` fixed a UAF during unbind by introducing a `completion`.
* **How it works:** The unbind function sets a flag (protected by a mutex) to prevent new operations, and then calls `wait_for_completion()`. The ongoing operation calls `complete()` when it finishes its critical section, allowing the unbind to safely proceed and free the memory.
### 3. Mutex Serialization
If the ongoing operation can safely block, simply serializing the unbind and the ioctl/queue operations with a mutex is a common fix.
* **Example:** Commit `6a19da111057` ("usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait") in `f_fs.c` fixed a UAF where `functionfs_unbind` was freeing the request while `ffs_ep0_write` was using it.
* **How it works:** They wrapped the teardown logic in `functionfs_unbind()` with `mutex_lock(&ffs->mutex)`, ensuring it could not run concurrently with the queueing logic.
### 4. Strict State Machine Enforcement
In `raw_gadget.c` itself, previous race conditions have been fixed by strictly enforcing state transitions.
* **Example:** Commit `5f0b5f4d50fa` ("usb: gadget: fix race when gadget driver register via ioctl") fixed a double-registration race by introducing a `STATE_DEV_REGISTERING` state.
* **Takeaway:** For your bug, updating `dev->state` (e.g., to `STATE_DEV_FAILED` or a new `STATE_DEV_UNBOUND`) inside `gadget_unbind()` while holding `dev->lock` is a mandatory first step. This ensures that any *new* ioctl calls will fail the `dev->state == STATE_DEV_RUNNING` check and bail out early.
### Summary Recommendation for the Fix
To fix this bug in `raw_gadget.c` while adhering to kernel idioms:
1. **State Update:** In `gadget_unbind()`, acquire `dev->lock`, clear `dev->gadget`, and set `dev->state = STATE_DEV_FAILED` (or similar).
2. **Synchronization:** To protect *already running* ioctls that have passed the state check, introduce a tracking mechanism.
* You can use the `udc_usage` counter approach for consistency with `gadgetfs`.
* However, instead of a `usleep_range()` polling loop, it is much more idiomatic to use a `wait_queue_head_t` (e.g., `wait_event_lock_irq()`) or a `completion` to put the `gadget_unbind()` thread to sleep until the last ongoing `usb_ep_queue()` finishes and wakes it up.
|
| 72/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 9715
output: 38
thoughts: 195
Thoughts: **Kernel Bug Fix Exploration**
Okay, so we've got a tricky one: a use-after-free in `raw_gadget.c`, or potentially related USB gadget drivers. It's triggered when unbinding the UDC while an ioctl is actively running. The proposed solution is to implement a `udc_usage` counter β a classic synchronization technique to make sure `gadget_unbind()` doesn't get ahead of any UDC calls, specifically ones like `usb_ep_queue()`.
My immediate thought is, let's see if we can find some prior art. Before we reinvent the wheel, I want to see if something similar has been addressed in the past few years, say the last three. I'll need to use `git-log` and narrow down the search in a couple of ways.
First, I need to focus on relevant files, so I'll start by searching in `drivers/usb/gadget/legacy/` and specifically, `raw_gadget.c`. Second, the real clue should come from the `udc_usage` counter implementation itself. I should search for `udc_usage` within `drivers/usb/gadget/legacy/inode.c`, so I can see the existing implementation. It's likely that a similar counter has been used in some other gadget driver code in the past, and I can learn from those implementations.
|
| 73/2 |
2026/07/29 08:12 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c Since:3 years]
Results: map[Output:bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
a5160af78be7 usb: raw-gadget: cap raw_io transfer length to KMALLOC_MAX_SIZE
37b9dd0d114a usb: raw-gadget: do not limit transfer length
b7d49096d595 drivers/usb/gadget: refactor min with min_t
cb787f4ac0c2 [tree-wide] finally take no_llseek out
c3a383d8d382 usb: raw-gadget: report suspend, resume, reset, and disconnect events
1f97e3f429cf usb: raw-gadget: don't disable device if usb_ep_queue fails
e8033bde451e usb: raw-gadget: properly handle interrupted requests
cf9f7a6ee7b1 usb: raw-gadget: return USB_GADGET_DELAYED_STATUS from setup()
]
|
| 74/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 10537
output: 36
thoughts: 85
Thoughts: Okay, let's dive into this. Here's how I'd approach this, thinking out loud:
**Hunting for USB Gadget UDC Unbind Shenanigans**
Alright, I'm trying to track down potential issues related to UDC unbinding and synchronization, specifically within the USB gadget driver code. My first instinct is to poke around in `drivers/usb/gadget/legacy/raw_gadget.c` since that seems like a likely spot for lower-level handling. I'll scan the commit history. I'm not expecting anything screaming "UAF DETECTED!" in the titles, but I'm looking for anything that touches UDC interaction or synchronization that *could* be related. It's a bit of a fishing expedition, so I'll be looking for relevant keywords.
Since `raw_gadget.c` seems to have nothing, it makes sense to look in `drivers/usb/gadget/legacy/inode.c`, as `inode.c` might provide some more context. I'll search for how `udc_usage` is handled there, seeing if I can tease out any synchronization strategies related to UDC access or resource management. Hopefully that will give me a better understanding of how the unbinding process *should* work and where things might be going sideways.
|
| 75/2 |
2026/07/29 08:12 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/usb/gadget/legacy/inode.c Since:10 years]
Results: map[Output:bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
57db9d428ba5 convert gadgetfs
b65e3b11e923 gadgetfs: switch to simple_remove_by_name()
f99b3917789d fs: rename generic_delete_inode() and generic_drop_inode()
bad356bb50e6 functionfs, gadgetfs: use simple_recursive_removal()
bd3cf1a9396e USB: gadget: Replace deprecated strncpy() with strscpy()
5b6dc50e9ed8 USB: gadget: Use str_enable_disable-like helpers
cb787f4ac0c2 [tree-wide] finally take no_llseek out
69f73ca33d6b legacy: convert to new timestamp accessors
]
|
| 76/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 11192
output: 33
thoughts: 35
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Investigating a USB Gadget Driver Issue**
Right, so I'm dealing with something related to the USB gadget drivers, and I have a feeling it has to do with usage tracking. The term "udc_usage" just popped into my head, which is pretty specific. My instinct is that the problem probably lies somewhere in the legacy inode code. So, the first thing I'm going to do is get my hands dirty. I'll need to pinpoint where exactly that variable or function is defined or used within the Linux kernel source. I'll use either `grepper` or `git-log` to search for `udc_usage` within `drivers/usb/gadget/legacy/inode.c`. That should give me a quick understanding of where it's being used and any potential context which I can use to drill down on the specific issue I'm debugging. Getting the usage details will be important to understand the overall architecture of this code block. From there, I can determine the most efficient path towards understanding and fixing the root cause.
|
| 77/2 |
2026/07/29 08:12 |
tool |
grepper |
0m
Args: map[Expression:udc_usage PathPrefix:drivers/usb/gadget/legacy/]
Results: map[Output:drivers/usb/gadget/legacy/inode.c=122=struct dev_data {
--
drivers/usb/gadget/legacy/inode.c-124- refcount_t count;
drivers/usb/gadget/legacy/inode.c:125: int udc_usage;
drivers/usb/gadget/legacy/inode.c-126- enum ep0_state state; /* P: lock */
--
drivers/usb/gadget/legacy/inode.c=913=ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-945- if ((retval = setup_req (ep, req, 0)) == 0) {
drivers/usb/gadget/legacy/inode.c:946: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-947- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-949- spin_lock_irq (&dev->lock);
drivers/usb/gadget/legacy/inode.c:950: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-951- }
--
drivers/usb/gadget/legacy/inode.c-991- len = min (len, (size_t)dev->req->actual);
drivers/usb/gadget/legacy/inode.c:992: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-993- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-998- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:999: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1000- clean_req (dev->gadget->ep0, dev->req);
--
drivers/usb/gadget/legacy/inode.c=1125=ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-1142- dev->state = STATE_DEV_CONNECTED;
drivers/usb/gadget/legacy/inode.c:1143: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1144- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1154- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1155: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1156- if (retval < 0) {
--
drivers/usb/gadget/legacy/inode.c=1251=static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
--
drivers/usb/gadget/legacy/inode.c-1261- } else if (gadget->ops->ioctl) {
drivers/usb/gadget/legacy/inode.c:1262: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1263- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1267- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1268: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1269- }
--
drivers/usb/gadget/legacy/inode.c=1333=gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
--
drivers/usb/gadget/legacy/inode.c-1497-
drivers/usb/gadget/legacy/inode.c:1498: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1499- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1502- spin_lock (&dev->lock);
drivers/usb/gadget/legacy/inode.c:1503: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1504- if (value < 0) {
--
drivers/usb/gadget/legacy/inode.c-1535-
drivers/usb/gadget/legacy/inode.c:1536: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1537- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1539- spin_lock(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1540: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1541- spin_unlock(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c=1640=gadgetfs_unbind (struct usb_gadget *gadget)
--
drivers/usb/gadget/legacy/inode.c-1647- dev->state = STATE_DEV_UNBOUND;
drivers/usb/gadget/legacy/inode.c:1648: while (dev->udc_usage > 0) {
drivers/usb/gadget/legacy/inode.c-1649- spin_unlock_irq(&dev->lock);
]
|
| 78/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 12989
output: 41
thoughts: 39
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**GadgetFS Unbind and Resource Management**
Right, so `gadgetfs_unbind` leverages `udc_usage` to ensure that any active operations on the gadget filesystem are completed before unbinding. I'm taking a moment here to dig into the exact mechanism `gadgetfs_unbind` employs for this waiting process. I need to refresh my understanding of the timing. Specifically, I need to look at the synchronization primitive used and how long it's safe to wait. I'm probably looking at a reference count, some sort of semaphore, or maybe a simple spinlock, but I need to be sure. I'm also thinking about potential deadlock scenarios; this is a place where that kind of bug could be really nasty. I want to make sure the waiting doesn't become a bottleneck during unbinding, especially if there's a heavy load on the device.
|
| 79/2 |
2026/07/29 08:12 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/inode.c FirstLine:1640 LineCount:20]
Results: map[Contents:1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
]
|
| 80/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 13608
output: 31
thoughts: 146
Thoughts: **Analyzing the USB Gadget Race Condition**
Okay, so the bug report highlights a race condition, specifically mentioning that `raw_gadget` is vulnerable. The core of the problem is that `gadget_unbind()` might be called while a UDC (USB Device Controller) call is in progress. The suggested solution is to implement a synchronization mechanism within `raw_gadget` to manage these concurrent operations. The report points to using a `udc_usage` counter, drawing a parallel to the `gadgetfs` implementation in `drivers/usb/gadget/legacy/inode.c`.
My immediate next step is to perform a thorough investigation. I need to determine if this pattern, or a similar fix, is already employed elsewhere within the kernel, or more specifically, within the USB gadget driver implementations. I'm going to search the entire kernel codebase, or at least the `drivers/usb/gadget/` directory, for instances of `udc_usage`. This search will help me understand if this is a standard practice, and if so, how itβs implemented and managed in other contexts. This research can quickly determine if `raw_gadget.c` has any current implementation, or if the best-practice is present elsewhere, or if any similar fixes have been implemented in `raw_gadget.c` or other gadget drivers.
|
| 81/2 |
2026/07/29 08:12 |
tool |
grepper |
0m
Args: map[Expression:udc_usage PathPrefix:drivers/usb/gadget/]
Results: map[Output:drivers/usb/gadget/legacy/inode.c=122=struct dev_data {
--
drivers/usb/gadget/legacy/inode.c-124- refcount_t count;
drivers/usb/gadget/legacy/inode.c:125: int udc_usage;
drivers/usb/gadget/legacy/inode.c-126- enum ep0_state state; /* P: lock */
--
drivers/usb/gadget/legacy/inode.c=913=ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-945- if ((retval = setup_req (ep, req, 0)) == 0) {
drivers/usb/gadget/legacy/inode.c:946: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-947- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-949- spin_lock_irq (&dev->lock);
drivers/usb/gadget/legacy/inode.c:950: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-951- }
--
drivers/usb/gadget/legacy/inode.c-991- len = min (len, (size_t)dev->req->actual);
drivers/usb/gadget/legacy/inode.c:992: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-993- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-998- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:999: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1000- clean_req (dev->gadget->ep0, dev->req);
--
drivers/usb/gadget/legacy/inode.c=1125=ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-1142- dev->state = STATE_DEV_CONNECTED;
drivers/usb/gadget/legacy/inode.c:1143: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1144- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1154- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1155: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1156- if (retval < 0) {
--
drivers/usb/gadget/legacy/inode.c=1251=static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
--
drivers/usb/gadget/legacy/inode.c-1261- } else if (gadget->ops->ioctl) {
drivers/usb/gadget/legacy/inode.c:1262: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1263- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1267- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1268: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1269- }
--
drivers/usb/gadget/legacy/inode.c=1333=gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
--
drivers/usb/gadget/legacy/inode.c-1497-
drivers/usb/gadget/legacy/inode.c:1498: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1499- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1502- spin_lock (&dev->lock);
drivers/usb/gadget/legacy/inode.c:1503: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1504- if (value < 0) {
--
drivers/usb/gadget/legacy/inode.c-1535-
drivers/usb/gadget/legacy/inode.c:1536: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1537- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1539- spin_lock(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1540: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1541- spin_unlock(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c=1640=gadgetfs_unbind (struct usb_gadget *gadget)
--
drivers/usb/gadget/legacy/inode.c-1647- dev->state = STATE_DEV_UNBOUND;
drivers/usb/gadget/legacy/inode.c:1648: while (dev->udc_usage > 0) {
drivers/usb/gadget/legacy/inode.c-1649- spin_unlock_irq(&dev->lock);
]
|
| 82/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 15556
output: 46
thoughts: 258
Thoughts: **Investigating Potential Use-After-Free Vulnerabilities in USB Gadget Drivers**
Okay, so the `gadgetfs` driver leverages `udc_usage`, which is interesting. My initial thought is to see if any other drivers, particularly within the USB gadget subsystem, employ similar mechanisms to address potential issues. Let's start with `raw_gadget`. I need to determine if any commits in `raw_gadget.c` or related files have introduced `udc_usage` or anything comparable.
Wait a minute, the bug report *specifically* mentions `raw_gadget.c` and details a fix involving clearing the `dev->gadget` pointer and updating `dev->state` during `gadget_unbind()`. It also references the `udc_usage` counter, used in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`, as a common pattern to resolve such issues. Therefore, it seems pretty clear that `raw_gadget` is *currently* missing this critical piece. The bug report is, in effect, a request for me to research how this class of similar bugs, UAFs, have been fixed in this same subsystem or specific files.
So, the focus of my research needs to be on identifying and assessing relevant past fixes to this exact problem in `raw_gadget.c` and potentially other gadget drivers. I'll need to dig through the commit history. I'll start by searching the `drivers/usb/gadget/` directory for commits that mention "unbind" along with keywords like "use-after-free" or "UAF". This should surface instances where similar vulnerabilities were addressed, potentially offering insights into how to best address this specific problem in `raw_gadget`.
|
| 83/2 |
2026/07/29 08:12 |
tool |
git-log |
0m
Args: map[MessageRegexps:[unbind use-after-free] PathPrefix:drivers/usb/gadget/ Since:5 years]
Results: map[Output:5650c18d93a1 usb: gadget: f_midi: cancel pending IN work before freeing the midi object
eba2936bbe6b usb: gadget: uvc: fix NULL pointer dereference during unbind race
0d6c8144ca4d usb: gadget: f_ncm: Fix atomic context locking issue
56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
6334b8e4553c usb: gadget: f_ncm: Fix UAF ncm object at re-bind after usb ep transport error
6a19da111057 usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait
d65e6b6e884a usb: gadget: f_ecm: Always set current gadget in ecm_bind()
1016fc0c096c USB: gadget: Fix obscure lockdep violation for udc_mutex
]
|
| 84/2 |
2026/07/29 08:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 16486
output: 26
thoughts: 38
Thoughts: Here's a summary of my thinking about that commit:
**Analyzing a Critical USB Gadget Driver Fix**
Okay, so I'm looking at commit `6a19da111057` in the USB gadget subsystem, specifically dealing with the `f_fs` function. The title itself gives me a good starting point - it's about preventing a race condition within the `ffs_ep0_queue_wait` function. That immediately grabs my attention. Races in endpoint handling are nasty; they lead to unpredictable behavior, data corruption, and the potential for a complete system crash, particularly in real-time embedded systems. My initial hypothesis, based on the title, is that this race likely involves interactions between the host's requests and the gadget's responses on endpoint 0 (EP0), the control endpoint. I'd expect the race might be occurring at the boundary between request queuing and the interrupt context. I'm going to dig into the code and see exactly where and how this race could manifest. I'll be looking for shared resources, conditional access, and any potential issues with atomic operations or locking mechanisms. I'm particularly interested in how the `ffs_ep0_queue_wait` function handles interrupts, how it communicates with the underlying hardware, and if it's properly synchronized in a multi-threaded or interrupt-driven environment. My focus will be on the logic around request queuing, completion, and any waiting mechanisms utilized.
|
| 85/2 |
2026/07/29 08:13 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 6a19da111057f69214b97c62fb0ac59023970850
Author: Udipto Goswami <quic_ugoswami@quicinc.com>
Date: Thu Dec 15 10:59:05 2022 +0530
usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait
While performing fast composition switch, there is a possibility that the
process of ffs_ep0_write/ffs_ep0_read get into a race condition
due to ep0req being freed up from functionfs_unbind.
Consider the scenario that the ffs_ep0_write calls the ffs_ep0_queue_wait
by taking a lock &ffs->ev.waitq.lock. However, the functionfs_unbind isn't
bounded so it can go ahead and mark the ep0req to NULL, and since there
is no NULL check in ffs_ep0_queue_wait we will end up in use-after-free.
Fix this by making a serialized execution between the two functions using
a mutex_lock(ffs->mutex).
Fixes: ddf8abd25994 ("USB: f_fs: the FunctionFS driver")
Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
Tested-by: Krishna Kurapati <quic_kriskura@quicinc.com>
Link: https://lore.kernel.org/r/20221215052906.8993-2-quic_ugoswami@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 73dc10a77cde..bb6fe1f3a29d 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -279,6 +279,9 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
struct usb_request *req = ffs->ep0req;
int ret;
+ if (!req)
+ return -EINVAL;
+
req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -1892,10 +1895,12 @@ static void functionfs_unbind(struct ffs_data *ffs)
ENTER();
if (!WARN_ON(!ffs->gadget)) {
+ mutex_lock(&ffs->mutex);
usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
ffs->ep0req = NULL;
ffs->gadget = NULL;
clear_bit(FFS_FL_BOUND, &ffs->flags);
+ mutex_unlock(&ffs->mutex);
ffs_data_put(ffs);
}
}
]
|
| 86/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 17729
output: 47
thoughts: 47
Thoughts: Okay, here's my take on those thoughts, framed as my own internal process:
**Analyzing a Potential Fix for USB Gadget Driver Issues**
Right, so, the core issue is the unbind operation clashing with the `ep0` queue wait. My initial thought is using a mutex to serialize those two operations, effectively protecting the critical section. That seems like a straightforward and likely effective solution.
Now, before I jump to a final conclusion, I need to do a thorough review. I need to dig into `raw_gadget.c` more deeply. It's highly probable that other commits within that file may address similar concurrency problems or related race conditions. Understanding the broader context of what's been addressed previously is crucial before committing to a specific fix. I'll need to meticulously examine the commit history for relevant patches and see if this aligns with past resolutions or suggests a more elegant/general approach.
|
| 87/2 |
2026/07/29 08:13 |
tool |
git-log |
0m
Args: map[MessageRegexps:[unbind] PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c Since:10 years]
Results:
|
| 88/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18027
output: 51
thoughts: 42
Thoughts: Okay, here's my summary, considering my expertise:
**Diving into the `raw_gadget.c` Analysis**
Alright, so I need to understand what's going on in this `raw_gadget.c` file. The first thing that jumps to my mind is to see if there are any mentions of "unbind" in the commit history, given the context. But, no luck there. It appears that specific vulnerability is not explicitly mentioned with that keyword.
Now, my experience tells me that if "unbind" isn't a direct hit, then I need to broaden my search. "Use-after-free" or "UAF" is a classic vulnerability class that can often manifest in similar ways. I need to see if any instances of a UAF might exist in the code, so let me pivot and see if those terms yield any results. This will help get me to the heart of the security concerns.
|
| 89/2 |
2026/07/29 08:13 |
tool |
git-log |
0m
Args: map[MessageRegexps:[use-after-free] PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c Since:10 years]
Results:
|
| 90/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18330
output: 47
thoughts: 37
Thoughts: **Analyzing `raw_gadget.c` for Vulnerabilities**
Okay, so I'm diving into `raw_gadget.c`. First things first, let's establish a baseline. No commits in this file explicitly reference "use-after-free." That's a good starting point, but it doesn't mean we're in the clear. Now, with a fresh perspective, I'm going to shift my focus. Let's run a search for the term "race" within this file. My expertise tells me that race conditions can often lead to subtle, exploitable bugs, even if explicit "use-after-free" mentions are absent. I'm looking for concurrent access to shared resources, and I want to identify any potential timing vulnerabilities.
|
| 91/2 |
2026/07/29 08:13 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race] PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c Since:10 years]
Results: map[Output:83e30f2bf86e USB: gadget: Fix the memory leak in raw_gadget driver
f2d8c2606825 usb: gadget: Fix non-unique driver names in raw-gadget driver
5f0b5f4d50fa usb: gadget: fix race when gadget driver register via ioctl
129aa9734559 usb: raw-gadget: fix memory leak in gadget_setup
]
|
| 92/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 18695
output: 26
thoughts: 31
Thoughts: Okay, here's my interpretation of that commit message, expressed as my internal thought process:
**Diving into that USB Gadget Driver Fix**
Alright, let's take a look at commit `5f0b5f4d50fa usb: gadget: fix race when gadget driver register via ioctl`. Hmm, "race condition" always raises my eyebrows. Gadget drivers, registering via ioctl... that's a classic area where you can stumble over concurrency issues. So, the kernel folks spotted a potential problem with a gadget driver registering itself and I'm here to unpack what the fix was all about. I need to understand the exact sequence, the shared resources involved, and *how* the race manifested to see if it makes sense in the general architecture. I will evaluate the fix to check for edge cases. I'll need to think through possible concurrent scenarios. I'm imagining a device driver, likely interacting with user space via `ioctl`. Was it locking wrong? Over-releasing? Or was the problem somewhere deeper, like with the underlying USB core's registration mechanisms? I'm curious what the patch did. Likely, the fix involves a careful application of locks, or possibly a re-ordering of operations, or perhaps it uses a different synchronization primitive altogether. I'll need to see the details to confirm.
|
| 93/2 |
2026/07/29 08:13 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 5f0b5f4d50fa0faa8c76ef9d42a42e8d43f98b44
Author: Schspa Shi <schspa@gmail.com>
Date: Sun May 8 23:02:47 2022 +0800
usb: gadget: fix race when gadget driver register via ioctl
The usb_gadget_register_driver can be called multi time by to
threads via USB_RAW_IOCTL_RUN ioctl syscall, which will lead
to multiple registrations.
Call trace:
driver_register+0x220/0x3a0 drivers/base/driver.c:171
usb_gadget_register_driver_owner+0xfb/0x1e0
drivers/usb/gadget/udc/core.c:1546
raw_ioctl_run drivers/usb/gadget/legacy/raw_gadget.c:513 [inline]
raw_ioctl+0x1883/0x2730 drivers/usb/gadget/legacy/raw_gadget.c:1220
ioctl USB_RAW_IOCTL_RUN
This routine allows two processes to register the same driver instance
via ioctl syscall. which lead to a race condition.
Please refer to the following scenarios.
T1 T2
------------------------------------------------------------------
usb_gadget_register_driver_owner
driver_register driver_register
driver_find driver_find
bus_add_driver bus_add_driver
priv alloced <context switch>
drv->p = priv;
<schedule out>
kobject_init_and_add // refcount = 1;
//couldn't find an available UDC or it's busy
<context switch>
priv alloced
drv->priv = priv;
kobject_init_and_add
---> refcount = 1 <------
// register success
<context switch>
===================== another ioctl/process ======================
driver_register
driver_find
k = kset_find_obj()
---> refcount = 2 <------
<context out>
driver_unregister
// drv->p become T2's priv
---> refcount = 1 <------
<context switch>
kobject_put(k)
---> refcount = 0 <------
return priv->driver;
--------UAF here----------
There will be UAF in this scenario.
We can fix it by adding a new STATE_DEV_REGISTERING device state to
avoid double register.
Reported-by: syzbot+dc7c3ca638e773db07f6@syzkaller.appspotmail.com
Link: https://lore.kernel.org/all/000000000000e66c2805de55b15a@google.com/
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
Signed-off-by: Schspa Shi <schspa@gmail.com>
Link: https://lore.kernel.org/r/20220508150247.38204-1-schspa@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 8d40a1f2ec57..e9440f7bf019 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -145,6 +145,7 @@ enum dev_state {
STATE_DEV_INVALID = 0,
STATE_DEV_OPENED,
STATE_DEV_INITIALIZED,
+ STATE_DEV_REGISTERING,
STATE_DEV_RUNNING,
STATE_DEV_CLOSED,
STATE_DEV_FAILED
@@ -508,6 +509,7 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
ret = -EINVAL;
goto out_unlock;
}
+ dev->state = STATE_DEV_REGISTERING;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_gadget_probe_driver(&dev->driver);
]
|
| 94/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20216
output: 43
thoughts: 58
Thoughts: **Analyzing the Unbinding Process**
Okay, so this commit addresses a registration race condition by introducing a new state. That's good, but it doesn't solve *my* current problem, which is specifically related to the unbinding process. I need to understand what's happening there. Let's dig into `drivers/usb/gadget/legacy/raw_gadget.c` and specifically examine the `gadget_unbind` function. I want to see how this function is implemented and identify any potential areas for a race or corruption. I'll need to trace the execution path and identify any critical sections or locking mechanisms that could be causing my issue during unbinding.
|
| 95/2 |
2026/07/29 08:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:gadget_unbind]
Results: map[SourceCode: 328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 96/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 20626
output: 31
thoughts: 403
Thoughts: **Analyzing the Bug Fix & Exploring Similar Solutions**
Okay, so I've got this bug report detailing a race condition when unbinding a USB gadget. The core issue is that `gadget_unbind()` needs to clean up the gadget's resources, but if it's too hasty, it can free things out from under other threads β specifically those handling ioctls and queuing USB endpoints. The suggested solution involves introducing a synchronization mechanism, like a `udc_usage` counter, to track ongoing UDC calls and ensure they're finished before `gadget_unbind()` frees the resources. Makes sense. This is a classic concurrency problem.
My task now is to see how similar issues have been tackled in the past, ideally within this same USB gadget subsystem. The user wants me to identify common idioms, locking rules, or patterns that have emerged from previous fixes.
Let's start by searching for similar synchronization techniques. Since `udc_usage` was explicitly mentioned in the bug report and we know it's used in `drivers/usb/gadget/legacy/inode.c`, I'll look for that first, focusing on the `drivers/usb/gadget/` and `drivers/usb/gadget/legacy/` directories. I'll need to expand that search beyond just the exact counter name; I'll want to see if there are other, related mechanisms for managing concurrent access to these USB gadget resources. I'll need to also look for commits addressing issues with similar root causes - missing locks, incorrect refcounting or error path bugs.
|
| 97/2 |
2026/07/29 08:13 |
tool |
grepper |
0m
Args: map[Expression:udc_usage PathPrefix:drivers/usb/gadget/]
Results: map[Output:drivers/usb/gadget/legacy/inode.c=122=struct dev_data {
--
drivers/usb/gadget/legacy/inode.c-124- refcount_t count;
drivers/usb/gadget/legacy/inode.c:125: int udc_usage;
drivers/usb/gadget/legacy/inode.c-126- enum ep0_state state; /* P: lock */
--
drivers/usb/gadget/legacy/inode.c=913=ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-945- if ((retval = setup_req (ep, req, 0)) == 0) {
drivers/usb/gadget/legacy/inode.c:946: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-947- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-949- spin_lock_irq (&dev->lock);
drivers/usb/gadget/legacy/inode.c:950: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-951- }
--
drivers/usb/gadget/legacy/inode.c-991- len = min (len, (size_t)dev->req->actual);
drivers/usb/gadget/legacy/inode.c:992: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-993- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-998- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:999: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1000- clean_req (dev->gadget->ep0, dev->req);
--
drivers/usb/gadget/legacy/inode.c=1125=ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-1142- dev->state = STATE_DEV_CONNECTED;
drivers/usb/gadget/legacy/inode.c:1143: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1144- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1154- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1155: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1156- if (retval < 0) {
--
drivers/usb/gadget/legacy/inode.c=1251=static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
--
drivers/usb/gadget/legacy/inode.c-1261- } else if (gadget->ops->ioctl) {
drivers/usb/gadget/legacy/inode.c:1262: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1263- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1267- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1268: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1269- }
--
drivers/usb/gadget/legacy/inode.c=1333=gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
--
drivers/usb/gadget/legacy/inode.c-1497-
drivers/usb/gadget/legacy/inode.c:1498: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1499- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1502- spin_lock (&dev->lock);
drivers/usb/gadget/legacy/inode.c:1503: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1504- if (value < 0) {
--
drivers/usb/gadget/legacy/inode.c-1535-
drivers/usb/gadget/legacy/inode.c:1536: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1537- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1539- spin_lock(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1540: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1541- spin_unlock(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c=1640=gadgetfs_unbind (struct usb_gadget *gadget)
--
drivers/usb/gadget/legacy/inode.c-1647- dev->state = STATE_DEV_UNBOUND;
drivers/usb/gadget/legacy/inode.c:1648: while (dev->udc_usage > 0) {
drivers/usb/gadget/legacy/inode.c-1649- spin_unlock_irq(&dev->lock);
]
|
| 98/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 22875
output: 42
thoughts: 126
Thoughts: **Analyzing Potential Synchronization Issues**
Okay, so I see that `inode.c` is the only file using `udc_usage`. Interesting, but that doesn't tell the whole story. I need to consider if other synchronization mechanisms are at play in related code. I vaguely remember seeing a mutex in `drivers/usb/gadget/function/f_fs.c`, so I'll check that next. Hang on, the prompt is guiding me here... it wants me to look for broader patterns. It's asking about similar root causes like missing locks or incorrect refcounting, or related error path bugs, in general. Alright, so I should be looking at the bigger picture and not getting too hyper-focused on `udc_usage` right now. Given what it suggests, it's probably best to broaden my search to catch related issues. Let me try searching for something like "use-after-free" or "UAF" within the `drivers/usb/gadget/` directory over the past three years. That should cover a reasonable timeframe for these types of bugs and a focused scope.
|
| 99/2 |
2026/07/29 08:13 |
tool |
git-log |
0m
Args: map[MessageRegexps:[use-after-free] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:5650c18d93a1 usb: gadget: f_midi: cancel pending IN work before freeing the midi object
0583f2fbf8f8 usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown
79e2d75725c8 usb: gadget: f_tcm: synchronize delayed set_alt with teardown
67e511d2989e usb: gadget: udc: Fix use-after-free in gadget_match_driver
30adce93d5c4 usb: gadget: f_printer: take kref only for successful open
2796646f6d89 usb: gadget: f_fs: serialize DMABUF cancel against request completion
eba2936bbe6b usb: gadget: uvc: fix NULL pointer dereference during unbind race
0d6c8144ca4d usb: gadget: f_ncm: Fix atomic context locking issue
56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
baeb66fbd420 usb: gadget: udc: fix use-after-free in usb_gadget_state_work
]
|
| 100/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 23584
output: 23
thoughts: 31
Thoughts: Okay, here's my interpretation of that commit message, from a first-person expert perspective:
**Analyzing a USB Gadget UVC Bug Fix**
Alright, let's dive into commit `eba2936bbe6b usb: gadget: uvc: fix NULL pointer dereference during unbind race`. This looks like a potentially nasty one. "NULL pointer dereference" immediately sets off alarm bells. That's a classic sign of a race condition, and in the USB gadget UVC space, that often points to issues with device unbinding while data is being handled. I can already picture the scenario: the device is unplugged or the driver is unloaded at the worst possible moment.
The phrase "during unbind race" confirms my initial suspicion. Someone probably had a race condition where the USB controller's internal data was getting freed out from under UVC functions, or perhaps the UVC driver itself was being unregistered too hastily. This is the kind of bug that can manifest as a hard crash or, even worse, as inconsistent behavior that's difficult to reproduce and track down. I'd expect to find an improperly synchronized access to a data structure, likely some kind of buffer or control structure related to the UVC stream. I will probably look for uses of atomic operations, mutexes, or other concurrency primitives to try and identify how that might relate to the issue. I am interested to see how the code was changed and the context of the change. This type of bug can be very subtle in its expression.
|
| 101/2 |
2026/07/29 08:13 |
tool |
git-show |
0m
Args:
Results: map[Output:commit eba2936bbe6b752a31725a9eb5c674ecbf21ee7d
Author: Jimmy Hu <hhhuuu@google.com>
Date: Fri Mar 20 14:54:27 2026 +0800
usb: gadget: uvc: fix NULL pointer dereference during unbind race
Commit b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly
shutdown") introduced two stages of synchronization waits totaling 1500ms
in uvc_function_unbind() to prevent several types of kernel panics.
However, this timing-based approach is insufficient during power
management (PM) transitions.
When the PM subsystem starts freezing user space processes, the
wait_event_interruptible_timeout() is aborted early, which allows the
unbind thread to proceed and nullify the gadget pointer
(cdev->gadget = NULL):
[ 814.123447][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind()
[ 814.178583][ T3173] PM: suspend entry (deep)
[ 814.192487][ T3173] Freezing user space processes
[ 814.197668][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind no clean disconnect, wait for release
When the PM subsystem resumes or aborts the suspend and tasks are
restarted, the V4L2 release path is executed and attempts to access the
already nullified gadget pointer, triggering a kernel panic:
[ 814.292597][ C0] PM: pm_system_irq_wakeup: 479 triggered dhdpcie_host_wake
[ 814.386727][ T3173] Restarting tasks ...
[ 814.403522][ T4558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030
[ 814.404021][ T4558] pc : usb_gadget_deactivate+0x14/0xf4
[ 814.404031][ T4558] lr : usb_function_deactivate+0x54/0x94
[ 814.404078][ T4558] Call trace:
[ 814.404080][ T4558] usb_gadget_deactivate+0x14/0xf4
[ 814.404083][ T4558] usb_function_deactivate+0x54/0x94
[ 814.404087][ T4558] uvc_function_disconnect+0x1c/0x5c
[ 814.404092][ T4558] uvc_v4l2_release+0x44/0xac
[ 814.404095][ T4558] v4l2_release+0xcc/0x130
Address the race condition and NULL pointer dereference by:
1. State Synchronization (flag + mutex)
Introduce a 'func_unbound' flag in struct uvc_device. This allows
uvc_function_disconnect() to safely skip accessing the nullified
cdev->gadget pointer. As suggested by Alan Stern, this flag is protected
by a new mutex (uvc->lock) to ensure proper memory ordering and prevent
instruction reordering or speculative loads. This mutex is also used to
protect 'func_connected' for consistent state management.
2. Explicit Synchronization (completion)
Use a completion to synchronize uvc_function_unbind() with the
uvc_vdev_release() callback. This prevents Use-After-Free (UAF) by
ensuring struct uvc_device is freed after all video device resources
are released.
Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown")
Cc: stable <stable@kernel.org>
Suggested-by: Alan Stern <stern@rowland.harvard.edu>
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Jimmy Hu <hhhuuu@google.com>
Link: https://patch.msgid.link/20260320065427.1374555-1-hhhuuu@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 494fdbc4e85b..8d404d88391c 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -413,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc)
{
int ret;
+ guard(mutex)(&uvc->lock);
+ if (uvc->func_unbound) {
+ dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n");
+ return;
+ }
+
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
}
@@ -431,6 +437,15 @@ static ssize_t function_name_show(struct device *dev,
static DEVICE_ATTR_RO(function_name);
+static void uvc_vdev_release(struct video_device *vdev)
+{
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+
+ /* Signal uvc_function_unbind() that the video device has been released */
+ if (uvc->vdev_release_done)
+ complete(uvc->vdev_release_done);
+}
+
static int
uvc_register_video(struct uvc_device *uvc)
{
@@ -443,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc)
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops;
uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
- uvc->vdev.release = video_device_release_empty;
+ uvc->vdev.release = uvc_vdev_release;
uvc->vdev.vfl_dir = VFL_DIR_TX;
uvc->vdev.lock = &uvc->video.mutex;
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
@@ -659,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
int ret = -EINVAL;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_unbound = false;
opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters. */
@@ -988,12 +1005,19 @@ static void uvc_free(struct usb_function *f)
static void uvc_function_unbind(struct usb_configuration *c,
struct usb_function *f)
{
+ DECLARE_COMPLETION_ONSTACK(vdev_release_done);
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
struct uvc_video *video = &uvc->video;
long wait_ret = 1;
+ bool connected;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock) {
+ uvc->func_unbound = true;
+ uvc->vdev_release_done = &vdev_release_done;
+ connected = uvc->func_connected;
+ }
kthread_cancel_work_sync(&video->hw_submit);
@@ -1006,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c,
* though the video device removal uevent. Allow some time for the
* application to close out before things get deleted.
*/
- if (uvc->func_connected) {
+ if (connected) {
uvcg_dbg(f, "waiting for clean disconnect\n");
wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
uvc->func_connected == false, msecs_to_jiffies(500));
@@ -1017,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
video_unregister_device(&uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
- if (uvc->func_connected) {
+ scoped_guard(mutex, &uvc->lock)
+ connected = uvc->func_connected;
+
+ if (connected) {
/*
* Wait for the release to occur to ensure there are no longer any
* pending operations that may cause panics when resources are cleaned
@@ -1029,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
}
+ /* Wait for the video device to be released */
+ wait_for_completion(&vdev_release_done);
+ uvc->vdev_release_done = NULL;
+
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
@@ -1047,6 +1078,8 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
mutex_init(&uvc->video.mutex);
+ mutex_init(&uvc->lock);
+ uvc->func_unbound = true;
uvc->state = UVC_STATE_DISCONNECTED;
init_waitqueue_head(&uvc->func_connected_queue);
opts = fi_to_f_uvc_opts(fi);
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 676419a04976..7abfdd5e1eef 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -155,6 +155,9 @@ struct uvc_device {
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
+ struct completion *vdev_release_done;
+ struct mutex lock; /* protects func_unbound and func_connected */
+ bool func_unbound;
bool func_connected;
wait_queue_head_t func_connected_queue;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ed48d38498fb..514e5930b9ca 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
+ guard(mutex)(&uvc->lock);
+
if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
return -EBUSY;
@@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc)
uvc_function_disconnect(uvc);
uvcg_video_disable(&uvc->video);
uvcg_free_buffers(&uvc->video.queue);
- uvc->func_connected = false;
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_connected = false;
wake_up_interruptible(&uvc->func_connected_queue);
}
]
|
| 102/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 27443
output: 27
thoughts: 91
Thoughts: Here's my analysis of this code change:
**Analyzing Video Device Release and State Protection**
Okay, so this commit utilizes a `completion` mechanism, which tells me it's likely a lightweight synchronization primitive, a way to signal when a video device has been freed. I understand this is often used when you need to wait for an asynchronous operation to complete before continuing. This seems pretty standard in device driver code.
The presence of a mutex alongside the `completion` is immediately interesting. My assumption is this mutex is being employed to guard state flags related to the video device. This is a very common pattern in multi-threaded environments, like a kernel, to prevent race conditions when different parts of the driver access and modify shared data. It's the right approach for ensuring data integrity and preventing those nasty concurrent access bugs.
Now, let's dive deeper and compare this to other relevant commits. I need to be sure that this is following common patterns and isn't introducing a new problem. This will help inform whether this is a robust solution to the release problem.
I'll specifically review the `0d6c8144ca4d usb: gadget: f_ncm: Fix atomic context locking issue` and `56a512a9b410 usb: gadget: f_ncm: align net_device lifecycle with bind/unbind` commits. Both of these are within the USB gadget context, which is relevant to my area. These commits might provide insight into how similar problems have been addressed in the past, or provide context that will help me better understand the need for the `completion` and mutex here. Maybe I'll learn something useful about the general locking behavior within this driver area. I have to make sure that the locking is correct. I should also check if the other commits were merged and are already considered stable.
|
| 103/2 |
2026/07/29 08:13 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 56a512a9b4107079f68701e7d55da8507eb963d9
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Tue Dec 30 18:13:16 2025 +0800
usb: gadget: f_ncm: align net_device lifecycle with bind/unbind
Currently, the net_device is allocated in ncm_alloc_inst() and freed in
ncm_free_inst(). This ties the network interface's lifetime to the
configuration instance rather than the USB connection (bind/unbind).
This decoupling causes issues when the USB gadget is disconnected where
the underlying gadget device is removed. The net_device can outlive its
parent, leading to dangling sysfs links and NULL pointer dereferences
when accessing the freed gadget device.
Problem 1: NULL pointer dereference on disconnect
Unable to handle kernel NULL pointer dereference at virtual address
0000000000000000
Call trace:
__pi_strlen+0x14/0x150
rtnl_fill_ifinfo+0x6b4/0x708
rtmsg_ifinfo_build_skb+0xd8/0x13c
rtmsg_ifinfo+0x50/0xa0
__dev_notify_flags+0x4c/0x1f0
dev_change_flags+0x54/0x70
do_setlink+0x390/0xebc
rtnl_newlink+0x7d0/0xac8
rtnetlink_rcv_msg+0x27c/0x410
netlink_rcv_skb+0x134/0x150
rtnetlink_rcv+0x18/0x28
netlink_unicast+0x254/0x3f0
netlink_sendmsg+0x2e0/0x3d4
Problem 2: Dangling sysfs symlinks
console:/ # ls -l /sys/class/net/ncm0
lrwxrwxrwx ... /sys/class/net/ncm0 ->
/sys/devices/platform/.../gadget.0/net/ncm0
console:/ # ls -l /sys/devices/platform/.../gadget.0/net/ncm0
ls: .../gadget.0/net/ncm0: No such file or directory
Move the net_device allocation to ncm_bind() and deallocation to
ncm_unbind(). This ensures the network interface exists only when the
gadget function is actually bound to a configuration.
To support pre-bind configuration (e.g., setting interface name or MAC
address via configfs), cache user-provided options in f_ncm_opts
using the gether_opts structure. Apply these cached settings to the
net_device upon creation in ncm_bind().
Preserve the use-after-free fix from commit 6334b8e4553c ("usb: gadget:
f_ncm: Fix UAF ncm object at re-bind after usb ep transport error").
Check opts->net in ncm_set_alt() and ncm_disable() to ensure
gether_disconnect() runs only if a connection was established.
Fixes: 40d133d7f542 ("usb: gadget: f_ncm: convert to new function interface with backward compatibility")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://patch.msgid.link/20251230-ncm-refactor-v1-3-793e347bc7a7@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 0e38330271d5..e23adc132f88 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -83,6 +83,11 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
return container_of(f, struct f_ncm, port.func);
}
+static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f)
+{
+ return container_of(f->fi, struct f_ncm_opts, func_inst);
+}
+
/*-------------------------------------------------------------------------*/
/*
@@ -859,6 +864,7 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
@@ -881,12 +887,13 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt > 1)
goto fail;
- if (ncm->netdev) {
- DBG(cdev, "reset ncm\n");
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- ncm_reset_values(ncm);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ DBG(cdev, "reset ncm\n");
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ ncm_reset_values(ncm);
+ }
/*
* CDC Network only sends data in non-default altsettings.
@@ -919,7 +926,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
- ncm->netdev = net;
+ scoped_guard(mutex, &opts->lock)
+ opts->net = net;
}
spin_lock(&ncm->lock);
@@ -1366,14 +1374,16 @@ static int ncm_unwrap_ntb(struct gether *port,
static void ncm_disable(struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ncm deactivated\n");
- if (ncm->netdev) {
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ }
if (ncm->notify->enabled) {
usb_ep_disable(ncm->notify);
@@ -1433,39 +1443,44 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
struct usb_string *us;
int status = 0;
struct usb_ep *ep;
- struct f_ncm_opts *ncm_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *netdev __free(free_gether_netdev) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
- ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
-
if (cdev->use_os_string) {
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
if (!os_desc_table)
return -ENOMEM;
}
- mutex_lock(&ncm_opts->lock);
- gether_set_gadget(ncm_opts->net, cdev->gadget);
- if (!ncm_opts->bound) {
- ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
- status = gether_register_netdev(ncm_opts->net);
+ netdev = gether_setup_default();
+ if (IS_ERR(netdev))
+ return -ENOMEM;
+
+ scoped_guard(mutex, &ncm_opts->lock) {
+ gether_apply_opts(netdev, &ncm_opts->net_opts);
+ netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
}
- mutex_unlock(&ncm_opts->lock);
+ gether_set_gadget(netdev, cdev->gadget);
+ status = gether_register_netdev(netdev);
if (status)
return status;
- ncm_opts->bound = true;
-
- ncm_string_defs[1].s = ncm->ethaddr;
+ /* export host's Ethernet address in CDC format */
+ status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12)
+ return -EINVAL;
+ ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
@@ -1563,6 +1578,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
f->os_desc_n = 1;
}
ncm->notify_req = no_free_ptr(request);
+ ncm->netdev = no_free_ptr(netdev);
+ ncm->port.ioport = netdev_priv(ncm->netdev);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1577,19 +1594,19 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
}
/* f_ncm_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(ncm);
+USB_ETHER_OPTS_ITEM(ncm);
/* f_ncm_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm);
/* f_ncm_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
/* f_ncm_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
+USB_ETHER_OPTS_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+USB_ETHER_OPTS_ATTR_IFNAME(ncm);
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page)
@@ -1655,34 +1672,27 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst);
- if (opts->bound)
- gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
kfree(opts->ncm_interf_group);
kfree(opts);
}
static struct usb_function_instance *ncm_alloc_inst(void)
{
- struct f_ncm_opts *opts;
+ struct usb_function_instance *ret;
struct usb_os_desc *descs[1];
char *names[1];
struct config_group *ncm_interf_group;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ struct f_ncm_opts *opts __free(kfree) = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
+
+ opts->net = NULL;
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
+ gether_setup_opts_default(&opts->net_opts, "usb");
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
@@ -1693,26 +1703,22 @@ static struct usb_function_instance *ncm_alloc_inst(void)
ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE);
- if (IS_ERR(ncm_interf_group)) {
- ncm_free_inst(&opts->func_inst);
+ if (IS_ERR(ncm_interf_group))
return ERR_CAST(ncm_interf_group);
- }
opts->ncm_interf_group = ncm_interf_group;
- return &opts->func_inst;
+ ret = &opts->func_inst;
+ retain_and_null_ptr(opts);
+ return ret;
}
static void ncm_free(struct usb_function *f)
{
- struct f_ncm *ncm;
- struct f_ncm_opts *opts;
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
- ncm = func_to_ncm(f);
- opts = container_of(f->fi, struct f_ncm_opts, func_inst);
- kfree(ncm);
- mutex_lock(&opts->lock);
- opts->refcnt--;
- mutex_unlock(&opts->lock);
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt--;
+ kfree(func_to_ncm(f));
}
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1736,13 +1742,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ ncm->port.ioport = NULL;
+ gether_cleanup(netdev_priv(ncm->netdev));
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
- int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1750,22 +1758,12 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ncm_opts, func_inst);
- mutex_lock(&opts->lock);
- opts->refcnt++;
- /* export host's Ethernet address in CDC format */
- status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
- sizeof(ncm->ethaddr));
- if (status < 12) { /* strlen("01234567890a") */
- kfree(ncm);
- mutex_unlock(&opts->lock);
- return ERR_PTR(-EINVAL);
- }
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt++;
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
- ncm->port.ioport = netdev_priv(opts->net);
- mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 49ec095cdb4b..d99330fe31e8 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -15,11 +15,13 @@
#include <linux/usb/composite.h>
+#include "u_ether.h"
+
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
- bool bound;
+ struct gether_opts net_opts;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];
]
|
| 104/2 |
2026/07/29 08:13 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 32961
output: 29
thoughts: 180
Thoughts: **Analyzing a UAF Fix in Raw Gadget**
Okay, so this commit addresses a Use-After-Free (UAF) issue. The core of the fix revolves around shifting the net_device allocation and deallocation to the `bind/unbind` operations. However, digging deeper, the real problem lies within `raw_gadget.c` specifically the `gadget_unbind()` function. It seems it isn't waiting for all pending ioctls, particularly calls like `usb_ep_queue()`, to complete before it exits.
The prompt highlights a race condition, and the suggested solution revolves around introducing a synchronization mechanism to track and manage ongoing UDC (USB Device Controller) calls. It mentions that other drivers, like `gadgetfs` within `drivers/usb/gadget/legacy/inode.c`, have employed a `udc_usage` counter to achieve this synchronization.
Alright, a `udc_usage` counter it is. To get a better handle on how this is typically implemented, I'll start by searching the Git log. I want to see how other drivers have introduced and used a `udc_usage` counter. This will help inform a clean implementation for raw_gadget.
|
| 105/2 |
2026/07/29 08:14 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:udc_usage Since:10 years]
Results:
|
| 106/2 |
2026/07/29 08:24 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 33463
output: 42
thoughts: 14
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Focusing the Lens: The `drivers/usb/gadget/` Area**
Alright, so the initial thought process is pretty clear: let's narrow our focus. Instead of getting bogged down in the vastness of the kernel source, we need to apply some intelligent scoping. The directive, "Let's restrict to `drivers/usb/gadget/`," is a crucial starting point. This says we're zeroing in on the USB gadget driver subsystem. This implies that my current line of inquiry or problem domain likely centers around device-mode USB functionality within the Linux kernel. It could be debugging a specific gadget function, understanding a particular controller's behavior, exploring a new feature, or perhaps even contributing to the existing code base. Given the target, I would expect to have a good understanding of USB device-mode concepts, such as endpoints, descriptors, composite devices, and standard protocols like mass storage, CDC, or HID. My thought here is that this narrowing down is strategic, ensuring that I maintain a manageable context for the task at hand. It's a fundamental step for effective kernel investigation.
|
| 107/2 |
2026/07/29 08:24 |
tool |
git-log |
0m
Args: map[CodeRegexp:udc_usage PathPrefix:drivers/usb/gadget/ Since:10 years]
Results: map[Output:520b72fc64de USB: gadgetfs: Fix crash caused by inadequate synchronization
6e76c01e7155 USB: gadgetfs: fix copy_to_user while holding spinlock
]
|
| 108/2 |
2026/07/29 08:24 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 33837
output: 25
thoughts: 28
Thoughts: Okay, here's my interpretation of that commit message, expressed as my internal thought process:
**Diving into the GadgetFS Synchronization Bug**
Alright, let's take a look at commit `520b72fc64de USB: gadgetfs: Fix crash caused by inadequate synchronization`. I immediately zero in on "inadequate synchronization" β that's often a red flag in kernel code. It hints at a race condition, a classic source of hard-to-reproduce crashes. GadgetFS, especially, deals with USB device emulation, and it's already a complex beast. I'm imagining potentially multiple threads or contexts accessing shared resources within the USB gadget framework. This commit is probably addressing an instance where accesses to some memory or data are not being properly protected.
The "crash" part reinforces my suspicions. These synchronization issues can lead to memory corruption, use-after-free scenarios, or even kernel panics. The fact that they pinpointed it to a crash indicates this bug was serious, and I suspect it was relatively reliable (at least in specific usage conditions) to be narrowed down in this way. I'll need to see the code diff to understand which data structures, locks, or atomic operations were missing or being incorrectly applied. It's possible a mutex wasn't used correctly, or spinlocks were used in a place where they'd block the correct execution flow and deadlock. Or maybe there was an incorrect memory barrier placement. Gotta know the details, of course, but "synchronization" and "crash" together tell a compelling story. I expect the fix to be a relatively localized change, likely adding or adjusting the existing locking mechanisms.
|
| 109/2 |
2026/07/29 08:24 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 520b72fc64debf8a86c3853b8e486aa5982188f0
Author: Alan Stern <stern@rowland.harvard.edu>
Date: Thu Sep 21 13:23:58 2017 -0400
USB: gadgetfs: Fix crash caused by inadequate synchronization
The gadgetfs driver (drivers/usb/gadget/legacy/inode.c) was written
before the UDC and composite frameworks were adopted; it is a legacy
driver. As such, it expects that once bound to a UDC controller, it
will not be unbound until it unregisters itself.
However, the UDC framework does unbind function drivers while they are
still registered. When this happens, it can cause the gadgetfs driver
to misbehave or crash. For example, userspace can cause a crash by
opening the device file and doing an ioctl call before setting up a
configuration (found by Andrey Konovalov using the syzkaller fuzzer).
This patch adds checks and synchronization to prevent these bad
behaviors. It adds a udc_usage counter that the driver increments at
times when it is using a gadget interface without holding the private
spinlock. The unbind routine waits for this counter to go to 0 before
returning, thereby ensuring that the UDC is no longer in use.
The patch also adds a check in the dev_ioctl() routine to make sure
the driver is bound to a UDC before dereferencing the gadget pointer,
and it makes destroy_ep_files() synchronize with the endpoint I/O
routines, to prevent the user from accessing an endpoint data
structure after it has been removed.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Tested-by: Andrey Konovalov <andreyknvl@google.com>
CC: <stable@vger.kernel.org>
Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 956b3dc7c3a4..5c28bee327e1 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -28,7 +28,7 @@
#include <linux/aio.h>
#include <linux/uio.h>
#include <linux/refcount.h>
-
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -116,6 +116,7 @@ enum ep0_state {
struct dev_data {
spinlock_t lock;
refcount_t count;
+ int udc_usage;
enum ep0_state state; /* P: lock */
struct usb_gadgetfs_event event [N_EVENT];
unsigned ev_next;
@@ -513,9 +514,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work);
}
- spin_unlock(&epdata->dev->lock);
usb_ep_free_request(ep, req);
+ spin_unlock(&epdata->dev->lock);
put_ep(epdata);
}
@@ -939,9 +940,11 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
struct usb_request *req = dev->req;
if ((retval = setup_req (ep, req, 0)) == 0) {
+ ++dev->udc_usage;
spin_unlock_irq (&dev->lock);
retval = usb_ep_queue (ep, req, GFP_KERNEL);
spin_lock_irq (&dev->lock);
+ --dev->udc_usage;
}
dev->state = STATE_DEV_CONNECTED;
@@ -1134,6 +1137,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
dev->state = STATE_DEV_CONNECTED;
+ ++dev->udc_usage;
spin_unlock_irq (&dev->lock);
if (copy_from_user (dev->req->buf, buf, len))
retval = -EFAULT;
@@ -1145,6 +1149,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
GFP_KERNEL);
}
spin_lock_irq(&dev->lock);
+ --dev->udc_usage;
if (retval < 0) {
clean_req (dev->gadget->ep0, dev->req);
} else
@@ -1246,9 +1251,21 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
struct usb_gadget *gadget = dev->gadget;
long ret = -ENOTTY;
- if (gadget->ops->ioctl)
+ spin_lock_irq(&dev->lock);
+ if (dev->state == STATE_DEV_OPENED ||
+ dev->state == STATE_DEV_UNBOUND) {
+ /* Not bound to a UDC */
+ } else if (gadget->ops->ioctl) {
+ ++dev->udc_usage;
+ spin_unlock_irq(&dev->lock);
+
ret = gadget->ops->ioctl (gadget, code, value);
+ spin_lock_irq(&dev->lock);
+ --dev->udc_usage;
+ }
+ spin_unlock_irq(&dev->lock);
+
return ret;
}
@@ -1466,10 +1483,12 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (value < 0)
break;
+ ++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
GFP_KERNEL);
spin_lock (&dev->lock);
+ --dev->udc_usage;
if (value < 0) {
clean_req (gadget->ep0, dev->req);
break;
@@ -1493,8 +1512,12 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = value;
req->zero = value < w_length;
+ ++dev->udc_usage;
spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
+ spin_lock(&dev->lock);
+ --dev->udc_usage;
+ spin_unlock(&dev->lock);
if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
@@ -1521,21 +1544,24 @@ static void destroy_ep_files (struct dev_data *dev)
/* break link to FS */
ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
list_del_init (&ep->epfiles);
+ spin_unlock_irq (&dev->lock);
+
dentry = ep->dentry;
ep->dentry = NULL;
parent = d_inode(dentry->d_parent);
/* break link to controller */
+ mutex_lock(&ep->lock);
if (ep->state == STATE_EP_ENABLED)
(void) usb_ep_disable (ep->ep);
ep->state = STATE_EP_UNBOUND;
usb_ep_free_request (ep->ep, ep->req);
ep->ep = NULL;
+ mutex_unlock(&ep->lock);
+
wake_up (&ep->wait);
put_ep (ep);
- spin_unlock_irq (&dev->lock);
-
/* break link to dcache */
inode_lock(parent);
d_delete (dentry);
@@ -1606,6 +1632,11 @@ gadgetfs_unbind (struct usb_gadget *gadget)
spin_lock_irq (&dev->lock);
dev->state = STATE_DEV_UNBOUND;
+ while (dev->udc_usage > 0) {
+ spin_unlock_irq(&dev->lock);
+ usleep_range(1000, 2000);
+ spin_lock_irq(&dev->lock);
+ }
spin_unlock_irq (&dev->lock);
destroy_ep_files (dev);
]
|
| 110/2 |
2026/07/29 08:24 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 36701
output: 47
thoughts: 595
Thoughts: **Analyzing Synchronization Approaches in USB Gadget Driver Unbind Operations**
Okay, so this is the commit where `udc_usage` was introduced in `gadgetfs` β makes sense. The bug report is clear: the `raw_gadget` driver needs better synchronization around UDC calls during unbinding to avoid a race condition. It seems the common solution is to ensure ongoing calls are completed before `gadget_unbind()` returns. They're asking me to figure out how similar problems were solved elsewhere in the USB gadget subsystem.
I've already found a few relevant examples. In `gadgetfs` (`inode.c`), the `udc_usage` counter was the fix, combined with a `while` loop and `usleep_range()` in `gadgetfs_unbind()`. Then there is `f_fs.c`, where they used a `mutex_lock` to serialize `functionfs_unbind` and `ffs_ep0_queue_wait`, preventing a race. Next, `f_uvc.c` dealt with a NULL pointer dereference using a `completion` (`wait_for_completion(&vdev_release_done)`) and a mutex to protect state flags. Lastly, I've seen in `f_ncm.c`, a UAF was solved by realigning the net_device lifecycle with the bind/unbind.
Wait a second, let me check the prompt again: "Your goal is to explore how similar bugs were fixed in the past in the same subsystem or files. ... Search for commits that address issues with similar root causes (e.g. similar missing locks, incorrect refcounting, or similar error path bugs) in the affected files. ... Your final reply must summarize your findings: what idioms, locking rules, or common patterns should be followed when writing a fix for this bug based on how previous similar bugs were addressed."
Right, I need to focus on *similar* root causes and synthesize this into a set of best practices, not just a list. I'm going to have to ensure I've exhausted searching for similar issues inside the file itself, and then look more broadly. I must now check `raw_gadget.c` more closely for anything else that might be relevant before summarizing my findings in the most useful and actionable way possible.
|
| 111/2 |
2026/07/29 08:24 |
tool |
git-log |
0m
Args: map[MessageRegexps:[race] PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c Since:10 years]
Results: map[Output:83e30f2bf86e USB: gadget: Fix the memory leak in raw_gadget driver
f2d8c2606825 usb: gadget: Fix non-unique driver names in raw-gadget driver
5f0b5f4d50fa usb: gadget: fix race when gadget driver register via ioctl
129aa9734559 usb: raw-gadget: fix memory leak in gadget_setup
]
|
| 112/2 |
2026/07/29 08:24 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 37947
output: 47
thoughts: 56
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Investigating Potential Synchronization Issues in `raw_gadget.c` After a Registration Race Fix**
Right, so commit `5f0b5f4d50fa` addressed a race condition during registration by introducing `STATE_DEV_REGISTERING`. That's good; it's always a solid approach to get a handle on those race conditions, by way of adding states. Now, with that recent patch in mind, I need to make sure we've fully addressed this issue, because these kinds of subtle bugs tend to linger in the code. I need to dig a little deeper.
My immediate thought is to check `raw_gadget.c` for other potential issues with synchronization. The initial fix is a good start, but now I'm starting to worry about other race conditions that might be lurking in that same section of code, or that might use similar approaches, like state changes. I'm going to look at the other commits in that file. I need to know: are there any other instances where synchronization primitives are used or *should* be used, but aren't? Are there other instances where state transitions are critical and could potentially create similar problems? I'll probably check for related context, too. This is the best way to get a full picture. Let's see what else might be hiding in there.
|
| 113/2 |
2026/07/29 08:24 |
tool |
git-log |
0m
Args: map[MessageRegexps:[lock] PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c Since:10 years]
Results: map[Output:fdd10499de0d usb: raw-gadget: fix raw_event_queue_fetch locking
]
|
| 114/2 |
2026/07/29 08:24 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 38388
output: 25
thoughts: 33
Thoughts: Okay, let's dive into commit `fdd10499de0d usb: raw-gadget: fix raw_event_queue_fetch locking`.
**Analyzing the Locking Mechanism in `raw_event_queue_fetch`**
Alright, so I'm looking at this fix for the `raw_event_queue_fetch` function within the USB raw-gadget subsystem. Immediately, my expert eye goes to the locking mechanism. This is critical; concurrent access to the event queue is a classic race condition scenario. I'm assuming, based on the commit message, that the current implementation has a flaw. The message states we need to `fix` the locking so I begin to analyze the structure of that part of the code and how they were implementing it. I need to understand what's been identified as the problem to assess the efficacy of the fix. I'm going to look for areas where the lock might be acquired and released in an incorrect order, or where a read/write might occur without proper synchronization. Perhaps there's an issue with the scope of the lock (too broad, or not broad enough), or potentially a deadlock scenario. I'm going to determine if the fix introduces a different problem. Essentially, my goal is to verify that this fix indeed addresses the potential data corruption or race condition that's described in the commit's description, and ensure that it doesn't introduce any regressions or performance bottlenecks. I need to make sure the fix is sound and correct, and that I understand exactly why it was necessary in the first place.
|
| 115/2 |
2026/07/29 08:24 |
tool |
git-show |
0m
Args:
Results: map[Output:commit fdd10499de0dbcd2cdfe8108ed162bb00fe6f276
Author: Andrey Konovalov <andreyknvl@gmail.com>
Date: Tue Apr 7 16:47:54 2020 +0200
usb: raw-gadget: fix raw_event_queue_fetch locking
If queue->size check in raw_event_queue_fetch() fails (which normally
shouldn't happen, that check is a fail-safe), the function returns
without reenabling interrupts. This patch fixes that issue, along with
propagating the cause of failure to the function caller.
Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Link: https://lore.kernel.org/r/9f7ce7a1472cfb9447f6c5a494186fa1f2670f6f.1586270396.git.andreyknvl@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index e490ffa1f58b..ca7d95bf7397 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -81,6 +81,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue,
static struct usb_raw_event *raw_event_queue_fetch(
struct raw_event_queue *queue)
{
+ int ret;
unsigned long flags;
struct usb_raw_event *event;
@@ -89,11 +90,18 @@ static struct usb_raw_event *raw_event_queue_fetch(
* there's at least one event queued by decrementing the semaphore,
* and then take the lock to protect queue struct fields.
*/
- if (down_interruptible(&queue->sema))
- return NULL;
+ ret = down_interruptible(&queue->sema);
+ if (ret)
+ return ERR_PTR(ret);
spin_lock_irqsave(&queue->lock, flags);
- if (WARN_ON(!queue->size))
- return NULL;
+ /*
+ * queue->size must have the same value as queue->sema counter (before
+ * the down_interruptible() call above), so this check is a fail-safe.
+ */
+ if (WARN_ON(!queue->size)) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ERR_PTR(-ENODEV);
+ }
event = queue->events[0];
queue->size--;
memmove(&queue->events[0], &queue->events[1],
@@ -522,10 +530,17 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
- if (!event) {
+ if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
+ if (IS_ERR(event)) {
+ dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENODEV;
+ }
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length))
return -EFAULT;
]
|
| 116/2 |
2026/07/29 08:24 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 39879
output: 1053
thoughts: 974
Thoughts: **Analyzing the USB Gadget Unbind Race Condition**
Okay, this looks like a straightforward locking issue to address. I've narrowed it down to a race condition between `gadget_unbind()` and `raw_process_ep0_io()` in `raw_gadget.c`. The user proposed a `udc_usage` counter, mirroring the `gadgetfs` approach, which seems like a reasonable starting point.
To get a broader perspective, I've researched similar fixes in the USB gadget subsystem over the past few years. My analysis of existing fixes highlights several patterns for synchronization during unbind:
* **The Gadgetfs Approach (Usage Counters and Sleep Loops):** This method, exemplified by commit `520b72fc64de`, uses a `udc_usage` counter to track ongoing UDC operations. The counter is incremented before making a UDC call and decremented afterward, with a `while` loop and `usleep_range()` in `gadgetfs_unbind()` to wait for the counter to reach zero. This pattern is functionally sound, but the polling nature of `usleep_range()` is not ideal in modern kernel code, as it's generally discouraged.
* **Completions for Explicit Synchronization:** Commit `eba2936bbe6b` in `f_uvc.c` demonstrates the use of a `completion` primitive to wait for specific operations to complete before unbinding. A mutex also protects a state flag to avoid accessing null pointers. This offers more precise synchronization.
* **Mutex Serialization:** Commit `6a19da111057` in `f_fs.c` provides a simpler solution by using a mutex to serialize access to critical sections in `functionfs_unbind` and the ioctl path.
* **State Machine Updates:** The `raw_gadget.c` itself, in commit `5f0b5f4d50fa` used a state machine to prevent concurrent operations. Also updating `dev->state = STATE_DEV_FAILED` (or an equivalent) in `gadget_unbind()` under `dev->lock` is critical to prevent new ioctls from proceeding.
Considering the existing patterns and the fact that `raw_gadget` already utilizes `dev->lock` (a spinlock), using a `udc_usage` counter alongside a `waitqueue` (e.g., `wait_event_interruptible`) would be the most efficient and idiomatically correct way to wait for UDC calls to finish. This avoids the busy-waiting of `usleep_range()`. A `completion` could also work depending on the number of concurrent operations we need to wait for. While using the `gadgetfs` pattern of `udc_usage` + `usleep_range()` is possible for consistency within the legacy drivers, the `waitqueue` or `completion` approach is preferable.
|
| 117/1 |
2026/07/29 08:25 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp2033811510]
|
| 118/1 |
2026/07/29 08:25 |
loop |
|
1h13m
|
| 119/2 |
2026/07/29 08:25 |
iteration |
0 |
45m
|
| 120/3 |
2026/07/29 08:25 |
agent |
patch-generator |
16m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
The explanation of the root cause of the bug is:
The root cause of this crash is a use-after-free (UAF) vulnerability in the `raw_gadget` driver. It occurs because the driver fails to properly clean up its internal state and synchronize concurrent operations when the underlying USB Device Controller (UDC) is unbound.
### Detailed Sequence of Events
1. **Gadget Initialization and Binding:**
The reproducer opens `/dev/raw-gadget` and initializes it. When the gadget binds to the `dummy_udc` controller, the UDC core invokes the `gadget_bind()` callback. This function saves the `struct usb_gadget` pointer into `dev->gadget` and eventually sets `dev->state = STATE_DEV_RUNNING`.
2. **UDC Unbinding:**
The reproducer then writes to `/sys/bus/platform/drivers/dummy_udc/unbind` via sysfs. This forces the UDC core to unbind the gadget driver, which invokes the `gadget_unbind()` callback in `raw_gadget.c`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
Notice that `gadget_unbind()` **fails to clear `dev->gadget`** and **fails to update `dev->state`**. It only clears the driver data. After this function returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device` (including the device name string allocated via `kvasprintf`).
3. **Dangling Pointer Dereference:**
The reproducer then issues a `USB_RAW_IOCTL_EP0_READ` (or `WRITE`) ioctl, which lands in `raw_process_ep0_io()`:
```c
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, void *data, bool in)
{
// ...
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { // Passes, state was never updated
// ...
}
if (!dev->gadget) { // Passes, dev->gadget is a dangling pointer
// ...
}
// ...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);
// ...
```
Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
4. **The Crash:**
Since the UDC is disconnected/freed, `usb_ep_queue()` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, ...)`. The `dev_err()` macro attempts to print the device name (`dev->gadget->dev.kobj.name`), which points to the freed 16-byte string, resulting in the KASAN `slab-use-after-free` crash observed in the trace.
### How to Fix
To fix this bug, `gadget_unbind()` must clear the `dev->gadget` pointer and update `dev->state` (e.g., to `STATE_DEV_FAILED`) while holding `dev->lock`.
However, simply clearing the pointer is not enough due to race conditions. If an ioctl thread has already passed the `!dev->gadget` check and released `dev->lock`, but hasn't yet called `usb_ep_queue()` (which is called lockless), `gadget_unbind()` could return and free the gadget right under the ioctl's feet.
To prevent this race, `raw_gadget` needs a synchronization mechanism to track ongoing UDC calls and wait for them to finish before returning from `gadget_unbind()`. A common pattern for this (as seen in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`) is to introduce a `udc_usage` counter:
- Increment `udc_usage` under `dev->lock` before releasing the lock to call `usb_ep_queue()`.
- Decrement `udc_usage` under `dev->lock` after `usb_ep_queue()` returns.
- In `gadget_unbind()`, wait for `udc_usage` to drop to `0` before returning.
Historical context on how similar bugs were fixed in the past:
Based on a review of the kernel history for the USB gadget subsystem (specifically within `drivers/usb/gadget/`), there are several established patterns for fixing use-after-free (UAF) and race conditions during the unbind phase.
Here is a summary of the idioms, locking rules, and common patterns used to address similar bugs:
### 1. The Usage Counter Pattern (Legacy Gadgetfs)
As you noted, `drivers/usb/gadget/legacy/inode.c` (`gadgetfs`) uses a `udc_usage` counter. This was introduced in commit `520b72fc64de` ("USB: gadgetfs: Fix crash caused by inadequate synchronization") to fix a very similar crash.
* **How it works:** The driver increments `udc_usage` under a spinlock before releasing the lock to call lockless UDC functions (like `usb_ep_queue`). It decrements it after re-acquiring the lock.
* **The Wait Mechanism:** In `gadgetfs_unbind()`, it uses a polling loop to wait for the counter to drop to zero:
```c
while (dev->udc_usage > 0) {
spin_unlock_irq(&dev->lock);
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
```
* **Takeaway:** While this is the exact precedent in the legacy gadget directory, polling with `usleep_range()` is generally discouraged in modern kernel development if better primitives are available.
### 2. Explicit Synchronization via Completions (Modern Approach)
More recent fixes in the gadget subsystem prefer using `struct completion` to synchronize unbind routines with ongoing asynchronous operations.
* **Example:** Commit `eba2936bbe6b` ("usb: gadget: uvc: fix NULL pointer dereference during unbind race") in `f_uvc.c` fixed a UAF during unbind by introducing a `completion`.
* **How it works:** The unbind function sets a flag (protected by a mutex) to prevent new operations, and then calls `wait_for_completion()`. The ongoing operation calls `complete()` when it finishes its critical section, allowing the unbind to safely proceed and free the memory.
### 3. Mutex Serialization
If the ongoing operation can safely block, simply serializing the unbind and the ioctl/queue operations with a mutex is a common fix.
* **Example:** Commit `6a19da111057` ("usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait") in `f_fs.c` fixed a UAF where `functionfs_unbind` was freeing the request while `ffs_ep0_write` was using it.
* **How it works:** They wrapped the teardown logic in `functionfs_unbind()` with `mutex_lock(&ffs->mutex)`, ensuring it could not run concurrently with the queueing logic.
### 4. Strict State Machine Enforcement
In `raw_gadget.c` itself, previous race conditions have been fixed by strictly enforcing state transitions.
* **Example:** Commit `5f0b5f4d50fa` ("usb: gadget: fix race when gadget driver register via ioctl") fixed a double-registration race by introducing a `STATE_DEV_REGISTERING` state.
* **Takeaway:** For your bug, updating `dev->state` (e.g., to `STATE_DEV_FAILED` or a new `STATE_DEV_UNBOUND`) inside `gadget_unbind()` while holding `dev->lock` is a mandatory first step. This ensures that any *new* ioctl calls will fail the `dev->state == STATE_DEV_RUNNING` check and bail out early.
### Summary Recommendation for the Fix
To fix this bug in `raw_gadget.c` while adhering to kernel idioms:
1. **State Update:** In `gadget_unbind()`, acquire `dev->lock`, clear `dev->gadget`, and set `dev->state = STATE_DEV_FAILED` (or similar).
2. **Synchronization:** To protect *already running* ioctls that have passed the state check, introduce a tracking mechanism.
* You can use the `udc_usage` counter approach for consistency with `gadgetfs`.
* However, instead of a `usleep_range()` polling loop, it is much more idiomatic to use a `wait_queue_head_t` (e.g., `wait_event_lock_irq()`) or a `completion` to put the `gadget_unbind()` thread to sleep until the last ongoing `usb_ep_queue()` finishes and wakes it up.
Reply: The root cause of this bug is a race condition between `gadget_unbind()` and various ioctls (like `raw_process_ep0_io()`, `raw_process_ep_io()`, and `raw_ioctl_ep_disable()`) that drop `dev->lock` to call UDC functions (e.g., `usb_ep_queue()`, `usb_ep_dequeue()`, `usb_ep_disable()`).
If `gadget_unbind()` is invoked concurrently, it clears the gadget driver data and returns, allowing the UDC core to free the `struct usb_gadget`. However, the ioctls might still be running or waiting for completion. When they wake up or fail, they dereference the freed `dev->gadget` pointer (e.g., to print an error message or to call `usb_ep_dequeue()`), leading to a KASAN slab-use-after-free.
Additionally, there was another UAF in `dev_free()`: `gadget_unbind()` did not free the endpoint requests (`dev->req` and `dev->eps[i].req`), leaving them to be freed by `dev_free()` when the device is closed. But `dev_free()` used `dev->gadget->ep0` to free them, which is a UAF if the gadget has already been unbound and freed.
### Fix Details
1. **Synchronization with `udc_usage` and `completion`:**
Similar to `gadgetfs`, we introduce a `udc_usage` counter to track ongoing UDC calls.
- Before dropping `dev->lock` to call a UDC function, we increment `udc_usage`.
- After re-acquiring `dev->lock`, we decrement `udc_usage` and, if it reaches 0, signal a completion.
- In `gadget_unbind()`, we set `dev->state = STATE_DEV_FAILED` and wait for `udc_usage` to drop to 0 using `wait_for_completion()`. This ensures no UDC calls are running when the gadget is unbound.
2. **Avoiding Deadlocks during `wait_for_completion`:**
We strictly hold `udc_usage` *only* while calling UDC functions, and **not** while waiting for the IO completion (`wait_for_completion_interruptible()`). Holding it during the wait could cause a deadlock if the UDC core waits for `gadget_unbind()` to finish before stopping the controller.
3. **Safe Gadget Dereferencing:**
After waking up from `wait_for_completion_interruptible()`, we must re-check `dev->gadget` under `dev->lock` before calling `usb_ep_dequeue()`. If `dev->gadget` is NULL, it means `gadget_unbind()` has already run, the controller is stopped, and the request is already cancelled, so we can safely skip the dequeue.
We also replace `dev_err(&dev->gadget->dev, ...)` with `dev_err(dev->dev, ...)` in paths where `dev->lock` is not held, as `dev->dev` (the misc device) is always safe to access.
4. **Proper Cleanup in `gadget_unbind()`:**
We move the cleanup of `dev->req` and `dev->eps[i].req` from `dev_free()` to `gadget_unbind()` (and the `gadget_bind()` error path). This prevents `dev_free()` from dereferencing the freed `dev->gadget` pointer long after the gadget has been unbound.
|
| 121/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 11228
output: 34
thoughts: 111
Thoughts: **Analyzing a KASAN Slab-Use-After-Free Bug in raw_gadget.c**
Okay, so I've got a KASAN slab-use-after-free bug to dig into, and it's flagged in `raw_gadget.c`. The root cause? A nasty race condition between the `gadget_unbind()` function and the `raw_process_ep0_io()` function. It seems that `gadget_unbind()` isn't properly cleaning up after itself. Specifically, it's missing crucial steps: not clearing `dev->gadget` or updating `dev->state` as it should, and critically, it's not waiting for any ongoing UDC (USB Device Controller) calls to complete before freeing resources. This looks like a classic scenario where a piece of memory is freed prematurely, and then accessed later β hence the use-after-free. I need to dive into the source code to confirm these initial observations. Let's start by taking a look at `drivers/usb/gadget/legacy/raw_gadget.c` and see what we can find.
|
| 122/4 |
2026/07/29 08:25 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Entities:[map[Kind:function Name:dev_free] map[Kind:function Name:dev_new] map[Kind:function Name:fill_ep_caps] map[Kind:function Name:fill_ep_limits] map[Kind:function Name:gadget_bind] map[Kind:function Name:gadget_disconnect] map[Kind:function Name:gadget_ep0_complete] map[Kind:function Name:gadget_ep_complete] map[Kind:function Name:gadget_reset] map[Kind:function Name:gadget_resume] map[Kind:function Name:gadget_setup] map[Kind:function Name:gadget_suspend] map[Kind:function Name:gadget_unbind] map[Kind:function Name:get_ep_addr] map[Kind:function Name:raw_alloc_io_data] map[Kind:function Name:raw_event_queue_add] map[Kind:function Name:raw_event_queue_destroy] map[Kind:function Name:raw_event_queue_fetch] map[Kind:function Name:raw_event_queue_init] map[Kind:function Name:raw_ioctl] map[Kind:function Name:raw_ioctl_configure] map[Kind:function Name:raw_ioctl_ep0_read] map[Kind:function Name:raw_ioctl_ep0_stall] map[Kind:function Name:raw_ioctl_ep0_write] map[Kind:function Name:raw_ioctl_ep_disable] map[Kind:function Name:raw_ioctl_ep_enable] map[Kind:function Name:raw_ioctl_ep_read] map[Kind:function Name:raw_ioctl_ep_set_clear_halt_wedge] map[Kind:function Name:raw_ioctl_ep_write] map[Kind:function Name:raw_ioctl_eps_info] map[Kind:function Name:raw_ioctl_event_fetch] map[Kind:function Name:raw_ioctl_init] map[Kind:function Name:raw_ioctl_run] map[Kind:function Name:raw_ioctl_vbus_draw] map[Kind:function Name:raw_misc_device_exit] map[Kind:function Name:raw_misc_device_init] map[Kind:function Name:raw_open] map[Kind:function Name:raw_process_ep0_io] map[Kind:function Name:raw_process_ep_io] map[Kind:function Name:raw_queue_event] map[Kind:function Name:raw_release] map[Kind:struct Name:raw_dev] map[Kind:struct Name:raw_ep] map[Kind:struct Name:raw_event_queue] map[Kind:global_variable Name:__UNIQUE_ID_addressable_raw_misc_device_init_903] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_779] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_780] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_781] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_782] map[Kind:global_variable Name:__exitcall_raw_misc_device_exit] map[Kind:global_variable Name:driver_id_numbers] map[Kind:global_variable Name:raw_fops] map[Kind:global_variable Name:raw_misc_device] map[Kind:enum Name:dev_state] map[Kind:enum Name:ep_state]]]
|
| 123/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 12596
output: 42
thoughts: 17
Thoughts: Here's my take on the thought process, as requested:
**Diving into the Driver Internals**
Okay, so I'm looking at something related to device drivers, probably USB given the "gadget" mention. My first instinct is to zero in on `struct raw_dev`. This struct likely holds the raw device representation within the kernel's USB stack, and understanding its structure is critical for debugging any device-related issues or understanding how the device is registered and managed. I need to refresh my memory on the key members, especially those related to resource allocation, endpoint management, and driver binding.
Then, since this likely involves device removal or detachment, `gadget_unbind` immediately pops into my head. This function is likely the core of the unbinding or detachment process in the USB gadget framework. Understanding its behavior is fundamental to knowing how a USB device is properly removed, which in turn might be critical for diagnosing issues related to device stability or resource leaks upon disconnect. I need to understand its function, and how it is related to `struct raw_dev`.
|
| 124/4 |
2026/07/29 08:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_dev]
Results: map[SourceCode: 160: struct raw_dev {
161: struct kref count;
162: spinlock_t lock;
163:
164: const char *udc_name;
165: struct usb_gadget_driver driver;
166:
167: /* Reference to misc device: */
168: struct device *dev;
169:
170: /* Make driver names unique */
171: int driver_id_number;
172:
173: /* Protected by lock: */
174: enum dev_state state;
175: bool gadget_registered;
176: struct usb_gadget *gadget;
177: struct usb_request *req;
178: bool ep0_in_pending;
179: bool ep0_out_pending;
180: bool ep0_urb_queued;
181: ssize_t ep0_status;
182: struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
183: int eps_num;
184:
185: struct completion ep0_done;
186: struct raw_event_queue queue;
187: };
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 125/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13291
output: 43
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing the `gadget_unbind` Function**
Right, let's dive straight in. The mental note I'm making here is: *`gadget_unbind` - check it out.* It's that direct. I need to refresh my memory or perhaps investigate something specific. Given my expertise, this is more likely to be a targeted investigation rather than a general review. I am probably looking at a system interaction, perhaps during debug, or in troubleshooting. Perhaps looking for a memory leak? Or how the gadget is behaving under certain load conditions. It might be related to a specific bug I've seen before or I'm trying to reproduce a reported issue.
My mind is implicitly assuming I know the context β what "gadget" we're talking about, where it's bound, and the overall system architecture. Now the *how* of that "check" can vary. It could be looking at the source code, inspecting logs for unbind events, stepping through the code with a debugger, or perhaps examining the kernel module's behavior. My focus is going to be on the `unbind` aspect. What is the process? Is there something missing?
|
| 126/4 |
2026/07/29 08:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:gadget_unbind]
Results: map[SourceCode: 328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 127/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13780
output: 47
thoughts: 14
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in the field and providing a fulsome summary:
**Checking `raw_process_ep0_io`**
Alright, time to dive into `raw_process_ep0_io`. That likely means we're dealing with the initial input/output stage for episode zero, right? Assuming this is a system involving episodes of something, which makes sense. I'm going to want to understand exactly how the system is handling the raw input data at the very beginning of the process. Is it validating the data? Are there any obvious bottlenecks or potential errors lurking here in the initial parsing or buffering? I need to quickly identify if there are any obvious issues. Where is this function defined? Let's check the function signature and documentation first to get an overview. My experience has me suspecting that these initial io functions can be a frequent source of performance problems if not optimized properly. I will now examine the function and follow its execution flow to ensure there are no issues.
|
| 128/4 |
2026/07/29 08:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep0_io]
Results: map[SourceCode: 683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 129/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 15272
output: 46
thoughts: 108
Thoughts: Okay, let me break this down.
**Analyzing the Concurrency Control Strategy for USB Device Driver Ioctls**
Right, so I'm looking at how to manage concurrency within this USB device driver, specifically around those ioctl calls. The initial idea, as the code seems to suggest, is to employ a `udc_usage` counter combined with either a `wait_queue_head_t` or, potentially, a `completion`. Hmm, that seems reasonable for basic synchronization. But then I pause... can I simplify this with just a `completion` structure? That's the question.
Wait a second, there's a wrinkle. We might have *multiple* ioctls running concurrently, which throws a wrench into a simple `completion`. Thinking about `raw_process_ep0_io`, it only permits one ep0 URB to be queued at any given time because of the `dev->ep0_urb_queued` flag. That makes sense, keeps things serialized on that endpoint. But what about `raw_process_ep_io`? That one seems like the key question. Does *that* function impose similar restrictions? I need to drill down into the logic of that function and how it handles concurrency. Understanding how `raw_process_ep_io` handles concurrent requests is absolutely crucial to getting this right.
|
| 130/4 |
2026/07/29 08:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep_io]
Results: map[SourceCode:1058: static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
1059: void *data, bool in)
1060: {
1061: int ret = 0;
1062: unsigned long flags;
1063: struct raw_ep *ep;
1064: DECLARE_COMPLETION_ONSTACK(done);
1065:
1066: spin_lock_irqsave(&dev->lock, flags);
1067: if (dev->state != STATE_DEV_RUNNING) {
1068: dev_dbg(dev->dev, "fail, device is not running\n");
1069: ret = -EINVAL;
1070: goto out_unlock;
1071: }
1072: if (!dev->gadget) {
1073: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1074: ret = -EBUSY;
1075: goto out_unlock;
1076: }
1077: if (io->ep >= dev->eps_num) {
1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
1079: ret = -EINVAL;
1080: goto out_unlock;
1081: }
1082: ep = &dev->eps[io->ep];
1083: if (ep->state != STATE_EP_ENABLED) {
1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
1085: ret = -EBUSY;
1086: goto out_unlock;
1087: }
1088: if (ep->disabling) {
1089: dev_dbg(&dev->gadget->dev,
1090: "fail, endpoint is already being disabled\n");
1091: ret = -EBUSY;
1092: goto out_unlock;
1093: }
1094: if (ep->urb_queued) {
1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
1096: ret = -EBUSY;
1097: goto out_unlock;
1098: }
1099: if (in != usb_endpoint_dir_in(ep->ep->desc)) {
1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
1101: ret = -EINVAL;
1102: goto out_unlock;
1103: }
1104:
1105: ep->dev = dev;
1106: ep->req->context = &done;
1107: ep->req->complete = gadget_ep_complete;
1108: ep->req->buf = data;
1109: ep->req->length = io->length;
1110: ep->req->zero = usb_raw_io_flags_zero(io->flags);
1111: ep->urb_queued = true;
1112: spin_unlock_irqrestore(&dev->lock, flags);
1113:
1114: ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1115: if (ret) {
1116: dev_err(&dev->gadget->dev,
1117: "fail, usb_ep_queue returned %d\n", ret);
1118: spin_lock_irqsave(&dev->lock, flags);
1119: goto out_queue_failed;
1120: }
1121:
1122: ret = wait_for_completion_interruptible(&done);
1123: if (ret) {
1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1125: usb_ep_dequeue(ep->ep, ep->req);
1126: wait_for_completion(&done);
1127: spin_lock_irqsave(&dev->lock, flags);
1128: if (ep->status == -ECONNRESET)
1129: ep->status = -EINTR;
1130: goto out_interrupted;
1131: }
1132:
1133: spin_lock_irqsave(&dev->lock, flags);
1134:
1135: out_interrupted:
1136: ret = ep->status;
1137: out_queue_failed:
1138: ep->urb_queued = false;
1139: out_unlock:
1140: spin_unlock_irqrestore(&dev->lock, flags);
1141: return ret;
1142: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 131/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 17057
output: 52
thoughts: 78
Thoughts: Okay, here's my understanding, thinking through the potential concurrency issues in this USB driver:
**Concurrency Concerns in USB Endpoint Handling**
Alright, let me break this down. My initial observation is that both `raw_process_ep0_io` and `raw_process_ep_io` functions are designed to call `usb_ep_queue`, and crucially, they do so *after* they've released some underlying lock. That's a potential race condition right there. Anytime we release a lock and then make a call like that, we need to be absolutely sure the shared data that's being accessed is handled appropriately. This really smells like a classic problem.
Then, to add to the complexity, `raw_ioctl_ep_set_clear_halt_wedge` seems to follow a similar pattern, calling `usb_ep_set_halt` or `usb_ep_clear_halt` after it has dropped the lock. Again, we're releasing a potentially critical resource lock and then calling a function that will, in all likelihood, manipulate endpoint state or shared data.
So, in all of these instances, the critical question is: what is the nature of the data/resources that is being protected by the lock? Also, how is the state or behavior guaranteed to be consistent after the lock is released but before the call to the endpoint functions occurs? If the lock prevents other threads/interrupts from corrupting shared data, then these implementations are potentially unsafe. We need to go much deeper on the memory models and locking primitives being used. We need to figure out what happens in those unlocked time windows. This is all very familiar to me, though, and I should have no problem delving into this.
|
| 132/4 |
2026/07/29 08:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_set_clear_halt_wedge]
Results: map[SourceCode: 974: static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
975: unsigned long value, bool set, bool halt)
976: {
977: int ret = 0, i = value;
978: unsigned long flags;
979:
980: spin_lock_irqsave(&dev->lock, flags);
981: if (dev->state != STATE_DEV_RUNNING) {
982: dev_dbg(dev->dev, "fail, device is not running\n");
983: ret = -EINVAL;
984: goto out_unlock;
985: }
986: if (!dev->gadget) {
987: dev_dbg(dev->dev, "fail, gadget is not bound\n");
988: ret = -EBUSY;
989: goto out_unlock;
990: }
991: if (i < 0 || i >= dev->eps_num) {
992: dev_dbg(dev->dev, "fail, invalid endpoint\n");
993: ret = -EBUSY;
994: goto out_unlock;
995: }
996: if (dev->eps[i].state == STATE_EP_DISABLED) {
997: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
998: ret = -EINVAL;
999: goto out_unlock;
1000: }
1001: if (dev->eps[i].disabling) {
1002: dev_dbg(&dev->gadget->dev,
1003: "fail, disable is in progress\n");
1004: ret = -EINVAL;
1005: goto out_unlock;
1006: }
1007: if (dev->eps[i].urb_queued) {
1008: dev_dbg(&dev->gadget->dev,
1009: "fail, waiting for urb completion\n");
1010: ret = -EINVAL;
1011: goto out_unlock;
1012: }
1013: if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
1014: dev_dbg(&dev->gadget->dev,
1015: "fail, can't halt/wedge ISO endpoint\n");
1016: ret = -EINVAL;
1017: goto out_unlock;
1018: }
1019:
1020: if (set && halt) {
1021: ret = usb_ep_set_halt(dev->eps[i].ep);
1022: if (ret < 0)
1023: dev_err(&dev->gadget->dev,
1024: "fail, usb_ep_set_halt returned %d\n", ret);
1025: } else if (!set && halt) {
1026: ret = usb_ep_clear_halt(dev->eps[i].ep);
1027: if (ret < 0)
1028: dev_err(&dev->gadget->dev,
1029: "fail, usb_ep_clear_halt returned %d\n", ret);
1030: } else if (set && !halt) {
1031: ret = usb_ep_set_wedge(dev->eps[i].ep);
1032: if (ret < 0)
1033: dev_err(&dev->gadget->dev,
1034: "fail, usb_ep_set_wedge returned %d\n", ret);
1035: }
1036:
1037: out_unlock:
1038: spin_unlock_irqrestore(&dev->lock, flags);
1039: return ret;
1040: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 133/4 |
2026/07/29 08:25 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18655
output: 46
thoughts: 154
Thoughts: **Endpoint Halt Handling - A Quick Review**
Okay, let me think this through. My first thought was that `usb_ep_set_halt` was being called with `dev->lock` held within the `raw_ioctl_ep_set_clear_halt_wedge` function. A quick double-check... yes, that's exactly what's happening. The function uses `spin_lock_irqsave(&dev->lock, flags);` before calling `usb_ep_set_halt` and then `spin_unlock_irqrestore(&dev->lock, flags);`. So, it *is* maintaining the lock during the call. That means `raw_ioctl_ep_set_clear_halt_wedge` doesn't relinquish the lock while setting or clearing the endpoint halt. Alright, good.
Now, I need to extend this thought process. What about `raw_ioctl_ep_enable` and `raw_ioctl_ep_disable`? We need to verify how these two handle the locking mechanism. This needs a further look.
|
| 134/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_enable]
Results: map[SourceCode: 840: static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
841: {
842: int ret = 0, i;
843: unsigned long flags;
844: struct usb_endpoint_descriptor *desc;
845: struct raw_ep *ep;
846: bool ep_props_matched = false;
847:
848: desc = memdup_user((void __user *)value, sizeof(*desc));
849: if (IS_ERR(desc))
850: return PTR_ERR(desc);
851:
852: /*
853: * Endpoints with a maxpacket length of 0 can cause crashes in UDC
854: * drivers.
855: */
856: if (usb_endpoint_maxp(desc) == 0) {
857: dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
858: kfree(desc);
859: return -EINVAL;
860: }
861:
862: spin_lock_irqsave(&dev->lock, flags);
863: if (dev->state != STATE_DEV_RUNNING) {
864: dev_dbg(dev->dev, "fail, device is not running\n");
865: ret = -EINVAL;
866: goto out_free;
867: }
868: if (!dev->gadget) {
869: dev_dbg(dev->dev, "fail, gadget is not bound\n");
870: ret = -EBUSY;
871: goto out_free;
872: }
873:
874: for (i = 0; i < dev->eps_num; i++) {
875: ep = &dev->eps[i];
876: if (ep->addr != usb_endpoint_num(desc) &&
877: ep->addr != USB_RAW_EP_ADDR_ANY)
878: continue;
879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
880: continue;
881: ep_props_matched = true;
882: if (ep->state != STATE_EP_DISABLED)
883: continue;
884: ep->ep->desc = desc;
885: ret = usb_ep_enable(ep->ep);
886: if (ret < 0) {
887: dev_err(&dev->gadget->dev,
888: "fail, usb_ep_enable returned %d\n", ret);
889: goto out_free;
890: }
891: ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
892: if (!ep->req) {
893: dev_err(&dev->gadget->dev,
894: "fail, usb_ep_alloc_request failed\n");
895: usb_ep_disable(ep->ep);
896: ret = -ENOMEM;
897: goto out_free;
898: }
899: ep->state = STATE_EP_ENABLED;
900: ep->ep->driver_data = ep;
901: ret = i;
902: goto out_unlock;
903: }
904:
905: if (!ep_props_matched) {
906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
907: ret = -EINVAL;
908: } else {
909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
910: ret = -EBUSY;
911: }
912:
913: out_free:
914: kfree(desc);
915: out_unlock:
916: spin_unlock_irqrestore(&dev->lock, flags);
917: return ret;
918: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 135/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 20299
output: 46
thoughts: 31
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating USB Endpoint State Transitions**
Alright, let's see... so `usb_ep_enable` gets called with `dev->lock` held. That makes sense; it implies a critical section, protecting the endpoint's state from concurrent modification during enablement. We need to be mindful of the locking order here to avoid any potential deadlocks, of course. We should also review any potential re-entrancy issues. Is that lock a spinlock, mutex, or something more complex? I need to remember what's at the next level of the code...
Now, the more important question: what about `raw_ioctl_ep_disable`? Does it also play nicely with that lock? Does it get called from an interrupt context, perhaps? And if it does, is the lock appropriately managed in that scenario? The implications are serious: any race conditions involving enable/disable cycles could lead to all sorts of nasty problems: data corruption, endpoint not operating at all or even a kernel crash. I'll need to dig into the call paths of `raw_ioctl_ep_disable` to be sure there's proper synchronization, or maybe even to refactor the design of the locking mechanism. The goal is to ensure this routine is safe, which means I will have to find out if `raw_ioctl_ep_disable` also takes the same lock as `usb_ep_enable` before operating.
|
| 136/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_disable]
Results: map[SourceCode: 920: static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
921: {
922: int ret = 0, i = value;
923: unsigned long flags;
924:
925: spin_lock_irqsave(&dev->lock, flags);
926: if (dev->state != STATE_DEV_RUNNING) {
927: dev_dbg(dev->dev, "fail, device is not running\n");
928: ret = -EINVAL;
929: goto out_unlock;
930: }
931: if (!dev->gadget) {
932: dev_dbg(dev->dev, "fail, gadget is not bound\n");
933: ret = -EBUSY;
934: goto out_unlock;
935: }
936: if (i < 0 || i >= dev->eps_num) {
937: dev_dbg(dev->dev, "fail, invalid endpoint\n");
938: ret = -EBUSY;
939: goto out_unlock;
940: }
941: if (dev->eps[i].state == STATE_EP_DISABLED) {
942: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
943: ret = -EINVAL;
944: goto out_unlock;
945: }
946: if (dev->eps[i].disabling) {
947: dev_dbg(&dev->gadget->dev,
948: "fail, disable already in progress\n");
949: ret = -EINVAL;
950: goto out_unlock;
951: }
952: if (dev->eps[i].urb_queued) {
953: dev_dbg(&dev->gadget->dev,
954: "fail, waiting for urb completion\n");
955: ret = -EINVAL;
956: goto out_unlock;
957: }
958: dev->eps[i].disabling = true;
959: spin_unlock_irqrestore(&dev->lock, flags);
960:
961: usb_ep_disable(dev->eps[i].ep);
962:
963: spin_lock_irqsave(&dev->lock, flags);
964: usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
965: kfree(dev->eps[i].ep->desc);
966: dev->eps[i].state = STATE_EP_DISABLED;
967: dev->eps[i].disabling = false;
968:
969: out_unlock:
970: spin_unlock_irqrestore(&dev->lock, flags);
971: return ret;
972: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 137/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21557
output: 39
thoughts: 106
Thoughts: **Analyzing USB Endpoint Disabling with Lock Handling**
Okay, so I'm looking at this `usb_ep_disable` function and immediately, something jumps out β it's called with the lock already dropped! That's a potential concurrency issue, isn't it? We also see that `usb_ep_queue` operates in this manner.
Alright, so if we use a `udc_usage` counter, and it looks like we *should* to manage things safely here, we absolutely need to be very precise about our locking. We'd have to increment it *before* dropping the lock, to avoid any race conditions, and then decrement it only *after* re-acquiring that lock. No room for error.
Wait a second... didn't I see something about `udc_usage` in the gadgetfs code? Yeah, that's right. Time to check `drivers/usb/gadget/legacy/inode.c`. Need to confirm how `gadgetfs` deals with this. Gotta make sure we're consistent and robust in our approach across the whole system.
|
| 138/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:dev_config]
Results: map[SourceCode:1800: static ssize_t
1801: dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
1802: {
1803: struct dev_data *dev = fd->private_data;
1804: ssize_t value, length = len;
1805: unsigned total;
1806: u32 tag;
1807: char *kbuf;
1808:
1809: spin_lock_irq(&dev->lock);
1810: if (dev->state > STATE_DEV_OPENED) {
1811: value = ep0_write(fd, buf, len, ptr);
1812: spin_unlock_irq(&dev->lock);
1813: return value;
1814: }
1815: spin_unlock_irq(&dev->lock);
1816:
1817: if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
1818: (len > PAGE_SIZE * 4))
1819: return -EINVAL;
1820:
1821: /* we might need to change message format someday */
1822: if (copy_from_user (&tag, buf, 4))
1823: return -EFAULT;
1824: if (tag != 0)
1825: return -EINVAL;
1826: buf += 4;
1827: length -= 4;
1828:
1829: kbuf = memdup_user(buf, length);
1830: if (IS_ERR(kbuf))
1831: return PTR_ERR(kbuf);
1832:
1833: spin_lock_irq (&dev->lock);
1834: value = -EINVAL;
1835: if (dev->buf) {
1836: spin_unlock_irq(&dev->lock);
1837: kfree(kbuf);
1838: return value;
1839: }
1840: dev->buf = kbuf;
1841:
1842: /* full or low speed config */
1843: dev->config = (void *) kbuf;
1844: total = le16_to_cpu(dev->config->wTotalLength);
1845: if (!is_valid_config(dev->config, total) ||
1846: total > length - USB_DT_DEVICE_SIZE)
1847: goto fail;
1848: kbuf += total;
1849: length -= total;
1850:
1851: /* optional high speed config */
1852: if (kbuf [1] == USB_DT_CONFIG) {
1853: dev->hs_config = (void *) kbuf;
1854: total = le16_to_cpu(dev->hs_config->wTotalLength);
1855: if (!is_valid_config(dev->hs_config, total) ||
1856: total > length - USB_DT_DEVICE_SIZE)
1857: goto fail;
1858: kbuf += total;
1859: length -= total;
1860: } else {
1861: dev->hs_config = NULL;
1862: }
1863:
1864: /* could support multiple configs, using another encoding! */
1865:
1866: /* device descriptor (tweaked for paranoia) */
1867: if (length != USB_DT_DEVICE_SIZE)
1868: goto fail;
1869: dev->dev = (void *)kbuf;
1870: if (dev->dev->bLength != USB_DT_DEVICE_SIZE
1871: || dev->dev->bDescriptorType != USB_DT_DEVICE
1872: || dev->dev->bNumConfigurations != 1)
1873: goto fail;
1874: dev->dev->bcdUSB = cpu_to_le16 (0x0200);
1875:
1876: /* triggers gadgetfs_bind(); then we can enumerate. */
1877: spin_unlock_irq (&dev->lock);
1878: if (dev->hs_config)
1879: gadgetfs_driver.max_speed = USB_SPEED_HIGH;
1880: else
1881: gadgetfs_driver.max_speed = USB_SPEED_FULL;
1882:
1883: value = usb_gadget_register_driver(&gadgetfs_driver);
1884: if (value != 0) {
1885: spin_lock_irq(&dev->lock);
1886: goto fail;
1887: } else {
1888: /* at this point "good" hardware has for the first time
1889: * let the USB the host see us. alternatively, if users
1890: * unplug/replug that will clear all the error state.
1891: *
1892: * note: everything running before here was guaranteed
1893: * to choke driver model style diagnostics. from here
1894: * on, they can work ... except in cleanup paths that
1895: * kick in after the ep0 descriptor is closed.
1896: */
1897: value = len;
1898: dev->gadget_registered = true;
1899: }
1900: return value;
1901:
1902: fail:
1903: dev->config = NULL;
1904: dev->hs_config = NULL;
1905: dev->dev = NULL;
1906: spin_unlock_irq (&dev->lock);
1907: pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
1908: kfree (dev->buf);
1909: dev->buf = NULL;
1910: return value;
1911: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 139/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23651
output: 36
thoughts: 26
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating `udc_usage` in the USB Gadget Driver**
Right, let's dive in. I need to understand how `udc_usage` is being handled, so I'll start by focusing on `drivers/usb/gadget/legacy/inode.c`. That's a good starting point, given the legacy nature and inode handling within the USB gadget framework. I expect to find some key interactions there, likely related to device registration, unregistration, or resource management. I'm hoping it's a relatively straightforward use case; sometimes these legacy drivers can be a bitβ¦dense. I'll be looking for where `udc_usage` is incremented, decremented, and where its value is actually *used* to control the behavior of the driver or hardware. Any potential race conditions or locking mechanisms that protect its manipulation will be immediately interesting. My goal is to quickly build a mental map of how `udc_usage` affects the USB gadget driver's state and operation. This should be a good starting point for my investigation.
|
| 140/4 |
2026/07/29 08:26 |
tool |
grepper |
0m
Args: map[Expression:udc_usage PathPrefix:drivers/usb/gadget/legacy/inode.c]
Results: map[Output:drivers/usb/gadget/legacy/inode.c=122=struct dev_data {
--
drivers/usb/gadget/legacy/inode.c-124- refcount_t count;
drivers/usb/gadget/legacy/inode.c:125: int udc_usage;
drivers/usb/gadget/legacy/inode.c-126- enum ep0_state state; /* P: lock */
--
drivers/usb/gadget/legacy/inode.c=913=ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-945- if ((retval = setup_req (ep, req, 0)) == 0) {
drivers/usb/gadget/legacy/inode.c:946: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-947- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-949- spin_lock_irq (&dev->lock);
drivers/usb/gadget/legacy/inode.c:950: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-951- }
--
drivers/usb/gadget/legacy/inode.c-991- len = min (len, (size_t)dev->req->actual);
drivers/usb/gadget/legacy/inode.c:992: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-993- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-998- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:999: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1000- clean_req (dev->gadget->ep0, dev->req);
--
drivers/usb/gadget/legacy/inode.c=1125=ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
--
drivers/usb/gadget/legacy/inode.c-1142- dev->state = STATE_DEV_CONNECTED;
drivers/usb/gadget/legacy/inode.c:1143: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1144- spin_unlock_irq (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1154- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1155: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1156- if (retval < 0) {
--
drivers/usb/gadget/legacy/inode.c=1251=static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value)
--
drivers/usb/gadget/legacy/inode.c-1261- } else if (gadget->ops->ioctl) {
drivers/usb/gadget/legacy/inode.c:1262: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1263- spin_unlock_irq(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1267- spin_lock_irq(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1268: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1269- }
--
drivers/usb/gadget/legacy/inode.c=1333=gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
--
drivers/usb/gadget/legacy/inode.c-1497-
drivers/usb/gadget/legacy/inode.c:1498: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1499- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1502- spin_lock (&dev->lock);
drivers/usb/gadget/legacy/inode.c:1503: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1504- if (value < 0) {
--
drivers/usb/gadget/legacy/inode.c-1535-
drivers/usb/gadget/legacy/inode.c:1536: ++dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1537- spin_unlock (&dev->lock);
--
drivers/usb/gadget/legacy/inode.c-1539- spin_lock(&dev->lock);
drivers/usb/gadget/legacy/inode.c:1540: --dev->udc_usage;
drivers/usb/gadget/legacy/inode.c-1541- spin_unlock(&dev->lock);
--
drivers/usb/gadget/legacy/inode.c=1640=gadgetfs_unbind (struct usb_gadget *gadget)
--
drivers/usb/gadget/legacy/inode.c-1647- dev->state = STATE_DEV_UNBOUND;
drivers/usb/gadget/legacy/inode.c:1648: while (dev->udc_usage > 0) {
drivers/usb/gadget/legacy/inode.c-1649- spin_unlock_irq(&dev->lock);
]
|
| 141/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25445
output: 35
thoughts: 397
Thoughts: **Reflecting on GadgetFS Synchronization**
Okay, so I'm thinking about how `gadgetfs` handles USB device controller (UDC) usage and unbinding, and the current approach is using `udc_usage` and polling. The code suggests a better way, which is to leverage `wait_queue_head_t` or a `completion`.
Now, if I go with the `completion` approach, where does it get initialized? It makes the most sense to initialize it during either `dev_new` or `gadget_bind`, since `gadget_unbind` is only called once. But then I'm wondering, when do I complete it? Do I complete it when `udc_usage` drops to zero? The problem with that is that `udc_usage` might drop to zero multiple times during the device's lifetime.
The `wait_queue_head_t` alternative seems simpler. I could use `wait_event_lock_irq(dev->wait, dev->udc_usage == 0, dev->lock);` and then, whenever `udc_usage` is decremented, I could check if it has reached zero and call `wake_up(&dev->wait);`. But then I have to think about the lock: is `wake_up` lock-safe? Seems like it is. Wait, `wait_event_lock_irq` expects the lock to be held. Hold on, `wait_event_lock_irq` drops the lock while waiting and reacquires it when it wakes. Alright, that sounds good. Now, let me just quickly check that `wait_event_lock_irq` is indeed available for use.
|
| 142/4 |
2026/07/29 08:26 |
tool |
grepper |
0m
Args: map[Expression:wait_event_lock_irq PathPrefix:include/linux/wait.h]
Results: map[Output:include/linux/wait.h=777=extern int do_wait_intr_irq(wait_queue_head_t *, wait_queue_entry_t *);
--
include/linux/wait.h-1032-
include/linux/wait.h:1033:#define __wait_event_lock_irq(wq_head, condition, lock, cmd) \
include/linux/wait.h-1034- (void)___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0, \
--
include/linux/wait.h-1040-/**
include/linux/wait.h:1041: * wait_event_lock_irq_cmd - sleep until a condition gets true. The
include/linux/wait.h-1042- * condition is checked under the lock. This
--
include/linux/wait.h-1062- */
include/linux/wait.h:1063:#define wait_event_lock_irq_cmd(wq_head, condition, lock, cmd) \
include/linux/wait.h-1064-do { \
--
include/linux/wait.h-1066- break; \
include/linux/wait.h:1067: __wait_event_lock_irq(wq_head, condition, lock, cmd); \
include/linux/wait.h-1068-} while (0)
--
include/linux/wait.h-1070-/**
include/linux/wait.h:1071: * wait_event_lock_irq - sleep until a condition gets true. The
include/linux/wait.h-1072- * condition is checked under the lock. This
--
include/linux/wait.h-1089- */
include/linux/wait.h:1090:#define wait_event_lock_irq(wq_head, condition, lock) \
include/linux/wait.h-1091-do { \
--
include/linux/wait.h-1093- break; \
include/linux/wait.h:1094: __wait_event_lock_irq(wq_head, condition, lock, ); \
include/linux/wait.h-1095-} while (0)
--
include/linux/wait.h-1169-
include/linux/wait.h:1170:#define __wait_event_lock_irq_timeout(wq_head, condition, lock, timeout, state) \
include/linux/wait.h-1171- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
--
include/linux/wait.h-1205- if (!___wait_cond_timeout(condition)) \
include/linux/wait.h:1206: __ret = __wait_event_lock_irq_timeout( \
include/linux/wait.h-1207- wq_head, condition, lock, timeout, \
--
include/linux/wait.h-1211-
include/linux/wait.h:1212:#define wait_event_lock_irq_timeout(wq_head, condition, lock, timeout) \
include/linux/wait.h-1213-({ \
--
include/linux/wait.h-1215- if (!___wait_cond_timeout(condition)) \
include/linux/wait.h:1216: __ret = __wait_event_lock_irq_timeout( \
include/linux/wait.h-1217- wq_head, condition, lock, timeout, \
]
|
| 143/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 27176
output: 47
thoughts: 1425
Thoughts: **Refining the Unbinding Logic**
Okay, so I'm thinking about how to properly synchronize the unbinding process in this raw USB gadget driver. My goal is to ensure the device is completely idle before `gadget_unbind` finishes.
First thought, is `wait_event_lock_irq` available? Yes, it is! But wait, `completion` seems simpler. I can use `completion` and not worry about initializing a wait queue head. A `completion` object could be initialized in `dev_new` or `gadget_bind` and wait for it if `udc_usage > 0`. Wait, what about the existing `ep0_done` in `raw_dev`? No, that's used for `raw_process_ep0_io`. I don't want to reuse that.
So, maybe I add a `struct completion unbind_done;` to `struct raw_dev`? But then, how to determine when to trigger the completion?
If I use `udc_usage`, I can decrement it, and if it reaches zero, signal the completion. But wait, no, I need a separate completion for this. I've thought of adding `wait_queue_head_t wait;` to `struct raw_dev`. But, `struct completion` looks easier to use.
Let me think⦠I've gone back and forth on this and realized I need a way to properly signal when the device is idle, only if it's currently in use (`udc_usage > 0`) during the unbinding process. I could add a `bool unbinding` variable protected by a lock to manage the unbinding process.
If I use `completion`, I can add:
```c
struct completion *unbind_completion;
```
Then in `gadget_unbind` I can do:
```c
DECLARE_COMPLETION_ONSTACK(done);
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->state = STATE_DEV_FAILED;
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
spin_unlock_irqrestore(&dev->lock, flags);
```
And in the places where I decrement `udc_usage`:
```c
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (dev->udc_usage == 0 && dev->unbind_completion)
complete(dev->unbind_completion);
spin_unlock_irqrestore(&dev->lock, flags);
```
This is pretty clean β I don't need a `wait_queue_head_t`. I only need a pointer to a completion, which will be stack allocated for the duration of the `gadget_unbind` call!
But⦠wait a second⦠what if `udc_usage` drops to zero *after* I signal completion, and then *another* ioctl increments it? Ah! No problem. If I set `dev->state = STATE_DEV_FAILED` and `dev->gadget = NULL` *before* checking `udc_usage` in `gadget_unbind`, any *new* ioctls will fail because they'll check those states! The states prevent new uses, and the completion handles the existing ones!
I need to confirm this assumption, but it looks like I've found a good solution. This is elegant and properly synchronizes unbinding by waiting for any active usage counts to go to zero before returning.
|
| 144/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep0_io]
Results: map[SourceCode: 683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 145/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 30709
output: 46
thoughts: 111
Thoughts: **Analyzing the USB Gadget State Transitions**
Okay, so I'm digging into this code and trying to get a handle on the device state transitions during gadget unbinding. I see that the `raw_process_ep0_io` function, which is the initial entry point for I/O on endpoint 0, is properly checking two crucial conditions *before* proceeding: `dev->state != STATE_DEV_RUNNING` and `!dev->gadget`. It's doing this while holding `dev->lock`, which is good, as it gives an element of atomicity.
So, the key here is that if `gadget_unbind` is called and updates the device's state to `STATE_DEV_FAILED` and sets `dev->gadget` to `NULL` *while the lock is held*, then any future ioctl calls should fail those checks I just mentioned. This looks correct so far; any attempted I/O on the device after unbind should be blocked from using the underlying UDC resources. I don't see any obvious race conditions.
But wait a minute... I can't be too hasty. I'm forgetting about `raw_process_ep_io`. I need to ensure that it has the same or similar protections, as any race condition in the `raw_process_ep_io` could leave the device in an unexpected state. I need to examine its locking and state-checking logic.
|
| 146/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep_io]
Results: map[SourceCode:1058: static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
1059: void *data, bool in)
1060: {
1061: int ret = 0;
1062: unsigned long flags;
1063: struct raw_ep *ep;
1064: DECLARE_COMPLETION_ONSTACK(done);
1065:
1066: spin_lock_irqsave(&dev->lock, flags);
1067: if (dev->state != STATE_DEV_RUNNING) {
1068: dev_dbg(dev->dev, "fail, device is not running\n");
1069: ret = -EINVAL;
1070: goto out_unlock;
1071: }
1072: if (!dev->gadget) {
1073: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1074: ret = -EBUSY;
1075: goto out_unlock;
1076: }
1077: if (io->ep >= dev->eps_num) {
1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
1079: ret = -EINVAL;
1080: goto out_unlock;
1081: }
1082: ep = &dev->eps[io->ep];
1083: if (ep->state != STATE_EP_ENABLED) {
1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
1085: ret = -EBUSY;
1086: goto out_unlock;
1087: }
1088: if (ep->disabling) {
1089: dev_dbg(&dev->gadget->dev,
1090: "fail, endpoint is already being disabled\n");
1091: ret = -EBUSY;
1092: goto out_unlock;
1093: }
1094: if (ep->urb_queued) {
1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
1096: ret = -EBUSY;
1097: goto out_unlock;
1098: }
1099: if (in != usb_endpoint_dir_in(ep->ep->desc)) {
1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
1101: ret = -EINVAL;
1102: goto out_unlock;
1103: }
1104:
1105: ep->dev = dev;
1106: ep->req->context = &done;
1107: ep->req->complete = gadget_ep_complete;
1108: ep->req->buf = data;
1109: ep->req->length = io->length;
1110: ep->req->zero = usb_raw_io_flags_zero(io->flags);
1111: ep->urb_queued = true;
1112: spin_unlock_irqrestore(&dev->lock, flags);
1113:
1114: ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1115: if (ret) {
1116: dev_err(&dev->gadget->dev,
1117: "fail, usb_ep_queue returned %d\n", ret);
1118: spin_lock_irqsave(&dev->lock, flags);
1119: goto out_queue_failed;
1120: }
1121:
1122: ret = wait_for_completion_interruptible(&done);
1123: if (ret) {
1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1125: usb_ep_dequeue(ep->ep, ep->req);
1126: wait_for_completion(&done);
1127: spin_lock_irqsave(&dev->lock, flags);
1128: if (ep->status == -ECONNRESET)
1129: ep->status = -EINTR;
1130: goto out_interrupted;
1131: }
1132:
1133: spin_lock_irqsave(&dev->lock, flags);
1134:
1135: out_interrupted:
1136: ret = ep->status;
1137: out_queue_failed:
1138: ep->urb_queued = false;
1139: out_unlock:
1140: spin_unlock_irqrestore(&dev->lock, flags);
1141: return ret;
1142: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 147/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 32520
output: 46
thoughts: 68
Thoughts: Okay, let me break this down.
**Analyzing the USB Endpoint State Transitions**
Alright, so I'm digging into this `raw_process_ep_io` function, and it seems to be doing some crucial checks. It's making sure the device state isn't already `STATE_DEV_RUNNING`, and it also verifies the `dev->gadget` pointer isn't null. The interesting part is that these checks are happening while holding `dev->lock`. That's standard practice to avoid race conditions when working with shared device resources, I understand.
But... hang on. I need to think about this a bit more. What about the related `raw_ioctl_ep_disable` function? Ah, yes! It calls `usb_ep_disable`. The crucial point there is that the `dev->lock` is dropped before calling `usb_ep_disable`. That's a potential area for race conditions. We need to analyze that path carefully because `usb_ep_disable` could be interacting with the endpoint state concurrently with other threads or interrupt handlers. It would mean that there could be inconsistencies and data corruption.
|
| 148/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_disable]
Results: map[SourceCode: 920: static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
921: {
922: int ret = 0, i = value;
923: unsigned long flags;
924:
925: spin_lock_irqsave(&dev->lock, flags);
926: if (dev->state != STATE_DEV_RUNNING) {
927: dev_dbg(dev->dev, "fail, device is not running\n");
928: ret = -EINVAL;
929: goto out_unlock;
930: }
931: if (!dev->gadget) {
932: dev_dbg(dev->dev, "fail, gadget is not bound\n");
933: ret = -EBUSY;
934: goto out_unlock;
935: }
936: if (i < 0 || i >= dev->eps_num) {
937: dev_dbg(dev->dev, "fail, invalid endpoint\n");
938: ret = -EBUSY;
939: goto out_unlock;
940: }
941: if (dev->eps[i].state == STATE_EP_DISABLED) {
942: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
943: ret = -EINVAL;
944: goto out_unlock;
945: }
946: if (dev->eps[i].disabling) {
947: dev_dbg(&dev->gadget->dev,
948: "fail, disable already in progress\n");
949: ret = -EINVAL;
950: goto out_unlock;
951: }
952: if (dev->eps[i].urb_queued) {
953: dev_dbg(&dev->gadget->dev,
954: "fail, waiting for urb completion\n");
955: ret = -EINVAL;
956: goto out_unlock;
957: }
958: dev->eps[i].disabling = true;
959: spin_unlock_irqrestore(&dev->lock, flags);
960:
961: usb_ep_disable(dev->eps[i].ep);
962:
963: spin_lock_irqsave(&dev->lock, flags);
964: usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
965: kfree(dev->eps[i].ep->desc);
966: dev->eps[i].state = STATE_EP_DISABLED;
967: dev->eps[i].disabling = false;
968:
969: out_unlock:
970: spin_unlock_irqrestore(&dev->lock, flags);
971: return ret;
972: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 149/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 33739
output: 46
thoughts: 122
Thoughts: Okay, let me think this through.
**Analyzing the USB Device State Management**
Alright, I'm digging into this code, specifically around the USB device's state transitions, and the use of the `dev->lock`. I see a check: `dev->state != STATE_DEV_RUNNING` *and* `!dev->gadget`, all within the scope of the `dev->lock`. This seems important for safeguarding the device's operational state. Now, I'm seeing `udc_usage` and how it relates to this. If `udc_usage` is tracking something like the number of users or accesses, I'll need to make sure it's incremented and decremented correctly. I see it referenced in `raw_process_ep0_io`, `raw_process_ep_io`, and `raw_ioctl_ep_disable`.
Hmm, but what about `raw_ioctl_ep_enable`? That's the key. Does it correctly account for any usage increment? Ah, here's the crucial point: it doesn't drop the lock *before* calling `usb_ep_enable`. Let me just double-check that `raw_ioctl_ep_enable` function one more time to be absolutely sure I'm not missing a crucial piece of the puzzle here! This is where race conditions can sneak in.
|
| 150/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_enable]
Results: map[SourceCode: 840: static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
841: {
842: int ret = 0, i;
843: unsigned long flags;
844: struct usb_endpoint_descriptor *desc;
845: struct raw_ep *ep;
846: bool ep_props_matched = false;
847:
848: desc = memdup_user((void __user *)value, sizeof(*desc));
849: if (IS_ERR(desc))
850: return PTR_ERR(desc);
851:
852: /*
853: * Endpoints with a maxpacket length of 0 can cause crashes in UDC
854: * drivers.
855: */
856: if (usb_endpoint_maxp(desc) == 0) {
857: dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
858: kfree(desc);
859: return -EINVAL;
860: }
861:
862: spin_lock_irqsave(&dev->lock, flags);
863: if (dev->state != STATE_DEV_RUNNING) {
864: dev_dbg(dev->dev, "fail, device is not running\n");
865: ret = -EINVAL;
866: goto out_free;
867: }
868: if (!dev->gadget) {
869: dev_dbg(dev->dev, "fail, gadget is not bound\n");
870: ret = -EBUSY;
871: goto out_free;
872: }
873:
874: for (i = 0; i < dev->eps_num; i++) {
875: ep = &dev->eps[i];
876: if (ep->addr != usb_endpoint_num(desc) &&
877: ep->addr != USB_RAW_EP_ADDR_ANY)
878: continue;
879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
880: continue;
881: ep_props_matched = true;
882: if (ep->state != STATE_EP_DISABLED)
883: continue;
884: ep->ep->desc = desc;
885: ret = usb_ep_enable(ep->ep);
886: if (ret < 0) {
887: dev_err(&dev->gadget->dev,
888: "fail, usb_ep_enable returned %d\n", ret);
889: goto out_free;
890: }
891: ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
892: if (!ep->req) {
893: dev_err(&dev->gadget->dev,
894: "fail, usb_ep_alloc_request failed\n");
895: usb_ep_disable(ep->ep);
896: ret = -ENOMEM;
897: goto out_free;
898: }
899: ep->state = STATE_EP_ENABLED;
900: ep->ep->driver_data = ep;
901: ret = i;
902: goto out_unlock;
903: }
904:
905: if (!ep_props_matched) {
906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
907: ret = -EINVAL;
908: } else {
909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
910: ret = -EBUSY;
911: }
912:
913: out_free:
914: kfree(desc);
915: out_unlock:
916: spin_unlock_irqrestore(&dev->lock, flags);
917: return ret;
918: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 151/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 35416
output: 47
thoughts: 175
Thoughts: **Analyzing USB Endpoint Locking and Concurrency**
Okay, let me think this through. `raw_ioctl_ep_enable` calls `usb_ep_enable`, and I see that it holds `dev->lock` when it makes that call. But doesn't `usb_ep_enable` have the *potential* to sleep? That's not ideal, it really shouldn't. However, if it *doesn't* drop the lock, then `gadget_unbind` is going to be blocked waiting for that lock. Specifically, `gadget_unbind` will get stuck at `spin_lock_irqsave(&dev->lock, flags);` until `raw_ioctl_ep_enable` releases the spinlock. Ah, right. This means we *don't* need `udc_usage` for `raw_ioctl_ep_enable` because the lock is already handling the concurrency issues we're concerned about. The unbind will wait until enable is done.
Hold on, what about `raw_ioctl_ep_set_clear_halt_wedge`? Does that also neglect to release the lock within its processing? We need to keep this consistent.
And `raw_ioctl_vbus_draw`? I need to analyze how it handles the lock too.
|
| 152/4 |
2026/07/29 08:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_vbus_draw]
Results: map[SourceCode:1207: static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
1208: {
1209: int ret = 0;
1210: unsigned long flags;
1211:
1212: spin_lock_irqsave(&dev->lock, flags);
1213: if (dev->state != STATE_DEV_RUNNING) {
1214: dev_dbg(dev->dev, "fail, device is not running\n");
1215: ret = -EINVAL;
1216: goto out_unlock;
1217: }
1218: if (!dev->gadget) {
1219: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1220: ret = -EBUSY;
1221: goto out_unlock;
1222: }
1223: usb_gadget_vbus_draw(dev->gadget, 2 * value);
1224:
1225: out_unlock:
1226: spin_unlock_irqrestore(&dev->lock, flags);
1227: return ret;
1228: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 153/4 |
2026/07/29 08:26 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 36278
output: 44
thoughts: 20
Thoughts: Here's my attempt at summarizing those thoughts, tailored for an expert audience and written in the first person:
**Kernel Configuration Troubleshooting**
Okay, so the current approach isn't working as expected β the lock isn't being dropped. That's the primary issue to address. I need to understand *why* it's not releasing. What's causing the hold? Are there race conditions I'm not seeing? Debugging this will be paramount.
Wait... could `raw_ioctl_configure` be involved here? It's a potential alternative configuration mechanism and I should consider if a different approach to ioctl is more efficient in managing the lock's operation. Perhaps using it might help circumvent the issue with the current implementation, or provide a clearer point of interaction. I need to thoroughly investigate the differences between the current IOCTL and `raw_ioctl_configure`, particularly in relation to lock management. Is this relevant? I'll need to research that.
|
| 154/4 |
2026/07/29 08:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_configure]
Results: map[SourceCode:1182: static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
1183: {
1184: int ret = 0;
1185: unsigned long flags;
1186:
1187: if (value)
1188: return -EINVAL;
1189: spin_lock_irqsave(&dev->lock, flags);
1190: if (dev->state != STATE_DEV_RUNNING) {
1191: dev_dbg(dev->dev, "fail, device is not running\n");
1192: ret = -EINVAL;
1193: goto out_unlock;
1194: }
1195: if (!dev->gadget) {
1196: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1197: ret = -EBUSY;
1198: goto out_unlock;
1199: }
1200: usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
1201:
1202: out_unlock:
1203: spin_unlock_irqrestore(&dev->lock, flags);
1204: return ret;
1205: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 155/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 36929
output: 44
thoughts: 66
Thoughts: Okay, let's break this down.
**Lock Dropping and UDC Function Calls: A Quick Review**
Right, so I'm tracing the lock behavior here. It looks like the system *doesn't* automatically drop the lock under certain conditions. That means I need to narrow down the specific code paths where the lock is explicitly released *before* calling User-Defined Code (UDC) functions. Based on the analysis I've done so far, the functions that seem to do this are `raw_process_ep0_io`, `raw_process_ep_io`, and `raw_ioctl_ep_disable`. Butβ¦hold on a second. There's something nagging at me. I'm just quickly confirming if I've reviewed all the calling functions. Did I miss anything? What about `raw_ioctl_run`? I need to double-check that. It's crucial to be absolutely certain I've got the complete picture of all the functions that might have this lock-dropping behavior. I need to fully understand all the lock releasing operations to ensure the underlying processes are safe.
|
| 156/4 |
2026/07/29 08:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_run]
Results: map[SourceCode: 579: static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
580: {
581: int ret = 0;
582: unsigned long flags;
583:
584: if (value)
585: return -EINVAL;
586:
587: spin_lock_irqsave(&dev->lock, flags);
588: if (dev->state != STATE_DEV_INITIALIZED) {
589: dev_dbg(dev->dev, "fail, device is not initialized\n");
590: ret = -EINVAL;
591: goto out_unlock;
592: }
593: dev->state = STATE_DEV_REGISTERING;
594: spin_unlock_irqrestore(&dev->lock, flags);
595:
596: ret = usb_gadget_register_driver(&dev->driver);
597:
598: spin_lock_irqsave(&dev->lock, flags);
599: if (ret) {
600: dev_err(dev->dev,
601: "fail, usb_gadget_register_driver returned %d\n", ret);
602: dev->state = STATE_DEV_FAILED;
603: goto out_unlock;
604: }
605: dev->gadget_registered = true;
606: dev->state = STATE_DEV_RUNNING;
607: /* Matches kref_put() in raw_release(). */
608: kref_get(&dev->count);
609:
610: out_unlock:
611: spin_unlock_irqrestore(&dev->lock, flags);
612: return ret;
613: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 157/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 37820
output: 157
thoughts: 97
Thoughts: **My Analysis of USB Gadget Driver Registration**
Okay, so I'm looking at how this `raw_ioctl_run` function handles the USB gadget driver registration, and there's a key optimization here. I see that it drops the lock *before* calling `usb_gadget_register_driver`. That's smart. The trick is, the gadget isn't bound at this point, which means there's no need to worry about `gadget_unbind` just yet. We don't have a device bound.
So, logically, I can narrow down where I *need* to be careful about concurrent access. It seems that the real area of concern for protecting against race conditions is within `raw_process_ep0_io`, `raw_process_ep_io`, and `raw_ioctl_ep_disable`. These are the functions that are likely to interact with the gadget state after registration. The trick here is how to track gadget usage.
Adding a counter such as `udc_usage` to the `struct raw_dev` seems like a good method to prevent concurrent issues. I think this will provide an effective way to manage and track UDC (USB Device Controller) usage and avoid any potential race conditions during the gadget's lifetime.
|
| 158/4 |
2026/07/29 08:27 |
tool |
codeeditor |
0m
Args: map[CurrentCode: /* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
NewCode: /* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
int udc_usage;
struct completion *unbind_completion;
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 159/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 38349
output: 43
thoughts: 11
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience and presented in the first person:
**Refining the Unbinding Mechanism**
Alright, time to get back to the code and specifically, `gadget_unbind`. I know this function inside and out, but it's always worth a fresh look. I need to make sure this critical unbinding process is as robust and efficient as possible. Considering the complexities of the system architecture, any subtle flaw here could create cascading issues elsewhere. Let's see... what's the latest feedback from testing, and are there any recent changes in dependent modules that might necessitate an adjustment here? My gut tells me a small tweak could yield significant gains in performance and stability β perhaps a refined locking strategy or a more precise resource release sequence. Time to dive back in and meticulously review every line. There's always room for improvement, and in a system like this, perfection is an ongoing pursuit.
|
| 160/4 |
2026/07/29 08:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:gadget_unbind]
Results: map[SourceCode: 328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 161/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 38766
output: 351
thoughts: 23
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Reflecting on UDC Usage and Unbinding a Gadget**
Okay, so I'm thinking about the `gadget_unbind` function. Something just hit me. Shouldn't it be designed to explicitly wait for `udc_usage` to hit zero *before* actually unbinding the gadget? Currently, I'm not seeing that level of synchronization. It seems like we might be creating a race condition if the UDC (USB Device Controller) is still actively processing data when we try to unbind. A spike in traffic and we're going to see a lockup for sure. We need to implement a mechanism for a clean unbind process. I need to make a note to myself to add this logic for proper device unbinding.
|
| 162/4 |
2026/07/29 08:27 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
NewCode:static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->state = STATE_DEV_FAILED;
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
spin_unlock_irqrestore(&dev->lock, flags);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 163/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 39322
output: 47
thoughts: 15
Thoughts: Okay, let's break this down. Here's how I'd approach summarizing the prompt, keeping in mind I'm dealing with someone who understands these technical nuances:
**Updating the `raw_process_ep0_io` Function**
Alright, so the task at hand is to update the `raw_process_ep0_io` function. This likely implies we're dealing with something related to raw data processing, potentially within a larger system or workflow, and specifically concerning an endpoint, most likely endpoint 0 (EP0), which is common in USB communications for control transfers. "IO" suggests it is related to input/output operations. This also suggests the function likely handles the initial communication setup and control signaling. The name `raw_process` implies low-level data manipulation. So, this update could be driven by changes in data formats, new protocol requirements, bug fixes, performance improvements, or even security enhancements. The important part is that I should consider what `raw_process_ep0_io` is doing and why.
|
| 164/4 |
2026/07/29 08:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep0_io]
Results: map[SourceCode: 683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 165/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40816
output: 34
thoughts: 464
Thoughts: **Analyzing UDC Usage and Gadget Unbinding**
Okay, so I'm thinking through this `usb_ep_queue` call. It's lockless, right? My initial thought was to increment `udc_usage` before releasing the lock and decrement it after re-acquiring it. But then I remembered that `usb_ep_queue` happens, and we wait for completion via `wait_for_completion_interruptible(&dev->ep0_done)`. If I decrement `udc_usage` immediately after `usb_ep_queue`, then `gadget_unbind` could potentially swoop in and free the gadget while we're still waiting or even during a call to `usb_ep_dequeue`!
Hang on, `usb_ep_dequeue` is also a UDC function! It also uses `dev->gadget->ep0`. Okay, so the core problem is that `udc_usage` *must* stay incremented until we're completely done with all UDC calls and we no longer need the gadget pointers.
Wait a second. If I hold `udc_usage` incremented while waiting for completion, `gadget_unbind` will block until the IO finishes, right? Yeah, that makes sense. `gadget_unbind` needs to wait for all pending IOs to either complete or get cancelled. However, this presents a problem. If `gadget_unbind` is waiting for `udc_usage` to reach zero, and `udc_usage` is held by a thread that's waiting on `dev->ep0_done`, who will actually trigger that completion?
Ah, right, the UDC driver. It'll complete the request when it's unbound. Typically, when the gadget is unbound, the UDC driver will cancel all pending requests and call their completion callbacks with `-ESHUTDOWN`. So, the completion *will* be signaled by the UDC driver!
But wait, if `gadget_unbind` is blocked waiting for `udc_usage` to drop to zero, does that somehow prevent the UDC driver from properly canceling the requests? I need to check how UDC drivers handle the unbind process. `usb_gadget_remove_driver`, which calls `gadget_unbind`, is usually called by the UDC core. Does the UDC core make sure to cancel requests *before* it calls `gadget_unbind`? I should probably examine `dummy_udc` to see how it works.
|
| 166/4 |
2026/07/29 08:27 |
tool |
grepper |
0m
Args: map[Expression:gadget_unbind PathPrefix:drivers/usb/gadget/udc/]
Results: map[Output:drivers/usb/gadget/udc/core.c=1641=static int gadget_bind_driver(struct device *dev)
--
drivers/usb/gadget/udc/core.c-1706-
drivers/usb/gadget/udc/core.c:1707:static void gadget_unbind_driver(struct device *dev)
drivers/usb/gadget/udc/core.c-1708-{
--
drivers/usb/gadget/udc/core.c=1951=static const struct bus_type gadget_bus_type = {
--
drivers/usb/gadget/udc/core.c-1953- .probe = gadget_bind_driver,
drivers/usb/gadget/udc/core.c:1954: .remove = gadget_unbind_driver,
drivers/usb/gadget/udc/core.c-1955- .match = gadget_match_driver,
--
drivers/usb/gadget/udc/dummy_hcd.c=950=static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable)
--
drivers/usb/gadget/udc/dummy_hcd.c-961- * just like the call to synchronize_irq() in
drivers/usb/gadget/udc/dummy_hcd.c:962: * gadget/udc/core:gadget_unbind_driver().
drivers/usb/gadget/udc/dummy_hcd.c-963- */
]
|
| 167/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 42244
output: 43
thoughts: 28
Thoughts: Here's my take on the thought process, as if it were my own:
**Investigating `gadget_unbind_driver` in `udc/core.c`**
Okay, time to dive in. I need to refresh my memory on how USB gadget drivers are unbinding, so the first thing I'm going to do is zero in on the `gadget_unbind_driver` function. The logical place to start is `drivers/usb/gadget/udc/core.c`, as this is a central piece of the UDC (USB Device Controller) framework. I expect to find the core logic for detaching a driver from a USB gadget there. This likely involves some cleanup of resources, signaling to the device or controller that the driver is gone, and potentially a graceful handling of any ongoing transfers. It's also likely this will be part of a bigger unbind process, perhaps coordinated by the core USB subsystem or the gadget driver itself. This should shed some light on what kind of cleanup happens on the gadget side.
|
| 168/4 |
2026/07/29 08:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:gadget_unbind_driver]
Results: map[SourceCode:1707: static void gadget_unbind_driver(struct device *dev)
1708: {
1709: struct usb_gadget *gadget = dev_to_usb_gadget(dev);
1710: struct usb_udc *udc = gadget->udc;
1711: struct usb_gadget_driver *driver = udc->driver;
1712:
1713: dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
1714:
1715: udc->allow_connect = false;
1716: cancel_work_sync(&udc->vbus_work);
1717: mutex_lock(&udc->connect_lock);
1718: usb_gadget_disconnect_locked(gadget);
1719: usb_gadget_disable_async_callbacks(udc);
1720: if (gadget->irq)
1721: synchronize_irq(gadget->irq);
1722: mutex_unlock(&udc->connect_lock);
1723:
1724: udc->driver->unbind(gadget);
1725:
1726: mutex_lock(&udc->connect_lock);
1727: usb_gadget_udc_stop_locked(udc);
1728: mutex_unlock(&udc->connect_lock);
1729:
1730: mutex_lock(&udc_lock);
1731: driver->is_bound = false;
1732: udc->driver = NULL;
1733: mutex_unlock(&udc_lock);
1734:
1735: kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
1736: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 169/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 43057
output: 40
thoughts: 401
Thoughts: **Analyzing a Potential USB Gadget Driver Deadlock**
Okay, so I'm thinking through this USB gadget disconnection sequence, and I'm seeing a potential race condition that could lead to a serious deadlock. The core of the problem seems to be the order of operations between disconnecting the gadget and unbinding the driver. Specifically, `usb_gadget_disconnect_locked` is called *before* `unbind`, which seems sensible enough, but the details are starting to nag at me.
First, I need to confirm whether `usb_gadget_disconnect_locked` handles request cancellation. It calls `udc->gadget->ops->pullup(udc->gadget, 0)`. That call usually tells the controller to stop. Most UDC's will cancel outstanding requests with that. So far, so good, but what if `usb_ep_queue` gets called *between* `usb_gadget_disconnect_locked` and `unbind`?
If `usb_ep_queue` gets called in this window, does it just error out, or does it queue the request? If it queues the request, that could be a problem. My initial thought is the request should be cancelled when `usb_gadget_udc_stop_locked` is called, but, wait, there's another point.
Here's the real problem: `gadget_unbind` potentially blocks, waiting for `udc_usage` to go to zero. And `udc_usage` is held by a thread waiting for `dev->ep0_done`. So if a request *is* queued and the `gadget_unbind` call blocks, then it is never going to drop `udc_usage` so the stop function will never run! So we could have a request queued but not completed, which blocks the unbind, and prevents the stop call. Which means the request will never be handled leading to a deadlock.
So, let me rephrase that: if `usb_ep_queue` is called *after* `usb_gadget_disconnect_locked`, but *before* `unbind`, and the request gets queued, then `gadget_unbind` might block because of the pending request. Because `gadget_unbind` blocks, `usb_gadget_udc_stop_locked` is *never* called, and that queued request might never be completed. And that's a classic deadlock.
However, if `usb_ep_queue` was called *before* `usb_gadget_disconnect_locked`, then it *should* be cancelled by that disconnect call, because that call pulls down the pullup. But, if `usb_ep_queue` is called *after* the disconnect, but *before* the unbind, there's a serious problem.
I need to see how something like `gadgetfs` deals with this kind of race. I should look at their code as a starting point.
|
| 170/4 |
2026/07/29 08:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:ep0_read]
Results: map[SourceCode: 912: static ssize_t
913: ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
914: {
915: struct dev_data *dev = fd->private_data;
916: ssize_t retval;
917: enum ep0_state state;
918:
919: spin_lock_irq (&dev->lock);
920: if (dev->state <= STATE_DEV_OPENED) {
921: retval = -EINVAL;
922: goto done;
923: }
924:
925: /* report fd mode change before acting on it */
926: if (dev->setup_abort) {
927: dev->setup_abort = 0;
928: retval = -EIDRM;
929: goto done;
930: }
931:
932: /* control DATA stage */
933: if ((state = dev->state) == STATE_DEV_SETUP) {
934:
935: if (dev->setup_in) { /* stall IN */
936: VDEBUG(dev, "ep0in stall\n");
937: (void) usb_ep_set_halt (dev->gadget->ep0);
938: retval = -EL2HLT;
939: dev->state = STATE_DEV_CONNECTED;
940:
941: } else if (len == 0) { /* ack SET_CONFIGURATION etc */
942: struct usb_ep *ep = dev->gadget->ep0;
943: struct usb_request *req = dev->req;
944:
945: if ((retval = setup_req (ep, req, 0)) == 0) {
946: ++dev->udc_usage;
947: spin_unlock_irq (&dev->lock);
948: retval = usb_ep_queue (ep, req, GFP_KERNEL);
949: spin_lock_irq (&dev->lock);
950: --dev->udc_usage;
951: }
952: dev->state = STATE_DEV_CONNECTED;
953:
954: /* assume that was SET_CONFIGURATION */
955: if (dev->current_config) {
956: unsigned power;
957:
958: if (gadget_is_dualspeed(dev->gadget)
959: && (dev->gadget->speed
960: == USB_SPEED_HIGH))
961: power = dev->hs_config->bMaxPower;
962: else
963: power = dev->config->bMaxPower;
964: usb_gadget_vbus_draw(dev->gadget, 2 * power);
965: }
966:
967: } else { /* collect OUT data */
968: if ((fd->f_flags & O_NONBLOCK) != 0
969: && !dev->setup_out_ready) {
970: retval = -EAGAIN;
971: goto done;
972: }
973: spin_unlock_irq (&dev->lock);
974: retval = wait_event_interruptible (dev->wait,
975: dev->setup_out_ready != 0);
976:
977: /* FIXME state could change from under us */
978: spin_lock_irq (&dev->lock);
979: if (retval)
980: goto done;
981:
982: if (dev->state != STATE_DEV_SETUP) {
983: retval = -ECANCELED;
984: goto done;
985: }
986: dev->state = STATE_DEV_CONNECTED;
987:
988: if (dev->setup_out_error)
989: retval = -EIO;
990: else {
991: len = min (len, (size_t)dev->req->actual);
992: ++dev->udc_usage;
993: spin_unlock_irq(&dev->lock);
994: if (copy_to_user (buf, dev->req->buf, len))
995: retval = -EFAULT;
996: else
997: retval = len;
998: spin_lock_irq(&dev->lock);
999: --dev->udc_usage;
1000: clean_req (dev->gadget->ep0, dev->req);
1001: /* NOTE userspace can't yet choose to stall */
1002: }
1003: }
1004: goto done;
1005: }
1006:
1007: /* else normal: return event data */
1008: if (len < sizeof dev->event [0]) {
1009: retval = -EINVAL;
1010: goto done;
1011: }
1012: len -= len % sizeof (struct usb_gadgetfs_event);
1013: dev->usermode_setup = 1;
1014:
1015: scan:
1016: /* return queued events right away */
1017: if (dev->ev_next != 0) {
1018: unsigned i, n;
1019:
1020: n = len / sizeof (struct usb_gadgetfs_event);
1021: if (dev->ev_next < n)
1022: n = dev->ev_next;
1023:
1024: /* ep0 i/o has special semantics during STATE_DEV_SETUP */
1025: for (i = 0; i < n; i++) {
1026: if (dev->event [i].type == GADGETFS_SETUP) {
1027: dev->state = STATE_DEV_SETUP;
1028: n = i + 1;
1029: break;
1030: }
1031: }
1032: spin_unlock_irq (&dev->lock);
1033: len = n * sizeof (struct usb_gadgetfs_event);
1034: if (copy_to_user (buf, &dev->event, len))
1035: retval = -EFAULT;
1036: else
1037: retval = len;
1038: if (len > 0) {
1039: /* NOTE this doesn't guard against broken drivers;
1040: * concurrent ep0 readers may lose events.
1041: */
1042: spin_lock_irq (&dev->lock);
1043: if (dev->ev_next > n) {
1044: memmove(&dev->event[0], &dev->event[n],
1045: sizeof (struct usb_gadgetfs_event)
1046: * (dev->ev_next - n));
1047: }
1048: dev->ev_next -= n;
1049: spin_unlock_irq (&dev->lock);
1050: }
1051: return retval;
1052: }
1053: if (fd->f_flags & O_NONBLOCK) {
1054: retval = -EAGAIN;
1055: goto done;
1056: }
1057:
1058: switch (state) {
1059: default:
1060: DBG (dev, "fail %s, state %d\n", __func__, state);
1061: retval = -ESRCH;
1062: break;
1063: case STATE_DEV_UNCONNECTED:
1064: case STATE_DEV_CONNECTED:
1065: spin_unlock_irq (&dev->lock);
1066: DBG (dev, "%s wait\n", __func__);
1067:
1068: /* wait for events */
1069: retval = wait_event_interruptible (dev->wait,
1070: dev->ev_next != 0);
1071: if (retval < 0)
1072: return retval;
1073: spin_lock_irq (&dev->lock);
1074: goto scan;
1075: }
1076:
1077: done:
1078: spin_unlock_irq (&dev->lock);
1079: return retval;
1080: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 171/4 |
2026/07/29 08:27 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 46587
output: 47
thoughts: 443
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in USB Gadget Drivers**
Okay, let's break this down. In `gadgetfs`, I see that `udc_usage` is incremented, the lock is dropped, then `usb_ep_queue` is called. The lock is reacquired, and `udc_usage` is decremented. The critical observation is that it *doesn't* wait for completion while holding `udc_usage`! This immediately screams potential Use-After-Free (UAF). If it doesn't wait, how does it prevent the UAF of the request or the gadget itself?
Ah, I see. `gadgetfs` doesn't wait for completion in `ep0_read` either. It just queues the request and returns, or waits for `dev->wait`. That's one possible explanation. But in `raw_gadget.c`, I'm looking at `raw_process_ep0_io`. Here, the request is queued, and it *does* wait for `dev->ep0_done` to complete! That's a different scenario.
Here's the problem. If I decrement `udc_usage` immediately after `usb_ep_queue`, then `gadget_unbind` could proceed concurrently. If `gadget_unbind` *does* proceed, it clears `dev->gadget` and sets `dev->state = STATE_DEV_FAILED`. Then `gadget_unbind` returns. The UDC core then potentially frees the gadget.
But, and this is the crux, `raw_process_ep0_io` is still waiting for `dev->ep0_done`! This is where the race condition could surface. Once the UDC core frees the gadget, it will cancel the request. The cancellation will invoke the completion callback, which will, crucially, complete `dev->ep0_done`. This is very interesting. This wakes up `raw_process_ep0_io`.
So, `raw_process_ep0_io` wakes up, and then it does:
```c
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
```
Wait a second. It *doesn't* touch `dev->gadget` after waking up! It's just accessing `dev->ep0_status` and similar fields, which are probably okay. This looks like a false alarm, but let me recheck `raw_process_ep0_io` for any other possibilities.
|
| 172/4 |
2026/07/29 08:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep0_io]
Results: map[SourceCode: 683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 173/4 |
2026/07/29 08:28 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 48881
output: 43
thoughts: 1267
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in USB Gadget Code**
Okay, so I'm digging into this potential UAF issue. My initial thought is that if `usb_ep_queue` fails, we could be in trouble. Specifically, if `gadget_unbind` has already freed `dev->gadget` before the error from `usb_ep_queue` is handled, then we're dereferencing a dangling pointer when we call `dev_err`. The crash report aligns with this hypothesis perfectly. The key here is that a failure in `usb_ep_queue` coupled with a subsequent call to `dev_err` after the gadget has been unbound leads to the UAF. We see this because it's dereferencing `dev->gadget->dev` after it's been freed.
The same problem can happen if `wait_for_completion_interruptible` is interrupted before the gadget is unbound. If `dev->gadget` has been freed before the interruption occurs, then `usb_ep_dequeue` also leads to a UAF!
So, the obvious mitigation is to hold `udc_usage` until we are entirely done with `dev->gadget`. This prevents `gadget_unbind` from freeing `dev->gadget` prematurely. Now, the next question is if this will block the UDC core from cancelling the request. I have to look into `dummy_udc` for this, and it appears the device calls `usb_gadget_disconnect_locked` before `unbind`. This in turn calls `pullup(0)`.
Specifically in `dummy_udc`, `dummy_pullup` calls `set_link_state`, which eventually stops the controller and cancels requests. This means requests will be cancelled before `unbind` is called. Therefore, if requests are cancelled prior to `unbind`, then `wait_for_completion` will complete before `unbind` is called.
The next scenario is what happens if `usb_ep_queue` is called *after* `pullup(0)` but *before* `unbind`? The UAF is still avoided because even if `usb_ep_queue` fails, we're holding `udc_usage`, so `unbind` will wait for our error handling to complete. Therefore, holding `udc_usage` across the entire block (including `usb_ep_queue` and `wait_for_completion`) is the correct approach.
If `usb_ep_queue` succeeds, the request is queued. When the controller is stopped, the request will complete with an error. But what if the controller is NOT stopped, but the user just unbinds the gadget? We check the core code for `gadget_unbind_driver`, which calls `usb_gadget_disconnect_locked`, which in turn calls `udc->gadget->ops->pullup(udc->gadget, 0);`. This pulls the USB up/down so it triggers the unbind. In `dummy_udc`, `dummy_pullup` calls `set_link_state` which cancels all requests. Therefore it is safe to hold `udc_usage` while waiting, as the completion or error is signaled by the UDC core.
So, one more scenario. What if `usb_ep_queue` queues the request, and then `unbind` is called *before* the driver completes it? In `dummy_udc`, `dummy_queue` might still queue the request *after* `pullup(0)` is called, but that should be handled in `dummy_queue`. I will need to check `dummy_queue` to ensure the gadget is unbind-safe.
|
| 174/4 |
2026/07/29 08:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_queue]
Results: map[SourceCode: 718: static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
719: gfp_t mem_flags)
720: {
721: struct dummy_ep *ep;
722: struct dummy_request *req;
723: struct dummy *dum;
724: struct dummy_hcd *dum_hcd;
725: unsigned long flags;
726:
727: req = usb_request_to_dummy_request(_req);
728: if (!_req || !list_empty(&req->queue) || !_req->complete)
729: return -EINVAL;
730:
731: ep = usb_ep_to_dummy_ep(_ep);
732: if (!_ep || (!ep->desc && _ep->name != ep0name))
733: return -EINVAL;
734:
735: dum = ep_to_dummy(ep);
736: dum_hcd = gadget_to_dummy_hcd(&dum->gadget);
737: if (!dum->driver || !is_enabled(dum_hcd))
738: return -ESHUTDOWN;
739:
740: #if 0
741: dev_dbg(udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
742: ep, _req, _ep->name, _req->length, _req->buf);
743: #endif
744: _req->status = -EINPROGRESS;
745: _req->actual = 0;
746: spin_lock_irqsave(&dum->lock, flags);
747:
748: /* implement an emulated single-request FIFO */
749: if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
750: !dum->fifo_req_busy &&
751: list_empty(&ep->queue) &&
752: _req->length <= FIFO_SIZE) {
753: req = &dum->fifo_req;
754: dum->fifo_req_busy = 1;
755: req->req = *_req;
756: req->req.buf = dum->fifo_buf;
757: memcpy(dum->fifo_buf, _req->buf, _req->length);
758: req->req.context = dum;
759: req->req.complete = fifo_complete;
760:
761: list_add_tail(&req->queue, &ep->queue);
762: spin_unlock(&dum->lock);
763: _req->actual = _req->length;
764: _req->status = 0;
765: usb_gadget_giveback_request(_ep, _req);
766: spin_lock(&dum->lock);
767: } else
768: list_add_tail(&req->queue, &ep->queue);
769: spin_unlock_irqrestore(&dum->lock, flags);
770:
771: /* real hardware would likely enable transfers here, in case
772: * it'd been left NAKing.
773: */
774: return 0;
775: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 175/4 |
2026/07/29 08:28 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 51964
output: 45
thoughts: 380
Thoughts: **Deadlock Danger in USB Gadget Unbinding - A Deep Dive**
Okay, so I'm looking at this USB gadget unbinding sequence, and something smells fishy. My initial thought is that if `dum_hcd` isn't enabled, we return `-ESHUTDOWN`, which is expected. `is_enabled` verifies the controller state β that's the baseline. But then the problem hit me: what if `usb_ep_queue` actually *queues* a request, and then `unbind` gets called right after?
If `usb_ep_queue` queues the request, that implies `is_enabled` *was* true. Then, looking at the code, `gadget_unbind_driver` calls `usb_gadget_udc_stop_locked` *after* calling `unbind`! Wait, let me repeat that, because itβs important. `usb_gadget_udc_stop_locked` comes *after* `unbind`!
And this is where the potential deadlock screams at me. If `unbind` is blocked, waiting for `udc_usage` to drop to zero (presumably held by something that's using the UDC), and weβve already queued a request that *hasn't completed* yet, who's going to complete it? `pullup(0)` *should* complete it. But if it doesnβt, which is my main concern here, that request will hang until `usb_gadget_udc_stop_locked` is called. That happens *after* `unbind`!
This means weβre stuck! A deadlock! `unbind` will never complete because the UDC is busy, and the queue request is never completed because the UDC is stopped, but is it? Now, let me check the `dummy_udc` implementation. In `dummy_udc`, `dummy_pullup` calls `set_link_state`. If `pullup` is 0, the `set_link_state` function will set `dum_hcd->port_status &= ~USB_PORT_STAT_CONNECTION;`. Then it will call `dummy_hub_control` or something similar, which means I should check whether it also calls `stop_activity`. I'll dig into that now...
|
| 176/4 |
2026/07/29 08:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:set_link_state]
Results: map[SourceCode: 451: /* caller must hold lock */
452: static void set_link_state(struct dummy_hcd *dum_hcd)
453: __must_hold(&dum->lock)
454: {
455: struct dummy *dum = dum_hcd->dum;
456: unsigned int power_bit;
457:
458: dum_hcd->active = 0;
459: if (dum->pullup)
460: if ((dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 &&
461: dum->gadget.speed != USB_SPEED_SUPER) ||
462: (dummy_hcd_to_hcd(dum_hcd)->speed != HCD_USB3 &&
463: dum->gadget.speed == USB_SPEED_SUPER))
464: return;
465:
466: set_link_state_by_speed(dum_hcd);
467: power_bit = (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 ?
468: USB_SS_PORT_STAT_POWER : USB_PORT_STAT_POWER);
469:
470: if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
471: dum_hcd->active)
472: dum_hcd->resuming = 0;
473:
474: /* Currently !connected or in reset */
475: if ((dum_hcd->port_status & power_bit) == 0 ||
476: (dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
477: unsigned int disconnect = power_bit &
478: dum_hcd->old_status & (~dum_hcd->port_status);
479: unsigned int reset = USB_PORT_STAT_RESET &
480: (~dum_hcd->old_status) & dum_hcd->port_status;
481:
482: /* Report reset and disconnect events to the driver */
483: if (dum->ints_enabled && (disconnect || reset)) {
484: ++dum->callback_usage;
485: /*
486: * stop_activity() can drop dum->lock, so it must
487: * not come between the dum->ints_enabled test
488: * and the ++dum->callback_usage.
489: */
490: stop_activity(dum);
491: spin_unlock(&dum->lock);
492: if (reset)
493: usb_gadget_udc_reset(&dum->gadget, dum->driver);
494: else
495: dum->driver->disconnect(&dum->gadget);
496: spin_lock(&dum->lock);
497: --dum->callback_usage;
498: }
499: } else if (dum_hcd->active != dum_hcd->old_active &&
500: dum->ints_enabled) {
501: ++dum->callback_usage;
502: spin_unlock(&dum->lock);
503: if (dum_hcd->old_active && dum->driver->suspend)
504: dum->driver->suspend(&dum->gadget);
505: else if (!dum_hcd->old_active && dum->driver->resume)
506: dum->driver->resume(&dum->gadget);
507: spin_lock(&dum->lock);
508: --dum->callback_usage;
509: }
510:
511: dum_hcd->old_status = dum_hcd->port_status;
512: dum_hcd->old_active = dum_hcd->active;
513: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 177/4 |
2026/07/29 08:28 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 54011
output: 43
thoughts: 1870
Thoughts: **Analyzing a Potential Deadlock in `raw_gadget`**
Okay, so I'm digging into this `raw_gadget` driver and I see a potential issue stemming from how requests are handled in relation to device unbinding. It all starts with `set_link_state`, which calls `stop_activity(dum)`, essentially cancelling all requests via `pullup(0)`. Now, I'm concerned about the timing. If `usb_ep_queue` is called *after* `pullup(0)` but *before* `unbind`, we have a problem.
Here's the breakdown: `pullup(0)` would set `dum->pullup` to 0, and subsequent checks within `dummy_queue` (through `!is_enabled(dum_hcd)`) would likely detect the disconnect state. The key is what happens with the request in the middle of all of this. If the request is queued and not completed by the time `unbind` is called, we could hit a deadlock. The `unbind` operation has logic to block, which calls `dummy_udc_stop`, which is blocked, and will wait forever.
I started examining how `gadgetfs` handles this situation to see if there are any hints. In `gadgetfs`, I observed the `ep0_read` routine. The key thing here is how `udc_usage` is managed. It's decremented *immediately* after `usb_ep_queue`, *not* after completion, and no wait is present. This lets `gadget_unbind` proceed without waiting. The UDC driver completes the request asynchronously, with the completion callback being `ep0_complete`.
Then I looked at the contrast in `raw_gadget` which is synchronous, and a `wait_for_completion_interruptible(&dev->ep0_done)` is done. This makes a problem! if we decrement `udc_usage` after calling `usb_ep_queue` but before calling `wait_for_completion`, and then `gadget_unbind` is called, and `dev->gadget` is set to NULL. This means that when the completion callback occurs, we would dereference a NULL pointer! The crash report confirms this, indicating a "slab-use-after-free" caused by dereferencing `dev->gadget` after it's been unbound.
The core question is this: should we hold `udc_usage` for the *entire* completion block, including the wait? If we do, we avoid the UAF, but we introduce a deadlock risk. If the request gets queued and *then* `gadget_unbind` is triggered, `gadget_unbind` would block, and the request might never complete, as it's stuck on the UDC driver. Conversely, If we don't hold `udc_usage` we have a potential UAF.
Then I realized that if `pullup(0)` occurs *before* `usb_ep_queue`, any queued requests are already implicitly cancelled. This means there is no need for a cancel. But what if the request gets queued AFTER `pullup(0)`? If `usb_ep_queue` is called in this disconnected state, the request might return an error, but if not, the request will hang.
So, the optimal strategy here would seem to be:
1. Check and not queue in `usb_ep_queue` after pullup. But how can we prevent calls to the queue from occurring in this state? It seems like this isn't possible because we don't know when `pullup(0)` is called.
2. If the request is queued, we should not wait for completion! The request should complete due to `pullup(0)`.
3. If we don't wait for completion we should cache `dev->gadget` and check that its non-NULL before the wait for completion happens.
4. Or, we need to add a way for the UDC core to notify the gadget driver of the disconnect.
|
| 178/4 |
2026/07/29 08:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:gadget_disconnect]
Results: map[SourceCode: 382: static void gadget_disconnect(struct usb_gadget *gadget)
383: {
384: struct raw_dev *dev = get_gadget_data(gadget);
385: int ret;
386:
387: dev_dbg(&gadget->dev, "gadget disconnected\n");
388: ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL);
389: if (ret < 0)
390: dev_err(&gadget->dev, "failed to queue disconnect event\n");
391: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 179/4 |
2026/07/29 08:28 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 57001
output: 23
thoughts: 128
Thoughts: **Analyzing a Potential Gadget Disconnect Issue**
Okay, so I've pinpointed that the `gadget_disconnect` function is triggered when `pullup(0)` is called. But wait, this doesn't immediately seem to block new requests or modify the device state in a way that would prevent races. Hmm. If we're holding `udc_usage` while waiting for some operation's completion, does that introduce a deadlock scenario? That's what I need to understand better.
To get a clearer picture, let me check how similar gadget drivers deal with this type of situation. Let's start with `f_uvc.c`. I recall seeing a related fix in commit `eba2936bbe6b` ("usb: gadget: uvc: fix NULL pointer dereference during unbind race"), which sounds promising. Time to dive into that specific commit and see how they implemented their solution with the use of a completion. That should give me some solid clues.
|
| 180/4 |
2026/07/29 08:28 |
tool |
git-show |
0m
Args:
Results: map[Output:commit eba2936bbe6b752a31725a9eb5c674ecbf21ee7d
Author: Jimmy Hu <hhhuuu@google.com>
Date: Fri Mar 20 14:54:27 2026 +0800
usb: gadget: uvc: fix NULL pointer dereference during unbind race
Commit b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly
shutdown") introduced two stages of synchronization waits totaling 1500ms
in uvc_function_unbind() to prevent several types of kernel panics.
However, this timing-based approach is insufficient during power
management (PM) transitions.
When the PM subsystem starts freezing user space processes, the
wait_event_interruptible_timeout() is aborted early, which allows the
unbind thread to proceed and nullify the gadget pointer
(cdev->gadget = NULL):
[ 814.123447][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind()
[ 814.178583][ T3173] PM: suspend entry (deep)
[ 814.192487][ T3173] Freezing user space processes
[ 814.197668][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind no clean disconnect, wait for release
When the PM subsystem resumes or aborts the suspend and tasks are
restarted, the V4L2 release path is executed and attempts to access the
already nullified gadget pointer, triggering a kernel panic:
[ 814.292597][ C0] PM: pm_system_irq_wakeup: 479 triggered dhdpcie_host_wake
[ 814.386727][ T3173] Restarting tasks ...
[ 814.403522][ T4558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030
[ 814.404021][ T4558] pc : usb_gadget_deactivate+0x14/0xf4
[ 814.404031][ T4558] lr : usb_function_deactivate+0x54/0x94
[ 814.404078][ T4558] Call trace:
[ 814.404080][ T4558] usb_gadget_deactivate+0x14/0xf4
[ 814.404083][ T4558] usb_function_deactivate+0x54/0x94
[ 814.404087][ T4558] uvc_function_disconnect+0x1c/0x5c
[ 814.404092][ T4558] uvc_v4l2_release+0x44/0xac
[ 814.404095][ T4558] v4l2_release+0xcc/0x130
Address the race condition and NULL pointer dereference by:
1. State Synchronization (flag + mutex)
Introduce a 'func_unbound' flag in struct uvc_device. This allows
uvc_function_disconnect() to safely skip accessing the nullified
cdev->gadget pointer. As suggested by Alan Stern, this flag is protected
by a new mutex (uvc->lock) to ensure proper memory ordering and prevent
instruction reordering or speculative loads. This mutex is also used to
protect 'func_connected' for consistent state management.
2. Explicit Synchronization (completion)
Use a completion to synchronize uvc_function_unbind() with the
uvc_vdev_release() callback. This prevents Use-After-Free (UAF) by
ensuring struct uvc_device is freed after all video device resources
are released.
Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown")
Cc: stable <stable@kernel.org>
Suggested-by: Alan Stern <stern@rowland.harvard.edu>
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Jimmy Hu <hhhuuu@google.com>
Link: https://patch.msgid.link/20260320065427.1374555-1-hhhuuu@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 494fdbc4e85b..8d404d88391c 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -413,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc)
{
int ret;
+ guard(mutex)(&uvc->lock);
+ if (uvc->func_unbound) {
+ dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n");
+ return;
+ }
+
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
}
@@ -431,6 +437,15 @@ static ssize_t function_name_show(struct device *dev,
static DEVICE_ATTR_RO(function_name);
+static void uvc_vdev_release(struct video_device *vdev)
+{
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+
+ /* Signal uvc_function_unbind() that the video device has been released */
+ if (uvc->vdev_release_done)
+ complete(uvc->vdev_release_done);
+}
+
static int
uvc_register_video(struct uvc_device *uvc)
{
@@ -443,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc)
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops;
uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
- uvc->vdev.release = video_device_release_empty;
+ uvc->vdev.release = uvc_vdev_release;
uvc->vdev.vfl_dir = VFL_DIR_TX;
uvc->vdev.lock = &uvc->video.mutex;
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
@@ -659,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
int ret = -EINVAL;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_unbound = false;
opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters. */
@@ -988,12 +1005,19 @@ static void uvc_free(struct usb_function *f)
static void uvc_function_unbind(struct usb_configuration *c,
struct usb_function *f)
{
+ DECLARE_COMPLETION_ONSTACK(vdev_release_done);
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
struct uvc_video *video = &uvc->video;
long wait_ret = 1;
+ bool connected;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock) {
+ uvc->func_unbound = true;
+ uvc->vdev_release_done = &vdev_release_done;
+ connected = uvc->func_connected;
+ }
kthread_cancel_work_sync(&video->hw_submit);
@@ -1006,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c,
* though the video device removal uevent. Allow some time for the
* application to close out before things get deleted.
*/
- if (uvc->func_connected) {
+ if (connected) {
uvcg_dbg(f, "waiting for clean disconnect\n");
wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
uvc->func_connected == false, msecs_to_jiffies(500));
@@ -1017,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
video_unregister_device(&uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
- if (uvc->func_connected) {
+ scoped_guard(mutex, &uvc->lock)
+ connected = uvc->func_connected;
+
+ if (connected) {
/*
* Wait for the release to occur to ensure there are no longer any
* pending operations that may cause panics when resources are cleaned
@@ -1029,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
}
+ /* Wait for the video device to be released */
+ wait_for_completion(&vdev_release_done);
+ uvc->vdev_release_done = NULL;
+
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
@@ -1047,6 +1078,8 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
mutex_init(&uvc->video.mutex);
+ mutex_init(&uvc->lock);
+ uvc->func_unbound = true;
uvc->state = UVC_STATE_DISCONNECTED;
init_waitqueue_head(&uvc->func_connected_queue);
opts = fi_to_f_uvc_opts(fi);
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 676419a04976..7abfdd5e1eef 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -155,6 +155,9 @@ struct uvc_device {
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
+ struct completion *vdev_release_done;
+ struct mutex lock; /* protects func_unbound and func_connected */
+ bool func_unbound;
bool func_connected;
wait_queue_head_t func_connected_queue;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ed48d38498fb..514e5930b9ca 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
+ guard(mutex)(&uvc->lock);
+
if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
return -EBUSY;
@@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc)
uvc_function_disconnect(uvc);
uvcg_video_disable(&uvc->video);
uvcg_free_buffers(&uvc->video.queue);
- uvc->func_connected = false;
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_connected = false;
wake_up_interruptible(&uvc->func_connected_queue);
}
]
|
| 181/4 |
2026/07/29 08:28 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 60849
output: 46
thoughts: 3044
Thoughts: **Analysis of USB Gadget Unbinding and Deadlock Prevention**
Okay, so I've been digging into this `f_uvc.c` code and comparing it to our situation, and I've identified some crucial points regarding how they handle `vdev_release` and the potential deadlock. They use a completion and a mutex-protected flag, `uvc->func_unbound`, to coordinate the release. We're thinking we can use `udc_usage` and a completion in a similar way. The simpler approach would seem to be waiting for `udc_usage` to drop to zero, but that's where the deadlock concern arises.
The core of the problem boils down to how the UDC driver cancels requests during unbinding and the interplay between `raw_process_ep0_io` (our code that handles endpoint 0 I/O) and `gadget_unbind`. If `raw_process_ep0_io` holds `udc_usage` while waiting for `dev->ep0_done` and `gadget_unbind` is also waiting for `udc_usage`, we have a deadlock. I initially thought it wouldn't happen because the UDC driver should cancel requests before unbinding, which would complete `dev->ep0_done`. That's correct; `usb_gadget_disconnect_locked` calls `pullup(0)`, which *should* cancel requests.
However, a potential race exists. If `usb_ep_queue` is called *after* `pullup(0)` but *before* `unbind`, and it *returns an error*, then `raw_process_ep0_io` just decrements `udc_usage` and returns. That avoids the issue. But if `usb_ep_queue` *succeeds* anyway, the request will be queued. If `unbind` then attempts to wait on `udc_usage`, it will never succeed, leading to deadlock.
The fix I'm coming to is, *not* to hold `udc_usage` while waiting for the completion. But how to avoid a Use-After-Free of `dev->gadget` if that is unbound? We could check `dev->state` or `dev->gadget` *after* waking up to see if it's safe. However, *before* we check those values, `gadget_unbind` could proceed to clear the gadget and free it. We need some protection, that is, we need to protect access to `dev->gadget` with `udc_usage`!
Here's my logic:
1. Increment `udc_usage` before queueing the request with `usb_ep_queue`.
2. Decrement `udc_usage` while waiting for completion.
3. On wake up, if `dev->gadget` is not null, then increment `udc_usage`, then queue the request, then decrement `udc_usage`.
That increment ensures that `gadget_unbind` won't proceed until `usb_ep_queue` is complete, and that the decrement ensures that `gadget_unbind` is not blocked indefinitely.
Now, if we decrement `udc_usage` before waiting for completion, we could have another UAF because `gadget_unbind` might free the gadget. `dev->req` is allocated by `raw_gadget`, not `gadget_unbind`. If the UDC driver is well behaved, it cancels requests and calls `gadget_ep0_complete`, which is where we manage the completion. That routine looks fine, the `dev` is still valid, but we need to protect access to `dev->gadget` with `udc_usage`. We need to be holding `udc_usage` while calling `usb_ep_dequeue`.
Now, does this avoid all deadlocks? What if `gadget_unbind` is called WHILE `usb_ep_queue` is running?
If we hold `udc_usage` during `usb_ep_queue`, then `gadget_unbind` will wait. If we hold `udc_usage` during `usb_ep_dequeue`, `gadget_unbind` will wait. If we DON'T hold `udc_usage` during `wait_for_completion`, `gadget_unbind` can proceed. This completely avoids the deadlock!
Here's the approach:
* Increment `udc_usage` before calling `usb_ep_queue`.
* Decrement `udc_usage` *immediately* after calling `usb_ep_queue` (before `wait_for_completion`).
* On wakeup: If `dev->gadget` is still valid, increment `udc_usage` *again*, call `
|
| 182/4 |
2026/07/29 08:29 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep_io]
Results: map[SourceCode:1058: static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
1059: void *data, bool in)
1060: {
1061: int ret = 0;
1062: unsigned long flags;
1063: struct raw_ep *ep;
1064: DECLARE_COMPLETION_ONSTACK(done);
1065:
1066: spin_lock_irqsave(&dev->lock, flags);
1067: if (dev->state != STATE_DEV_RUNNING) {
1068: dev_dbg(dev->dev, "fail, device is not running\n");
1069: ret = -EINVAL;
1070: goto out_unlock;
1071: }
1072: if (!dev->gadget) {
1073: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1074: ret = -EBUSY;
1075: goto out_unlock;
1076: }
1077: if (io->ep >= dev->eps_num) {
1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
1079: ret = -EINVAL;
1080: goto out_unlock;
1081: }
1082: ep = &dev->eps[io->ep];
1083: if (ep->state != STATE_EP_ENABLED) {
1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
1085: ret = -EBUSY;
1086: goto out_unlock;
1087: }
1088: if (ep->disabling) {
1089: dev_dbg(&dev->gadget->dev,
1090: "fail, endpoint is already being disabled\n");
1091: ret = -EBUSY;
1092: goto out_unlock;
1093: }
1094: if (ep->urb_queued) {
1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
1096: ret = -EBUSY;
1097: goto out_unlock;
1098: }
1099: if (in != usb_endpoint_dir_in(ep->ep->desc)) {
1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
1101: ret = -EINVAL;
1102: goto out_unlock;
1103: }
1104:
1105: ep->dev = dev;
1106: ep->req->context = &done;
1107: ep->req->complete = gadget_ep_complete;
1108: ep->req->buf = data;
1109: ep->req->length = io->length;
1110: ep->req->zero = usb_raw_io_flags_zero(io->flags);
1111: ep->urb_queued = true;
1112: spin_unlock_irqrestore(&dev->lock, flags);
1113:
1114: ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1115: if (ret) {
1116: dev_err(&dev->gadget->dev,
1117: "fail, usb_ep_queue returned %d\n", ret);
1118: spin_lock_irqsave(&dev->lock, flags);
1119: goto out_queue_failed;
1120: }
1121:
1122: ret = wait_for_completion_interruptible(&done);
1123: if (ret) {
1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1125: usb_ep_dequeue(ep->ep, ep->req);
1126: wait_for_completion(&done);
1127: spin_lock_irqsave(&dev->lock, flags);
1128: if (ep->status == -ECONNRESET)
1129: ep->status = -EINTR;
1130: goto out_interrupted;
1131: }
1132:
1133: spin_lock_irqsave(&dev->lock, flags);
1134:
1135: out_interrupted:
1136: ret = ep->status;
1137: out_queue_failed:
1138: ep->urb_queued = false;
1139: out_unlock:
1140: spin_unlock_irqrestore(&dev->lock, flags);
1141: return ret;
1142: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 183/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 66314
output: 47
thoughts: 641
Thoughts: **Analyzing a Potential USB Gadget Deadlock**
Okay, this looks familiar! The code flow here shows a pattern that initially made me think of a deadlock situation. Specifically, the concern was about holding `udc_usage` across the entire block, including the `wait_for_completion` calls. But wait, let's think this through carefully. Is this really a deadlock scenario?
Let's break down the call stack: `gadget_unbind` is the function that potentially blocks waiting for `udc_usage`. And `gadget_unbind` is called by `usb_gadget_remove_driver`, which in turn is called by `gadget_unbind_driver` within `udc/core.c`. This sequence leads to `usb_gadget_disconnect_locked` which calls `pullup(0)`.
Here's the key: `pullup(0)` cancels *all* requests. So, when requests are canceled, their completion callbacks get called. These callbacks then call `complete(&dev->ep0_done)` or `complete(&done)`, and this triggers `wait_for_completion_interruptible` to return! Consequently, `raw_process_ep0_io` decrements `udc_usage` and returns. And this unblocks `gadget_unbind`! So, there is no deadlock here.
However, a new scenario pops into mind. What if `usb_ep_queue` is called *after* `pullup(0)` but *before* `unbind`? Will the request get queued? If so, will the UDC driver complete it? If the UDC driver doesn't, `wait_for_completion` will block forever.
Then again, if `pullup(0)` was called, the controller is disconnected. If the controller is disconnected, `usb_ep_queue` should usually return an error such as `-ESHUTDOWN`. If it returns an error, we do not wait for the completion! We just decrement `udc_usage` and return.
But what if the UDC driver is buggy and queues the request anyway? If the UDC driver is buggy, it's a UDC driver bug. But then again, if the driver queues the request and `unbind` blocks, `usb_gadget_udc_stop_locked` will *never* be called. Therefore, the request will *never* be canceled. Is this a real problem?
In `gadgetfs`, they don't hold `udc_usage` while waiting for completion. However, `gadgetfs` uses asynchronous IO, which means it doesn't wait for completion synchronously anyway. However, in `f_fs.c` (FunctionFS), they use `mutex_lock(&ffs->mutex)` to serialize `ffs_ep0_queue_wait` and `functionfs_unbind`. I should really check `f_fs.c` for more insight.
|
| 184/4 |
2026/07/29 08:29 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_ep0_queue_wait]
Results: map[SourceCode: SourceFile:]
|
| 185/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 67649
output: 43
thoughts: 23
Thoughts: Here's my summary, as if I were thinking these thoughts:
**Analyzing `ffs_ep0_queue_wait`**
Okay, time to dive in. I need to refresh my memory on how we're handling endpoint 0 queue waits. Let me pull up `f_fs.c` and go straight to the `ffs_ep0_queue_wait` function. This is critical for understanding the core USB functionality here, especially regarding control transfers. I need to see exactly how we are handling the wait state. What conditions trigger it? How does the system respond when that condition is met? Are we handling any potential race conditions correctly? Specifically, I'm looking for the wait behavior within the device driver, thinking about potential issues that may occur, like timeouts, incorrect wakeups, and improper queue management, especially given our target application, I need to know the potential performance bottlenecks that could be present. Let's see how this is implemented.
|
| 186/4 |
2026/07/29 08:29 |
tool |
grepper |
0m
Args: map[Expression:ffs_ep0_queue_wait PathPrefix:drivers/usb/gadget/function/f_fs.c]
Results: map[Output:drivers/usb/gadget/function/f_fs.c=303=static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
--
drivers/usb/gadget/function/f_fs.c-309-
drivers/usb/gadget/function/f_fs.c:310:static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
drivers/usb/gadget/function/f_fs.c-311- __releases(&ffs->ev.waitq.lock)
--
drivers/usb/gadget/function/f_fs.c=365=static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
--
drivers/usb/gadget/function/f_fs.c-489- /* unlocks spinlock */
drivers/usb/gadget/function/f_fs.c:490: ret = __ffs_ep0_queue_wait(ffs, data, len);
drivers/usb/gadget/function/f_fs.c-491- }
--
drivers/usb/gadget/function/f_fs.c=539=static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
--
drivers/usb/gadget/function/f_fs.c-623- /* unlocks spinlock */
drivers/usb/gadget/function/f_fs.c:624: ret = __ffs_ep0_queue_wait(ffs, data, len);
drivers/usb/gadget/function/f_fs.c-625- if ((ret > 0) && (copy_to_user(buf, data, ret)))
]
|
| 187/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 68373
output: 48
thoughts: 16
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Investigating `__ffs_ep0_queue_wait`**
Alright, time to dig into this. "Let's check `__ffs_ep0_queue_wait`." I'm mentally parsing that right away, of course. My first instinct is to understand its role within the larger FFS (Function File System) framework, assuming this is indeed related to USB device emulation, given the context I usually work in. I need to refresh my mental model of how the endpoint 0 queue management functions in this implementation. I'm immediately thinking about potential race conditions and locking mechanisms around this wait function. What are the signals this is waiting on? Are we dealing with an interruptible wait, or something more blocking? I'm hoping to quickly identify the conditions that trigger this wait and, critically, what might be causing it to stall or behave unexpectedly in the problematic scenario I'm currently debugging. I need to understand its relationship to the control transfer process... is it waiting for a setup packet, an OUT data stage, or perhaps the completion of an IN transfer? Depending on where it is, it can highlight a failure in the driver, the hardware, or maybe even in the host. Gotta check its interaction with the DMA and the FIFO.
|
| 188/4 |
2026/07/29 08:29 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:__ffs_ep0_queue_wait]
Results: map[SourceCode: 310: static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
311: __releases(&ffs->ev.waitq.lock)
312: {
313: struct usb_request *req = ffs->ep0req;
314: int ret;
315:
316: if (!req) {
317: spin_unlock_irq(&ffs->ev.waitq.lock);
318: return -EINVAL;
319: }
320:
321: req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
322:
323: spin_unlock_irq(&ffs->ev.waitq.lock);
324:
325: req->buf = data;
326: req->length = len;
327:
328: /*
329: * UDC layer requires to provide a buffer even for ZLP, but should
330: * not use it at all. Let's provide some poisoned pointer to catch
331: * possible bug in the driver.
332: */
333: if (req->buf == NULL)
334: req->buf = (void *)0xDEADBABE;
335:
336: reinit_completion(&ffs->ep0req_completion);
337:
338: ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
339: if (ret < 0)
340: return ret;
341:
342: ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
343: if (ret) {
344: usb_ep_dequeue(ffs->gadget->ep0, req);
345: return -EINTR;
346: }
347:
348: ffs->setup_state = FFS_NO_SETUP;
349: return req->status ? req->status : req->actual;
350: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 189/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 69378
output: 38
thoughts: 79
Thoughts: Okay, here's my take on those code snippets, thinking through it like I would be dissecting this myself:
**Digging into the `__ffs_ep0_queue_wait` Function**
Alright, so I'm seeing this `__ffs_ep0_queue_wait` function, and the first thing that jumps out is the sequence: `usb_ep_queue` followed by `wait_for_completion_interruptible`. Standard setup for asynchronous USB transactions, I get it. Queue the request, then block until it completes or is interrupted. Seems straightforward enough at this level.
But, hold on a second... Interrupts always make me a little wary, especially when dealing with hardware. I need to think through the error conditions here. An interrupt means something unusual happened. Okay, what if it's interrupted? The code immediately calls `usb_ep_dequeue`. So, the endpoint needs to be unqueued. That makes sense, cleanup is crucial in this type of process!
Now, the red flag that's waving in front of me is the possibility of a use-after-free (UAF) situation. I need to be sure the gadget isn't deallocated or messed with in the middle of this process. The whole point of concurrency is to make sure this doesn't happen. The code needs to handle potential issues correctly during the interaction with the hardware. Where's the logic to protect against a use-after-free scenario involving `ffs->gadget`? Let's check `ffs_closed`. That's where I bet they are handling the shutdown/cleanup of the gadget. I'm going to look there to make sure I'm not missing anything. I need to be sure that the gadget won't be modified or deallocated before the endpoint can properly dequeue. Let's see how they deal with that!
|
| 190/4 |
2026/07/29 08:29 |
tool |
grepper |
0m
Args: map[Expression:ffs_closed PathPrefix:drivers/usb/gadget/function/f_fs.c]
Results: map[Output:drivers/usb/gadget/function/f_fs.c=289=static int ffs_ready(struct ffs_data *ffs);
drivers/usb/gadget/function/f_fs.c:290:static void ffs_closed(struct ffs_data *ffs);
drivers/usb/gadget/function/f_fs.c-291-static void ffs_reset_work(struct work_struct *work);
--
drivers/usb/gadget/function/f_fs.c=2233=static void ffs_data_clear(struct ffs_data *ffs)
--
drivers/usb/gadget/function/f_fs.c-2237-
drivers/usb/gadget/function/f_fs.c:2238: ffs_closed(ffs);
drivers/usb/gadget/function/f_fs.c-2239-
--
drivers/usb/gadget/function/f_fs.c=4270=static int ffs_ready(struct ffs_data *ffs)
--
drivers/usb/gadget/function/f_fs.c-4300-
drivers/usb/gadget/function/f_fs.c:4301:static void ffs_closed(struct ffs_data *ffs)
drivers/usb/gadget/function/f_fs.c-4302-{
--
drivers/usb/gadget/function/f_fs.c-4315- if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
drivers/usb/gadget/function/f_fs.c:4316: ffs_obj->ffs_closed_callback)
drivers/usb/gadget/function/f_fs.c:4317: ffs_obj->ffs_closed_callback(ffs);
drivers/usb/gadget/function/f_fs.c-4318-
]
|
| 191/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 70391
output: 44
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing `ffs_func_unbind`**
Right, let's dive into this `ffs_func_unbind` function. I need to refresh my mental model of its purpose and how it fits into the broader architecture. This sounds like it's likely related to file system operations, possibly unbinding a file system function or resource. Let me quickly consider: is this related to mounting/unmounting, driver interactions, or perhaps some sort of security context management? What data structures are probably involved β are we talking about inode structures, virtual file system (VFS) layers, or potentially some kind of capability-based system? I'll probably start by looking at the surrounding code to determine which, before deciding which part of the code it pertains to. It is important to know which file system the function applies to, because that will allow me to narrow down the possible data structures. Let's start with the basics.
|
| 192/4 |
2026/07/29 08:29 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_func_unbind]
Results: map[SourceCode:4081: static void ffs_func_unbind(struct usb_configuration *c,
4082: struct usb_function *f)
4083: {
4084: struct ffs_function *func = ffs_func_from_usb(f);
4085: struct ffs_data *ffs = func->ffs;
4086: struct f_fs_opts *opts =
4087: container_of(f->fi, struct f_fs_opts, func_inst);
4088: struct ffs_ep *ep = func->eps;
4089: unsigned count = ffs->eps_count;
4090: unsigned long flags;
4091:
4092: if (ffs->func == func) {
4093: ffs_func_eps_disable(func);
4094: ffs->func = NULL;
4095: }
4096:
4097: /* Drain any pending AIO completions */
4098: drain_workqueue(ffs->io_completion_wq);
4099:
4100: ffs_event_add(ffs, FUNCTIONFS_UNBIND);
4101: if (!--opts->refcnt)
4102: functionfs_unbind(ffs);
4103:
4104: /* cleanup after autoconfig */
4105: spin_lock_irqsave(&func->ffs->eps_lock, flags);
4106: while (count--) {
4107: if (ep->ep && ep->req)
4108: usb_ep_free_request(ep->ep, ep->req);
4109: ep->req = NULL;
4110: ++ep;
4111: }
4112: spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
4113: kfree(func->eps);
4114: func->eps = NULL;
4115: /*
4116: * eps, descriptors and interfaces_nums are allocated in the
4117: * same chunk so only one free is required.
4118: */
4119: func->function.fs_descriptors = NULL;
4120: func->function.hs_descriptors = NULL;
4121: func->function.ss_descriptors = NULL;
4122: func->function.ssp_descriptors = NULL;
4123: func->interfaces_nums = NULL;
4124:
4125: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 193/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 71377
output: 42
thoughts: 10
Thoughts: Okay, let's dive into this. Here's my thought process summarized, assuming I'm already pretty familiar with functionfs and its workings:
**Checking `functionfs_unbind`**
Right, time to check `functionfs_unbind`. I need to refresh my memory on the critical points of this function, since it's the unbinding operation β crucial for ensuring a clean teardown when a function is detached. I know it's a key function, but I want to make sure I haven't missed any edge cases or potential issues in my understanding of its implementation. Let's see... what's the typical flow? What resources does it release? Does it handle potential errors gracefully? I need to review how it interacts with the underlying USB core, especially in scenarios where a device is being removed or a function is being switched. I'm focusing specifically on how it ensures no dangling pointers or race conditions. Any recent patches or bug fixes related to `functionfs_unbind` that I should be aware of? Best to refresh myself on that, too. Time to start the analysis!
|
| 194/4 |
2026/07/29 08:29 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:functionfs_unbind]
Results: map[SourceCode:2333: static void functionfs_unbind(struct ffs_data *ffs)
2334: {
2335: if (!WARN_ON(!ffs->gadget)) {
2336: /* dequeue before freeing ep0req */
2337: usb_ep_dequeue(ffs->gadget->ep0, ffs->ep0req);
2338: mutex_lock(&ffs->mutex);
2339: usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
2340: ffs->ep0req = NULL;
2341: ffs->gadget = NULL;
2342: clear_bit(FFS_FL_BOUND, &ffs->flags);
2343: mutex_unlock(&ffs->mutex);
2344: ffs_data_put(ffs);
2345: }
2346: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 195/4 |
2026/07/29 08:29 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 71941
output: 45
thoughts: 335
Thoughts: **Race Condition Identified in FunctionFS USB Driver**
Okay, so I'm looking at this `functionfs_unbind` function, and the sequence it performs is immediately raising concerns. It's calling `usb_ep_dequeue` *before* taking the mutex. Then, it locks the mutex, releases the request, and sets `ffs->gadget` to `NULL`. This is a classic race condition setup.
The issue becomes immediately apparent when I consider `__ffs_ep0_queue_wait`. This function *doesn't* hold `ffs->mutex`, at least not according to the `__releases` annotation. It uses `wait_for_completion_interruptible`, and if that's interrupted, `usb_ep_dequeue(ffs->gadget->ep0, req)` gets called.
Here's where the problem really bites: if `functionfs_unbind` is running concurrently, it might have already set `ffs->gadget` to `NULL` by the time `__ffs_ep0_queue_wait` calls `usb_ep_dequeue`. So, we have a clear potential for a NULL pointer dereference or even a use-after-free situation on the gadget struct!
Thinking deeper about the function call chain, `__ffs_ep0_queue_wait` is invoked from both `ffs_ep0_write` and `ffs_ep0_read`. And that makes me wonder... does *either* of those functions hold `ffs->mutex`? I need to verify how these two functions handle the mutex. The write/read functions need to be carefully examined as the entry point into this potential problem area. I'm going to have to dive into those and trace the locking to see if that's the source of the protection. This is going to require careful code review!
|
| 196/4 |
2026/07/29 08:30 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_fs.c Name:ffs_ep0_write]
Results: map[SourceCode: 365: static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
366: size_t len, loff_t *ptr)
367: {
368: struct ffs_data *ffs = file->private_data;
369: ssize_t ret;
370: char *data;
371:
372: /* Fast check if setup was canceled */
373: if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
374: return -EIDRM;
375:
376: /* Acquire mutex */
377: ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
378: if (ret < 0)
379: return ret;
380:
381: /* Check state */
382: switch (ffs->state) {
383: case FFS_READ_DESCRIPTORS:
384: case FFS_READ_STRINGS:
385: /* Copy data */
386: if (len < 16) {
387: ret = -EINVAL;
388: break;
389: }
390:
391: data = ffs_prepare_buffer(buf, len);
392: if (IS_ERR(data)) {
393: ret = PTR_ERR(data);
394: break;
395: }
396:
397: /* Handle data */
398: if (ffs->state == FFS_READ_DESCRIPTORS) {
399: pr_info("read descriptors\n");
400: ret = __ffs_data_got_descs(ffs, data, len);
401: if (ret < 0)
402: break;
403:
404: ffs->state = FFS_READ_STRINGS;
405: ret = len;
406: } else {
407: pr_info("read strings\n");
408: ret = __ffs_data_got_strings(ffs, data, len);
409: if (ret < 0)
410: break;
411:
412: ret = ffs_epfiles_create(ffs);
413: if (ret) {
414: ffs->state = FFS_CLOSING;
415: break;
416: }
417:
418: ffs->state = FFS_ACTIVE;
419: mutex_unlock(&ffs->mutex);
420:
421: ret = ffs_ready(ffs);
422: if (ret < 0) {
423: ffs->state = FFS_CLOSING;
424: return ret;
425: }
426:
427: return len;
428: }
429: break;
430:
431: case FFS_ACTIVE:
432: data = NULL;
433: /*
434: * We're called from user space, we can use _irq
435: * rather then _irqsave
436: */
437: spin_lock_irq(&ffs->ev.waitq.lock);
438: switch (ffs_setup_state_clear_cancelled(ffs)) {
439: case FFS_SETUP_CANCELLED:
440: ret = -EIDRM;
441: goto done_spin;
442:
443: case FFS_NO_SETUP:
444: ret = -ESRCH;
445: goto done_spin;
446:
447: case FFS_SETUP_PENDING:
448: break;
449: }
450:
451: /* FFS_SETUP_PENDING */
452: if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
453: spin_unlock_irq(&ffs->ev.waitq.lock);
454: ret = __ffs_ep0_stall(ffs);
455: break;
456: }
457:
458: /* FFS_SETUP_PENDING and not stall */
459: len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength));
460:
461: spin_unlock_irq(&ffs->ev.waitq.lock);
462:
463: data = ffs_prepare_buffer(buf, len);
464: if (IS_ERR(data)) {
465: ret = PTR_ERR(data);
466: break;
467: }
468:
469: spin_lock_irq(&ffs->ev.waitq.lock);
470:
471: /*
472: * We are guaranteed to be still in FFS_ACTIVE state
473: * but the state of setup could have changed from
474: * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
475: * to check for that. If that happened we copied data
476: * from user space in vain but it's unlikely.
477: *
478: * For sure we are not in FFS_NO_SETUP since this is
479: * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
480: * transition can be performed and it's protected by
481: * mutex.
482: */
483: if (ffs_setup_state_clear_cancelled(ffs) ==
484: FFS_SETUP_CANCELLED) {
485: ret = -EIDRM;
486: done_spin:
487: spin_unlock_irq(&ffs->ev.waitq.lock);
488: } else {
489: /* unlocks spinlock */
490: ret = __ffs_ep0_queue_wait(ffs, data, len);
491: }
492: kfree(data);
493: break;
494:
495: default:
496: ret = -EBADFD;
497: break;
498: }
499:
500: mutex_unlock(&ffs->mutex);
501: return ret;
502: }
SourceFile:drivers/usb/gadget/function/f_fs.c]
|
| 197/4 |
2026/07/29 08:30 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 74679
output: 1076
thoughts: 2121
Thoughts: **Refining the Raw Gadget Unbind Logic**
Alright, let's break this down. My initial thought process focused on how `functionfs` handles unbinding and request cancellation, which seems to be a solid model to follow. I observed that `functionfs_unbind` calls `usb_ep_dequeue` *before* acquiring its mutex, which seems counterintuitive at first. But, after more careful consideration, it actually creates a safe mechanism where the request is cancelled prior to mutex acquisition, allowing the waiting thread to continue safely.
The key to applying this to `raw_gadget` is figuring out how to manage contention around request cancellation and UDC unbinding, because `raw_gadget` needs to manage multiple endpoints and therefore requests. At first, I was tempted to use `dev->lock` directly, but quickly realized that spinlocks don't play well with waiting for completion. Instead, the `udc_usage` counter presented itself as a viable "mutex" in this situation.
My initial line of thinking was to hold `udc_usage` while waiting for completion. However, this raises a potential problem: if a request gets queued after the UDC core has called `pullup(0)` to disconnect, who would cancel it to allow unbind to continue? I realized that `pullup(0)` is called before the unbind sequence. That means that, crucially, `pullup(0)` *should* cancel all pending requests. This makes holding `udc_usage` during the `wait_for_completion` safe, because the requests are guaranteed to be cancelled at that point.
However, a nagging feeling remains: what if `usb_ep_queue` gets called *after* `pullup(0)`? This would leave a request queued when the device unbinds. A UDC driver bug might also lead to this situation. However, in this case, the unbind would deadlock, which is a problem, but a different one.
After much deliberation, I've arrived at what seems like a more robust approach. I've concluded it's better to *not* hold `udc_usage` during the entire `wait_for_completion` period. Instead, I should check `dev->gadget` after the wait, which offers much better protection against potential deadlocks and races.
The new logic would look something like this in `gadget_unbind`:
```c
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
dev->udc_usage++;
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
} else {
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
}
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
```
This ensures we only attempt to dequeue if the gadget is still valid. This avoids both UAFs and deadlocks!
I've also meticulously checked the `raw_process_ep_io` and `raw_ioctl_ep_disable` functions to ensure this pattern holds across all the critical sections where requests are submitted and disabled. The key is now ensuring that `udc_usage` is incremented and decremented correctly.
I've verified that the three critical functions (`raw_process_ep0_io`, `raw_process_ep_io`, and `raw_ioctl_ep_disable`) are correctly instrumented with `udc_usage` while the other IOCTL functions that hold the spinlock are safe. It's a simple, and solid solution.
|
| 198/4 |
2026/07/29 08:30 |
tool |
codeeditor |
0m
Args: map[CurrentCode: dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} NewCode: dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
goto out_queue_failed;
}
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
spin_unlock_irqrestore(&dev->lock, flags);
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
} else {
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
}
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 199/4 |
2026/07/29 08:30 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 78842
output: 1007
thoughts: 14
Thoughts: Okay, let's dive into this. Here's how I'd approach summarizing the prompt, keeping in mind my expertise:
**Updating `raw_process_ep_io`**
Alright, so the task at hand is to update `raw_process_ep_io`. Seems straightforward, but as an expert, I immediately start considering the implications. What exactly *is* `raw_process_ep_io`? Is it a function, a module, a process, a data structure? The term "raw" suggests it's handling the unprocessed or minimally processed input/output for something, likely in the context of event processing or data pipelines. `EP` likely means "Event Processor" or something similar. I need to understand its role in the larger system before I even begin thinking about specific updates.
I'd be thinking: what are the current inputs and outputs? Are there any known issues or areas for improvement? Are there performance bottlenecks or error handling shortcomings? Has the specification for the event processing changed? Are we moving to a new data format? Any of this kind of information would influence the process and design considerations. I'd need to consider the impact of any changes on other parts of the system β downstream consumers, upstream producers, and all the relevant dependencies. I'd be looking for documentation, unit tests, and integration tests to ensure any changes are thoroughly validated. Finally, I'd consider versioning and rollback strategies.
|
| 200/4 |
2026/07/29 08:30 |
tool |
codeeditor |
0m
Args: map[CurrentCode: ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(ep->ep, ep->req);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} NewCode: ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
goto out_queue_failed;
}
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
spin_unlock_irqrestore(&dev->lock, flags);
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(usb_ep, ep->req);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
} else {
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 201/4 |
2026/07/29 08:30 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 80170
output: 519
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Updating `raw_ioctl_ep_disable`**
Okay, so we're circling back to `raw_ioctl_ep_disable`. Let's get this right. I need to make sure this is consistent with the latest changes in the ioctl handling. The goal here, as always, is to disable the endpoint cleanly, but the context has evolved, so I've got to take a fresh look. I need to keep in mind any potential race conditions between this ioctl and other operations that might be happening on the endpoint. Does the existing locking scheme still hold up, or do I need to introduce a new mutex or spinlock to protect against concurrent access?
Beyond that, I also need to ensure that the error handling is still robust and correct. Think about all the edge cases and failure scenarios. Does this ioctl return an appropriate error code if the endpoint is already disabled? What about if it's in the process of being torn down? Or if the underlying hardware is unresponsive? This is a critical point; we don't want to leak resources or leave the system in an unstable state.
I also need to evaluate the implications for power management. Does this change affect the power state of the device in any way? I'll need to double-check the interaction with any relevant power management callbacks or state machines. Finally, I'll need to go back over the unit tests and integration tests. Need to ensure that the modifications I make are thoroughly covered. I'll probably need to add a few new test cases specifically to exercise the new code paths. Let's make this as bulletproof as possible.
|
| 202/4 |
2026/07/29 08:30 |
tool |
codeeditor |
0m
Args: map[CurrentCode: if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} NewCode: if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 203/4 |
2026/07/29 08:30 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 81070
output: 46
thoughts: 1472
Thoughts: **Analyzing USB Endpoint Disable and Gadget Unbinding**
Okay, so I'm digging into this `raw_ioctl_ep_disable` function and I need to make sure I understand the locking and potential race conditions, especially given that `dev->udc_usage` counter. My initial thought was, "Hey, in `raw_ioctl_ep_disable`, we call `dev_dbg` before incrementing `dev->udc_usage` - is this safe?" The answer, of course, is yes, because we hold `dev->lock`. That's the baseline.
Now, let's consider what happens if `gadget_unbind` is called concurrently. `gadget_unbind` takes `dev->lock`, sets `dev->gadget = NULL`, and sets `dev->state` to `STATE_DEV_FAILED`. But, crucially, `raw_ioctl_ep_disable` checks `dev->state != STATE_DEV_RUNNING` and `!dev->gadget` at the start, *while still holding* `dev->lock`. Therefore, during the time we are checking those things, `dev->gadget` is guaranteed to be non-NULL and a valid pointer! This is the core safety.
The next thing I'm focused on is the scenario where `gadget_unbind` is triggered *while* `usb_ep_disable` is running. This is where `udc_usage` comes in: because we hold `udc_usage`, `gadget_unbind` will be blocked, waiting for that counter to drop to zero. We know that `gadget_unbind` waits on `udc_usage` because the code in the kernel says:
```c
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
```
Once our `usb_ep_disable` completes, we re-acquire `dev->lock`, decrement `udc_usage`, and signal `unbind_completion`. This releases `gadget_unbind`.
Then the next question, and this is important, is about the endpoint structure itself. I'm calling `usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req)` and `kfree(dev->eps[i].ep->desc)`. Is `dev->eps[i].ep` valid after the call to `usb_ep_disable`? `usb_ep_disable` is only supposed to disable the endpoint; the underlying structure should still be valid until the gadget is freed.
And now it clicks. If `gadget_unbind` is waiting on `udc_usage`, it means the gadget isn't freed yet! Therefore, `dev->eps[i].ep` is definitely still valid at the moment we free the request and endpoint description.
Ok, so after our `udc_usage` decrement and `unbind_completion` signal, `gadget_unbind` can proceed. But now, when `gadget_unbind` finishes, it returns and the UDC core frees the gadget. And that's the next critical observation; *we're still holding* `dev->lock`! So if `gadget_unbind` can return only after our work is done, it all makes sense. Does `gadget_unbind` wait on `dev->lock`?
I just found the answer! No, `gadget_unbind` drops `dev->lock` *before* returning.
```c
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
spin_unlock_irqrestore(&dev->lock, flags);
```
So `gadget_unbind` takes the lock again at the top of its code, and then *immediately* drops it before it can actually complete! This means that *if* we are holding `dev->lock` when we complete `unbind_completion`, then `gadget_unbind` will wake up, but then immediately block on `spin_lock_irqsave(&dev->lock, flags)`. `gadget_unbind` CANNOT finish and free the gadget until *we* release `dev->lock`.
Therefore, I conclude that it's safe to call `usb_ep_free_request` because the gadget cannot have been freed yet! We are holding
|
| 204/4 |
2026/07/29 08:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_enable]
Results: map[SourceCode: 840: static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
841: {
842: int ret = 0, i;
843: unsigned long flags;
844: struct usb_endpoint_descriptor *desc;
845: struct raw_ep *ep;
846: bool ep_props_matched = false;
847:
848: desc = memdup_user((void __user *)value, sizeof(*desc));
849: if (IS_ERR(desc))
850: return PTR_ERR(desc);
851:
852: /*
853: * Endpoints with a maxpacket length of 0 can cause crashes in UDC
854: * drivers.
855: */
856: if (usb_endpoint_maxp(desc) == 0) {
857: dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
858: kfree(desc);
859: return -EINVAL;
860: }
861:
862: spin_lock_irqsave(&dev->lock, flags);
863: if (dev->state != STATE_DEV_RUNNING) {
864: dev_dbg(dev->dev, "fail, device is not running\n");
865: ret = -EINVAL;
866: goto out_free;
867: }
868: if (!dev->gadget) {
869: dev_dbg(dev->dev, "fail, gadget is not bound\n");
870: ret = -EBUSY;
871: goto out_free;
872: }
873:
874: for (i = 0; i < dev->eps_num; i++) {
875: ep = &dev->eps[i];
876: if (ep->addr != usb_endpoint_num(desc) &&
877: ep->addr != USB_RAW_EP_ADDR_ANY)
878: continue;
879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
880: continue;
881: ep_props_matched = true;
882: if (ep->state != STATE_EP_DISABLED)
883: continue;
884: ep->ep->desc = desc;
885: ret = usb_ep_enable(ep->ep);
886: if (ret < 0) {
887: dev_err(&dev->gadget->dev,
888: "fail, usb_ep_enable returned %d\n", ret);
889: goto out_free;
890: }
891: ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
892: if (!ep->req) {
893: dev_err(&dev->gadget->dev,
894: "fail, usb_ep_alloc_request failed\n");
895: usb_ep_disable(ep->ep);
896: ret = -ENOMEM;
897: goto out_free;
898: }
899: ep->state = STATE_EP_ENABLED;
900: ep->ep->driver_data = ep;
901: ret = i;
902: goto out_unlock;
903: }
904:
905: if (!ep_props_matched) {
906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
907: ret = -EINVAL;
908: } else {
909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
910: ret = -EBUSY;
911: }
912:
913: out_free:
914: kfree(desc);
915: out_unlock:
916: spin_unlock_irqrestore(&dev->lock, flags);
917: return ret;
918: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 205/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 84831
output: 52
thoughts: 251
Thoughts: **Analyzing the Thread Safety of USB Endpoint Operations**
Okay, so I'm looking at this `raw_ioctl_ep_enable` function and immediately, my spider senses are tingling. I see that `dev_err(&dev->gadget->dev, ...)` call. The code uses `dev->lock`, which means `dev->gadget` *should* be valid within that critical section. But I've learned to be paranoid, so I need to make absolutely sure.
Wait a second⦠I'm seeing calls to `usb_ep_enable` and `usb_ep_alloc_request` *with* `dev->lock` held? Let's confirm that. Line 862 shows `spin_lock_irqsave(&dev->lock, flags);` and line 916 has the corresponding `spin_unlock_irqrestore(&dev->lock, flags);`. Yep, it's clear: both functions are called while the spinlock is active.
Now, the real question: is this safe? Calling `usb_ep_enable` with a spinlock held... that's usually a recipe for disaster if it sleeps! I need to quickly determine if it's blocking. A quick check of the function definition should show it's non-blocking. Ah, no sleep, that's good.
What about `usb_ep_alloc_request`? I recall that one allocates memory. But it's using `GFP_ATOMIC`. That's the crucial hint! `GFP_ATOMIC` implies it's a non-sleeping allocation. Excellent!
So, the conclusion is clear: this is perfectly safe. Both functions are called under the protection of the spinlock, and neither function can block. Now, let's move on to the `raw_ioctl_ep_set_clear_halt_wedge` function.
|
| 206/4 |
2026/07/29 08:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_ep_set_clear_halt_wedge]
Results: map[SourceCode: 974: static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
975: unsigned long value, bool set, bool halt)
976: {
977: int ret = 0, i = value;
978: unsigned long flags;
979:
980: spin_lock_irqsave(&dev->lock, flags);
981: if (dev->state != STATE_DEV_RUNNING) {
982: dev_dbg(dev->dev, "fail, device is not running\n");
983: ret = -EINVAL;
984: goto out_unlock;
985: }
986: if (!dev->gadget) {
987: dev_dbg(dev->dev, "fail, gadget is not bound\n");
988: ret = -EBUSY;
989: goto out_unlock;
990: }
991: if (i < 0 || i >= dev->eps_num) {
992: dev_dbg(dev->dev, "fail, invalid endpoint\n");
993: ret = -EBUSY;
994: goto out_unlock;
995: }
996: if (dev->eps[i].state == STATE_EP_DISABLED) {
997: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
998: ret = -EINVAL;
999: goto out_unlock;
1000: }
1001: if (dev->eps[i].disabling) {
1002: dev_dbg(&dev->gadget->dev,
1003: "fail, disable is in progress\n");
1004: ret = -EINVAL;
1005: goto out_unlock;
1006: }
1007: if (dev->eps[i].urb_queued) {
1008: dev_dbg(&dev->gadget->dev,
1009: "fail, waiting for urb completion\n");
1010: ret = -EINVAL;
1011: goto out_unlock;
1012: }
1013: if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
1014: dev_dbg(&dev->gadget->dev,
1015: "fail, can't halt/wedge ISO endpoint\n");
1016: ret = -EINVAL;
1017: goto out_unlock;
1018: }
1019:
1020: if (set && halt) {
1021: ret = usb_ep_set_halt(dev->eps[i].ep);
1022: if (ret < 0)
1023: dev_err(&dev->gadget->dev,
1024: "fail, usb_ep_set_halt returned %d\n", ret);
1025: } else if (!set && halt) {
1026: ret = usb_ep_clear_halt(dev->eps[i].ep);
1027: if (ret < 0)
1028: dev_err(&dev->gadget->dev,
1029: "fail, usb_ep_clear_halt returned %d\n", ret);
1030: } else if (set && !halt) {
1031: ret = usb_ep_set_wedge(dev->eps[i].ep);
1032: if (ret < 0)
1033: dev_err(&dev->gadget->dev,
1034: "fail, usb_ep_set_wedge returned %d\n", ret);
1035: }
1036:
1037: out_unlock:
1038: spin_unlock_irqrestore(&dev->lock, flags);
1039: return ret;
1040: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 207/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 86641
output: 46
thoughts: 38
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing Lock Acquisition and Potential Race Conditions**
Okay, so this code grabs `dev->lock` for the entire function's execution. That's good; it means that while this function is running, `dev->gadget` is protected from concurrent modification. That's a fundamental synchronization pattern I'm familiar with and expect. The exclusivity of the lock guarantees that the pointer, and any related data, isn't going to disappear on me while I'm accessing it within this function.
But, hang on a second... what about `raw_ioctl_eps_info`? That call needs a second, deeper look. I need to quickly recall the implications of that call and its interactions with `dev->lock`. I need to ensure it doesn't represent a potential race condition in this overall scheme.
|
| 208/4 |
2026/07/29 08:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_eps_info]
Results: map[SourceCode:1247: static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
1248: {
1249: int ret = 0, i;
1250: unsigned long flags;
1251: struct usb_raw_eps_info *info;
1252: struct raw_ep *ep;
1253:
1254: info = kzalloc_obj(*info);
1255: if (!info) {
1256: ret = -ENOMEM;
1257: goto out;
1258: }
1259:
1260: spin_lock_irqsave(&dev->lock, flags);
1261: if (dev->state != STATE_DEV_RUNNING) {
1262: dev_dbg(dev->dev, "fail, device is not running\n");
1263: ret = -EINVAL;
1264: spin_unlock_irqrestore(&dev->lock, flags);
1265: goto out_free;
1266: }
1267: if (!dev->gadget) {
1268: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1269: ret = -EBUSY;
1270: spin_unlock_irqrestore(&dev->lock, flags);
1271: goto out_free;
1272: }
1273:
1274: for (i = 0; i < dev->eps_num; i++) {
1275: ep = &dev->eps[i];
1276: strscpy(&info->eps[i].name[0], ep->ep->name,
1277: USB_RAW_EP_NAME_MAX);
1278: info->eps[i].addr = ep->addr;
1279: fill_ep_caps(&ep->ep->caps, &info->eps[i].caps);
1280: fill_ep_limits(ep->ep, &info->eps[i].limits);
1281: }
1282: ret = dev->eps_num;
1283: spin_unlock_irqrestore(&dev->lock, flags);
1284:
1285: if (copy_to_user((void __user *)value, info, sizeof(*info)))
1286: ret = -EFAULT;
1287:
1288: out_free:
1289: kfree(info);
1290: out:
1291: return ret;
1292: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 209/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 87648
output: 44
thoughts: 40
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Safety in Peripheral Access**
Right, so I'm looking at this code and, as I see it, the use of `dev->lock` when accessing both `dev->gadget` and `ep->ep` is crucial. That gives me the confidence that the operations are thread-safe. I'm expecting no race conditions here because of that locking. It seems straightforward, the locking strategy looks sound in this context.
Hmm, wait a second. That looks correct, but it has me wondering... What about that initialisation function, `raw_ioctl_init`? I need to quickly re-evaluate that call and make sure it doesn't bypass any of these locks or introduce a window where these structures can be accessed unsafely before they're properly protected. I need to refresh my understanding of how that integrates into the overall locking scheme.
|
| 210/4 |
2026/07/29 08:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_init]
Results: map[SourceCode: 480: static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
481: {
482: int ret = 0;
483: int driver_id_number;
484: struct usb_raw_init arg;
485: char *udc_driver_name;
486: char *udc_device_name;
487: char *driver_driver_name;
488: unsigned long flags;
489:
490: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
491: return -EFAULT;
492:
493: switch (arg.speed) {
494: case USB_SPEED_UNKNOWN:
495: arg.speed = USB_SPEED_HIGH;
496: break;
497: case USB_SPEED_LOW:
498: case USB_SPEED_FULL:
499: case USB_SPEED_HIGH:
500: case USB_SPEED_SUPER:
501: break;
502: default:
503: return -EINVAL;
504: }
505:
506: driver_id_number = ida_alloc(&driver_id_numbers, GFP_KERNEL);
507: if (driver_id_number < 0)
508: return driver_id_number;
509:
510: driver_driver_name = kmalloc(DRIVER_DRIVER_NAME_LENGTH_MAX, GFP_KERNEL);
511: if (!driver_driver_name) {
512: ret = -ENOMEM;
513: goto out_free_driver_id_number;
514: }
515: snprintf(driver_driver_name, DRIVER_DRIVER_NAME_LENGTH_MAX,
516: DRIVER_NAME ".%d", driver_id_number);
517:
518: udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
519: if (!udc_driver_name) {
520: ret = -ENOMEM;
521: goto out_free_driver_driver_name;
522: }
523: ret = strscpy(udc_driver_name, &arg.driver_name[0],
524: UDC_NAME_LENGTH_MAX);
525: if (ret < 0)
526: goto out_free_udc_driver_name;
527: ret = 0;
528:
529: udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
530: if (!udc_device_name) {
531: ret = -ENOMEM;
532: goto out_free_udc_driver_name;
533: }
534: ret = strscpy(udc_device_name, &arg.device_name[0],
535: UDC_NAME_LENGTH_MAX);
536: if (ret < 0)
537: goto out_free_udc_device_name;
538: ret = 0;
539:
540: spin_lock_irqsave(&dev->lock, flags);
541: if (dev->state != STATE_DEV_OPENED) {
542: dev_dbg(dev->dev, "fail, device is not opened\n");
543: ret = -EINVAL;
544: goto out_unlock;
545: }
546: dev->udc_name = udc_driver_name;
547:
548: dev->driver.function = DRIVER_DESC;
549: dev->driver.max_speed = arg.speed;
550: dev->driver.setup = gadget_setup;
551: dev->driver.disconnect = gadget_disconnect;
552: dev->driver.bind = gadget_bind;
553: dev->driver.unbind = gadget_unbind;
554: dev->driver.suspend = gadget_suspend;
555: dev->driver.resume = gadget_resume;
556: dev->driver.reset = gadget_reset;
557: dev->driver.driver.name = driver_driver_name;
558: dev->driver.udc_name = udc_device_name;
559: dev->driver.match_existing_only = 1;
560: dev->driver_id_number = driver_id_number;
561:
562: dev->state = STATE_DEV_INITIALIZED;
563: spin_unlock_irqrestore(&dev->lock, flags);
564: return ret;
565:
566: out_unlock:
567: spin_unlock_irqrestore(&dev->lock, flags);
568: out_free_udc_device_name:
569: kfree(udc_device_name);
570: out_free_udc_driver_name:
571: kfree(udc_driver_name);
572: out_free_driver_driver_name:
573: kfree(driver_driver_name);
574: out_free_driver_id_number:
575: ida_free(&driver_id_numbers, driver_id_number);
576: return ret;
577: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 211/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 89518
output: 46
thoughts: 26
Thoughts: Okay, here's my attempt at summarizing those thoughts from an expert's perspective, using a first-person perspective:
**Debugging the Device Driver Interaction**
Alright, I'm digging into this code, trying to understand how this device is interacting with the system. And something immediately stands out to me: I don't see any explicit use of `dev->gadget`. That's unusual, especially given the context I'm in. It's almost like it's bypassing the standard gadget framework somehow. This could be a significant design choice, maybe for performance or some specific hardware requirement, but I need to understand why.
Wait a minute, hold on. What about `raw_ioctl_event_fetch`? Itβs possible that it is using this to get the information it needs, instead of dev->gadget. I need to make sure I am not missing anything that would affect the gadget as a whole. I need to look closer at that function, how it interacts, and what data it's extracting. I need to trace the data flow to be certain this is the case.
|
| 212/4 |
2026/07/29 08:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_event_fetch]
Results: map[SourceCode: 615: static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
616: {
617: struct usb_raw_event arg;
618: unsigned long flags;
619: struct usb_raw_event *event;
620: uint32_t length;
621:
622: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
623: return -EFAULT;
624:
625: spin_lock_irqsave(&dev->lock, flags);
626: if (dev->state != STATE_DEV_RUNNING) {
627: dev_dbg(dev->dev, "fail, device is not running\n");
628: spin_unlock_irqrestore(&dev->lock, flags);
629: return -EINVAL;
630: }
631: if (!dev->gadget) {
632: dev_dbg(dev->dev, "fail, gadget is not bound\n");
633: spin_unlock_irqrestore(&dev->lock, flags);
634: return -EBUSY;
635: }
636: spin_unlock_irqrestore(&dev->lock, flags);
637:
638: event = raw_event_queue_fetch(&dev->queue);
639: if (PTR_ERR(event) == -EINTR) {
640: dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
641: return -EINTR;
642: }
643: if (IS_ERR(event)) {
644: dev_err(&dev->gadget->dev, "failed to fetch event\n");
645: spin_lock_irqsave(&dev->lock, flags);
646: dev->state = STATE_DEV_FAILED;
647: spin_unlock_irqrestore(&dev->lock, flags);
648: return -ENODEV;
649: }
650: length = min(arg.length, event->length);
651: if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
652: kfree(event);
653: return -EFAULT;
654: }
655:
656: kfree(event);
657: return 0;
658: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 213/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 90552
output: 360
thoughts: 139
Thoughts: **UAF Vulnerability in `raw_ioctl_event_fetch`**
Okay, so I'm looking at this `raw_ioctl_event_fetch` function, and something's really jumping out at me. It's dropping `dev->lock` *before* it makes calls to `dev_dbg` and `dev_err` with `dev->gadget->dev`. That's a huge red flag! It's a textbook use-after-free, a UAF, right here.
The problem, as I see it, is that if `gadget_unbind` gets called concurrently, while `raw_event_queue_fetch` is sitting there waiting, `dev->gadget` will be freed. Then, *boom*, when `raw_event_queue_fetch` returns and we dereference `dev->gadget->dev`, we're accessing freed memory. That's just asking for trouble, unpredictable behavior, potential arbitrary code execution... the whole nasty package.
The solution is staring me in the face, isn't it? We need to use `dev->dev` instead of `dev->gadget->dev` in those debug and error calls. That way, we're not dependent on the `gadget` member remaining valid, and we avoid the UAF entirely. Seems like a straightforward fix, but this type of thing can be critical. Better get this patched up before it becomes a problem.
|
| 214/4 |
2026/07/29 08:31 |
tool |
codeeditor |
0m
Args: map[CurrentCode: event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length); NewCode: event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length); SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 215/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 91364
output: 39
thoughts: 38
Thoughts: Okay, here's my thought process on that code snippet, articulated as though it were my own internal monologue:
**Investigating Potential Race Conditions**
Right, so I'm seeing a reference to `dev->gadget`. I need to immediately assess the potential for a race condition here. The core question bubbling up is: are there *any* other spots in this file where `dev->gadget` is dereferenced, but *without* the `dev->lock` being held? That's the crucial point. Without the lock, concurrent access could lead to all sorts of nasty problems: data corruption, crashes, you name it.
My instinct is to be thorough. I'm going to scan the entire file. I'm looking for *every* instance of `dev->gadget`. I need a complete picture, a definitive list of where this variable is accessed and under what circumstances. This isn't just about finding the current instance; it's about eliminating blind spots and ensuring I haven't missed a subtle interaction elsewhere that could be problematic. I need to be 100% sure this code is safe.
|
| 216/4 |
2026/07/29 08:31 |
tool |
grepper |
0m
Args: map[Expression:dev->gadget PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=205=static void dev_free(struct kref *kref)
--
drivers/usb/gadget/legacy/raw_gadget.c-216- if (dev->ep0_urb_queued)
drivers/usb/gadget/legacy/raw_gadget.c:217: usb_ep_dequeue(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c:218: usb_ep_free_request(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c-219- }
--
drivers/usb/gadget/legacy/raw_gadget.c=281=static int gadget_bind(struct usb_gadget *gadget,
--
drivers/usb/gadget/legacy/raw_gadget.c-304- dev->req->complete = gadget_ep0_complete;
drivers/usb/gadget/legacy/raw_gadget.c:305: dev->gadget = gadget;
drivers/usb/gadget/legacy/raw_gadget.c:306: gadget_for_each_ep(ep, dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-307- dev->eps[i].ep = ep;
--
drivers/usb/gadget/legacy/raw_gadget.c=444=static int raw_release(struct inode *inode, struct file *fd)
--
drivers/usb/gadget/legacy/raw_gadget.c-452- dev->state = STATE_DEV_CLOSED;
drivers/usb/gadget/legacy/raw_gadget.c:453: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-454- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c-456- }
drivers/usb/gadget/legacy/raw_gadget.c:457: if (dev->gadget_registered)
drivers/usb/gadget/legacy/raw_gadget.c-458- unregister = true;
drivers/usb/gadget/legacy/raw_gadget.c:459: dev->gadget_registered = false;
drivers/usb/gadget/legacy/raw_gadget.c-460- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=579=static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-604- }
drivers/usb/gadget/legacy/raw_gadget.c:605: dev->gadget_registered = true;
drivers/usb/gadget/legacy/raw_gadget.c-606- dev->state = STATE_DEV_RUNNING;
--
drivers/usb/gadget/legacy/raw_gadget.c=615=static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-630- }
drivers/usb/gadget/legacy/raw_gadget.c:631: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-632- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-639- if (PTR_ERR(event) == -EINTR) {
drivers/usb/gadget/legacy/raw_gadget.c:640: dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c-641- return -EINTR;
--
drivers/usb/gadget/legacy/raw_gadget.c-643- if (IS_ERR(event)) {
drivers/usb/gadget/legacy/raw_gadget.c:644: dev_err(&dev->gadget->dev, "failed to fetch event\n");
drivers/usb/gadget/legacy/raw_gadget.c-645- spin_lock_irqsave(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=683=static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
--
drivers/usb/gadget/legacy/raw_gadget.c-694- }
drivers/usb/gadget/legacy/raw_gadget.c:695: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-696- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-700- if (dev->ep0_urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
drivers/usb/gadget/legacy/raw_gadget.c-702- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-706- (!in && !dev->ep0_out_pending)) {
drivers/usb/gadget/legacy/raw_gadget.c:707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
drivers/usb/gadget/legacy/raw_gadget.c-708- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-727-
drivers/usb/gadget/legacy/raw_gadget.c:728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
drivers/usb/gadget/legacy/raw_gadget.c-729- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:730: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-731- "fail, usb_ep_queue returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-737- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c:739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c-740- wait_for_completion(&dev->ep0_done);
--
drivers/usb/gadget/legacy/raw_gadget.c=796=static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-808- }
drivers/usb/gadget/legacy/raw_gadget.c:809: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-810- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-814- if (dev->ep0_urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:815: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
drivers/usb/gadget/legacy/raw_gadget.c-816- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-819- if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
drivers/usb/gadget/legacy/raw_gadget.c:820: dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
drivers/usb/gadget/legacy/raw_gadget.c-821- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-824-
drivers/usb/gadget/legacy/raw_gadget.c:825: ret = usb_ep_set_halt(dev->gadget->ep0);
drivers/usb/gadget/legacy/raw_gadget.c-826- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:827: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-828- "fail, usb_ep_set_halt returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c=840=static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-867- }
drivers/usb/gadget/legacy/raw_gadget.c:868: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-869- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-878- continue;
drivers/usb/gadget/legacy/raw_gadget.c:879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
drivers/usb/gadget/legacy/raw_gadget.c-880- continue;
--
drivers/usb/gadget/legacy/raw_gadget.c-886- if (ret < 0) {
drivers/usb/gadget/legacy/raw_gadget.c:887: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-888- "fail, usb_ep_enable returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-892- if (!ep->req) {
drivers/usb/gadget/legacy/raw_gadget.c:893: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-894- "fail, usb_ep_alloc_request failed\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-905- if (!ep_props_matched) {
drivers/usb/gadget/legacy/raw_gadget.c:906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
drivers/usb/gadget/legacy/raw_gadget.c-907- ret = -EINVAL;
drivers/usb/gadget/legacy/raw_gadget.c-908- } else {
drivers/usb/gadget/legacy/raw_gadget.c:909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
drivers/usb/gadget/legacy/raw_gadget.c-910- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c=920=static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-930- }
drivers/usb/gadget/legacy/raw_gadget.c:931: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-932- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-941- if (dev->eps[i].state == STATE_EP_DISABLED) {
drivers/usb/gadget/legacy/raw_gadget.c:942: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
drivers/usb/gadget/legacy/raw_gadget.c-943- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-946- if (dev->eps[i].disabling) {
drivers/usb/gadget/legacy/raw_gadget.c:947: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-948- "fail, disable already in progress\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-952- if (dev->eps[i].urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:953: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-954- "fail, waiting for urb completion\n");
--
drivers/usb/gadget/legacy/raw_gadget.c=974=static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
--
drivers/usb/gadget/legacy/raw_gadget.c-985- }
drivers/usb/gadget/legacy/raw_gadget.c:986: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-987- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-996- if (dev->eps[i].state == STATE_EP_DISABLED) {
drivers/usb/gadget/legacy/raw_gadget.c:997: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
drivers/usb/gadget/legacy/raw_gadget.c-998- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-1001- if (dev->eps[i].disabling) {
drivers/usb/gadget/legacy/raw_gadget.c:1002: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1003- "fail, disable is in progress\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1007- if (dev->eps[i].urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:1008: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1009- "fail, waiting for urb completion\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1013- if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
drivers/usb/gadget/legacy/raw_gadget.c:1014: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1015- "fail, can't halt/wedge ISO endpoint\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1022- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:1023: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1024- "fail, usb_ep_set_halt returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-1027- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:1028: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1029- "fail, usb_ep_clear_halt returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-1032- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:1033: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1034- "fail, usb_ep_set_wedge returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c=1058=static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
--
drivers/usb/gadget/legacy/raw_gadget.c-1071- }
drivers/usb/gadget/legacy/raw_gadget.c:1072: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1073- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1077- if (io->ep >= dev->eps_num) {
drivers/usb/gadget/legacy/raw_gadget.c:1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
drivers/usb/gadget/legacy/raw_gadget.c-1079- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-1083- if (ep->state != STATE_EP_ENABLED) {
drivers/usb/gadget/legacy/raw_gadget.c:1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
drivers/usb/gadget/legacy/raw_gadget.c-1085- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-1088- if (ep->disabling) {
drivers/usb/gadget/legacy/raw_gadget.c:1089: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1090- "fail, endpoint is already being disabled\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1094- if (ep->urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
drivers/usb/gadget/legacy/raw_gadget.c-1096- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-1099- if (in != usb_endpoint_dir_in(ep->ep->desc)) {
drivers/usb/gadget/legacy/raw_gadget.c:1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
drivers/usb/gadget/legacy/raw_gadget.c-1101- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-1115- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:1116: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1117- "fail, usb_ep_queue returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-1123- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c-1125- usb_ep_dequeue(ep->ep, ep->req);
--
drivers/usb/gadget/legacy/raw_gadget.c=1182=static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1194- }
drivers/usb/gadget/legacy/raw_gadget.c:1195: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1196- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1199- }
drivers/usb/gadget/legacy/raw_gadget.c:1200: usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
drivers/usb/gadget/legacy/raw_gadget.c-1201-
--
drivers/usb/gadget/legacy/raw_gadget.c=1207=static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1217- }
drivers/usb/gadget/legacy/raw_gadget.c:1218: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1219- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1222- }
drivers/usb/gadget/legacy/raw_gadget.c:1223: usb_gadget_vbus_draw(dev->gadget, 2 * value);
drivers/usb/gadget/legacy/raw_gadget.c-1224-
--
drivers/usb/gadget/legacy/raw_gadget.c=1247=static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1266- }
drivers/usb/gadget/legacy/raw_gadget.c:1267: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1268- dev_dbg(dev->dev, "fail, gadget is not bound\n");
]
|
| 217/4 |
2026/07/29 08:31 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 97524
output: 30
thoughts: 343
Thoughts: **Reviewing the USB Gadget Driver's Locking Strategy**
Okay, let's recap the locking situation in this USB gadget driver. Looking at `raw_ioctl_ep0_stall`, it's straightforward; the `dev->lock` is held, ensuring thread safety around the check for an already queued URB. Similarly, `raw_ioctl_ep_enable` and `raw_ioctl_ep_set_clear_halt_wedge` are also protected by the same `dev->lock`, which means the calls to `usb_ep_enable` and `usb_ep_set_halt` within those functions are safe from race conditions, which is exactly what I expect.
Previously, I had flagged `raw_process_ep0_io`, `raw_process_ep_io`, and `raw_ioctl_event_fetch` as potential areas of concern regarding locking and concurrency. However, I've just verified that those issues have been addressed.
Now, to be absolutely certain, I'll leverage `patch-diff` to meticulously review all of the changes again, just to be completely confident that the identified race conditions have been effectively neutralized and that no new issues have crept in during the fixes. A thorough review is always a good idea when dealing with this level of complexity.
|
| 218/4 |
2026/07/29 08:32 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..4fe8bbf7a 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -160,28 +160,30 @@ enum dev_state {
struct raw_dev {
struct kref count;
spinlock_t lock;
const char *udc_name;
struct usb_gadget_driver driver;
/* Reference to misc device: */
struct device *dev;
/* Make driver names unique */
int driver_id_number;
/* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
int eps_num;
struct completion ep0_done;
struct raw_event_queue queue;
};
@@ -321,20 +323,33 @@ static int gadget_bind(struct usb_gadget *gadget,
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
}
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->state = STATE_DEV_FAILED;
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
static int gadget_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
int ret = 0;
@@ -615,44 +630,44 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
{
struct usb_raw_event arg;
unsigned long flags;
struct usb_raw_event *event;
uint32_t length;
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
return -EFAULT;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EINVAL;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
kfree(event);
return -EFAULT;
}
kfree(event);
return 0;
}
@@ -683,74 +698,99 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->ep0_urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !dev->ep0_in_pending) ||
(!in && !dev->ep0_out_pending)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EBUSY;
goto out_unlock;
}
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -920,54 +960,58 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (i < 0 || i >= dev->eps_num) {
dev_dbg(dev->dev, "fail, invalid endpoint\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->eps[i].state == STATE_EP_DISABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].disabling) {
dev_dbg(&dev->gadget->dev,
"fail, disable already in progress\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -1058,85 +1102,110 @@ static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
struct raw_ep *ep;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (io->ep >= dev->eps_num) {
dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
ret = -EINVAL;
goto out_unlock;
}
ep = &dev->eps[io->ep];
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->disabling) {
dev_dbg(&dev->gadget->dev,
"fail, endpoint is already being disabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if (in != usb_endpoint_dir_in(ep->ep->desc)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EINVAL;
goto out_unlock;
}
ep->dev = dev;
ep->req->context = &done;
ep->req->complete = gadget_ep_complete;
ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
]
|
| 219/4 |
2026/07/29 08:32 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 102881
output: 55
thoughts: 15
Thoughts: Here's your summary:
**The Fix is in!**
Alright, looking at these changes, I'm satisfied. They're comprehensive and appear to address the issue completely. I've reviewed the code meticulously, and everything aligns with the requirements and the overall architecture. Now, let me break down exactly *why* this works and what we've achieved with this fix. I'll focus on the specific logic, the reasoning behind the implementation, and the potential implications for future modifications. Time to explain the nitty-gritty.
|
| 220/4 |
2026/07/29 08:32 |
tool |
codeexpert |
2m
Args: map[Question:Is there any other place where `dev->gadget` is dereferenced without `dev->lock` in `drivers/usb/gadget/legacy/raw_gadget.c`?]
Results: map[Answer:Yes, there are several other places in `drivers/usb/gadget/legacy/raw_gadget.c` where `dev->gadget` is dereferenced without holding `dev->lock`.
Here is the complete list of those occurrences:
### 1. In `raw_ioctl_event_fetch()`
* **Lines 640 and 644:**
```c
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
```
* **Explanation:** `dev->lock` is released at line 636 (`spin_unlock_irqrestore(&dev->lock, flags);`) before calling `raw_event_queue_fetch()`, and is only re-acquired at line 645 inside the `IS_ERR(event)` block.
---
### 2. In `raw_process_ep0_io()`
* **Lines 728, 730, 738, and 739:**
```c
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
```
* **Explanation:** `dev->lock` is released at line 726 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 732, 741) or after the wait completes (line 747).
---
### 3. In `raw_process_ep_io()`
* **Lines 1116 and 1124:**
```c
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
```
* **Explanation:** `dev->lock` is released at line 1112 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 1118, 1127) or after the wait completes (line 1133).
---
### 4. In `dev_free()` (Safe context, but lock is not held)
* **Lines 217 and 218:**
```c
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
```
* **Explanation:** `dev->lock` is not held here. However, since this is inside `dev_free()` (which is called via `kref_put()` when the reference count of `dev` drops to 0), there is no possibility of concurrent access, making it safe.]
|
| 221/5 |
2026/07/29 08:32 |
agent |
codeexpert |
2m
Model:
Results: map[Answer:Yes, there are several other places in `drivers/usb/gadget/legacy/raw_gadget.c` where `dev->gadget` is dereferenced without holding `dev->lock`.
Here is the complete list of those occurrences:
### 1. In `raw_ioctl_event_fetch()`
* **Lines 640 and 644:**
```c
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
```
* **Explanation:** `dev->lock` is released at line 636 (`spin_unlock_irqrestore(&dev->lock, flags);`) before calling `raw_event_queue_fetch()`, and is only re-acquired at line 645 inside the `IS_ERR(event)` block.
---
### 2. In `raw_process_ep0_io()`
* **Lines 728, 730, 738, and 739:**
```c
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
```
* **Explanation:** `dev->lock` is released at line 726 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 732, 741) or after the wait completes (line 747).
---
### 3. In `raw_process_ep_io()`
* **Lines 1116 and 1124:**
```c
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
```
* **Explanation:** `dev->lock` is released at line 1112 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 1118, 1127) or after the wait completes (line 1133).
---
### 4. In `dev_free()` (Safe context, but lock is not held)
* **Lines 217 and 218:**
```c
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
```
* **Explanation:** `dev->lock` is not held here. However, since this is inside `dev_free()` (which is called via `kref_put()` when the reference count of `dev` drops to 0), there is no possibility of concurrent access, making it safe.]
Instruction:
You are a capable Linux kernel developer tasked with researching complex questions
about kernel source code. You will be given a concrete question, and need to provide
a concrete answer.
Use tools extensively while researching the question. Don't make assumptions,
or rely on your previous knowledge about the kernel source code, use available tools
to access the actual source code.
Use all available sources of information:
- kernel source code
- documentation in the Documentation dir in the source tree
- git commits descriptions, git blame
Do not guess file names or file paths and attempt to read them without
verifying their existence first using content search or directory listing tools.
If a file, symbol, or directory is not found via content search ('grepper') or
directory listing ('codesearch-dir-index'), treat it as completely absent.
Do not attempt to guess alternative names, extensions, or directories.
Tool Selection Guidelines:
1. For C functions, structs, and variables: Prefer 'codesearch-definition-source'
or 'codesearch-struct-layout' FIRST to retrieve clean, exact definitions
without line-number guessing.
2. If symbol lookup fails (e.g., preprocessor macros, macro-generated code, or
disabled #ifdef branches), fall back to 'read-file' or 'grepper'.
3. For file headers, #include directives, preprocessor macro definitions, and
non-C files (Kconfig, Makefiles, docs): Use 'read-file' or 'grepper' directly.
Do NOT use 'git-log' to search for the presence or existence of files in the
repository. 'git-log' is only for tracing commit history of files that are
already present in the current checkout. If a file does not exist in the
current checkout, it cannot be used for reproduction.
Avoid running broad 'git-log' queries (such as searches on the entire repo)
without a specific 'PathPrefix' to restrict the scope, as these are very
expensive and will time out.
If a 'git-log' tool call times out, do not retry the query with the same broad
scope. You must specify a tighter, narrower 'PathPrefix' for subsequent queries.
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: Is there any other place where `dev->gadget` is dereferenced without `dev->lock` in `drivers/usb/gadget/legacy/raw_gadget.c`?
|
| 222/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 3921
output: 39
thoughts: 175
Thoughts: **Investigating Unprotected Dereferences in raw_gadget.c**
Okay, so the question posed is whether `dev->gadget` is dereferenced in `drivers/usb/gadget/legacy/raw_gadget.c` without being protected by a corresponding `dev->lock`. That's a good question, and worth investigating. First, I need to confirm the existence of that file. Let me quickly use `grepper` to see if `drivers/usb/gadget/legacy/raw_gadget.c` actually exists in the source tree. Once I confirm the file's existence, I'll then use `grepper` again, but this time to specifically search within that file for instances of `dev->gadget`. This will give me a starting point to assess how that member is used and whether the locking is handled correctly.
|
| 223/6 |
2026/07/29 08:32 |
tool |
grepper |
0m
Args: map[Expression:dev->gadget PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=205=static void dev_free(struct kref *kref)
--
drivers/usb/gadget/legacy/raw_gadget.c-216- if (dev->ep0_urb_queued)
drivers/usb/gadget/legacy/raw_gadget.c:217: usb_ep_dequeue(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c:218: usb_ep_free_request(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c-219- }
--
drivers/usb/gadget/legacy/raw_gadget.c=281=static int gadget_bind(struct usb_gadget *gadget,
--
drivers/usb/gadget/legacy/raw_gadget.c-304- dev->req->complete = gadget_ep0_complete;
drivers/usb/gadget/legacy/raw_gadget.c:305: dev->gadget = gadget;
drivers/usb/gadget/legacy/raw_gadget.c:306: gadget_for_each_ep(ep, dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-307- dev->eps[i].ep = ep;
--
drivers/usb/gadget/legacy/raw_gadget.c=444=static int raw_release(struct inode *inode, struct file *fd)
--
drivers/usb/gadget/legacy/raw_gadget.c-452- dev->state = STATE_DEV_CLOSED;
drivers/usb/gadget/legacy/raw_gadget.c:453: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-454- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c-456- }
drivers/usb/gadget/legacy/raw_gadget.c:457: if (dev->gadget_registered)
drivers/usb/gadget/legacy/raw_gadget.c-458- unregister = true;
drivers/usb/gadget/legacy/raw_gadget.c:459: dev->gadget_registered = false;
drivers/usb/gadget/legacy/raw_gadget.c-460- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=579=static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-604- }
drivers/usb/gadget/legacy/raw_gadget.c:605: dev->gadget_registered = true;
drivers/usb/gadget/legacy/raw_gadget.c-606- dev->state = STATE_DEV_RUNNING;
--
drivers/usb/gadget/legacy/raw_gadget.c=615=static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-630- }
drivers/usb/gadget/legacy/raw_gadget.c:631: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-632- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-639- if (PTR_ERR(event) == -EINTR) {
drivers/usb/gadget/legacy/raw_gadget.c:640: dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c-641- return -EINTR;
--
drivers/usb/gadget/legacy/raw_gadget.c-643- if (IS_ERR(event)) {
drivers/usb/gadget/legacy/raw_gadget.c:644: dev_err(&dev->gadget->dev, "failed to fetch event\n");
drivers/usb/gadget/legacy/raw_gadget.c-645- spin_lock_irqsave(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=683=static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
--
drivers/usb/gadget/legacy/raw_gadget.c-694- }
drivers/usb/gadget/legacy/raw_gadget.c:695: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-696- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-700- if (dev->ep0_urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
drivers/usb/gadget/legacy/raw_gadget.c-702- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-706- (!in && !dev->ep0_out_pending)) {
drivers/usb/gadget/legacy/raw_gadget.c:707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
drivers/usb/gadget/legacy/raw_gadget.c-708- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-727-
drivers/usb/gadget/legacy/raw_gadget.c:728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
drivers/usb/gadget/legacy/raw_gadget.c-729- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:730: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-731- "fail, usb_ep_queue returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-737- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c:739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c-740- wait_for_completion(&dev->ep0_done);
--
drivers/usb/gadget/legacy/raw_gadget.c=796=static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-808- }
drivers/usb/gadget/legacy/raw_gadget.c:809: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-810- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-814- if (dev->ep0_urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:815: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
drivers/usb/gadget/legacy/raw_gadget.c-816- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-819- if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
drivers/usb/gadget/legacy/raw_gadget.c:820: dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
drivers/usb/gadget/legacy/raw_gadget.c-821- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-824-
drivers/usb/gadget/legacy/raw_gadget.c:825: ret = usb_ep_set_halt(dev->gadget->ep0);
drivers/usb/gadget/legacy/raw_gadget.c-826- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:827: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-828- "fail, usb_ep_set_halt returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c=840=static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-867- }
drivers/usb/gadget/legacy/raw_gadget.c:868: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-869- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-878- continue;
drivers/usb/gadget/legacy/raw_gadget.c:879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
drivers/usb/gadget/legacy/raw_gadget.c-880- continue;
--
drivers/usb/gadget/legacy/raw_gadget.c-886- if (ret < 0) {
drivers/usb/gadget/legacy/raw_gadget.c:887: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-888- "fail, usb_ep_enable returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-892- if (!ep->req) {
drivers/usb/gadget/legacy/raw_gadget.c:893: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-894- "fail, usb_ep_alloc_request failed\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-905- if (!ep_props_matched) {
drivers/usb/gadget/legacy/raw_gadget.c:906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
drivers/usb/gadget/legacy/raw_gadget.c-907- ret = -EINVAL;
drivers/usb/gadget/legacy/raw_gadget.c-908- } else {
drivers/usb/gadget/legacy/raw_gadget.c:909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
drivers/usb/gadget/legacy/raw_gadget.c-910- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c=920=static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-930- }
drivers/usb/gadget/legacy/raw_gadget.c:931: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-932- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-941- if (dev->eps[i].state == STATE_EP_DISABLED) {
drivers/usb/gadget/legacy/raw_gadget.c:942: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
drivers/usb/gadget/legacy/raw_gadget.c-943- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-946- if (dev->eps[i].disabling) {
drivers/usb/gadget/legacy/raw_gadget.c:947: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-948- "fail, disable already in progress\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-952- if (dev->eps[i].urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:953: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-954- "fail, waiting for urb completion\n");
--
drivers/usb/gadget/legacy/raw_gadget.c=974=static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
--
drivers/usb/gadget/legacy/raw_gadget.c-985- }
drivers/usb/gadget/legacy/raw_gadget.c:986: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-987- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-996- if (dev->eps[i].state == STATE_EP_DISABLED) {
drivers/usb/gadget/legacy/raw_gadget.c:997: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
drivers/usb/gadget/legacy/raw_gadget.c-998- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-1001- if (dev->eps[i].disabling) {
drivers/usb/gadget/legacy/raw_gadget.c:1002: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1003- "fail, disable is in progress\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1007- if (dev->eps[i].urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:1008: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1009- "fail, waiting for urb completion\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1013- if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
drivers/usb/gadget/legacy/raw_gadget.c:1014: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1015- "fail, can't halt/wedge ISO endpoint\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1022- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:1023: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1024- "fail, usb_ep_set_halt returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-1027- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:1028: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1029- "fail, usb_ep_clear_halt returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-1032- if (ret < 0)
drivers/usb/gadget/legacy/raw_gadget.c:1033: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1034- "fail, usb_ep_set_wedge returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c=1058=static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
--
drivers/usb/gadget/legacy/raw_gadget.c-1071- }
drivers/usb/gadget/legacy/raw_gadget.c:1072: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1073- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1077- if (io->ep >= dev->eps_num) {
drivers/usb/gadget/legacy/raw_gadget.c:1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
drivers/usb/gadget/legacy/raw_gadget.c-1079- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-1083- if (ep->state != STATE_EP_ENABLED) {
drivers/usb/gadget/legacy/raw_gadget.c:1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
drivers/usb/gadget/legacy/raw_gadget.c-1085- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-1088- if (ep->disabling) {
drivers/usb/gadget/legacy/raw_gadget.c:1089: dev_dbg(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1090- "fail, endpoint is already being disabled\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1094- if (ep->urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c:1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
drivers/usb/gadget/legacy/raw_gadget.c-1096- ret = -EBUSY;
--
drivers/usb/gadget/legacy/raw_gadget.c-1099- if (in != usb_endpoint_dir_in(ep->ep->desc)) {
drivers/usb/gadget/legacy/raw_gadget.c:1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
drivers/usb/gadget/legacy/raw_gadget.c-1101- ret = -EINVAL;
--
drivers/usb/gadget/legacy/raw_gadget.c-1115- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:1116: dev_err(&dev->gadget->dev,
drivers/usb/gadget/legacy/raw_gadget.c-1117- "fail, usb_ep_queue returned %d\n", ret);
--
drivers/usb/gadget/legacy/raw_gadget.c-1123- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c:1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c-1125- usb_ep_dequeue(ep->ep, ep->req);
--
drivers/usb/gadget/legacy/raw_gadget.c=1182=static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1194- }
drivers/usb/gadget/legacy/raw_gadget.c:1195: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1196- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1199- }
drivers/usb/gadget/legacy/raw_gadget.c:1200: usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
drivers/usb/gadget/legacy/raw_gadget.c-1201-
--
drivers/usb/gadget/legacy/raw_gadget.c=1207=static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1217- }
drivers/usb/gadget/legacy/raw_gadget.c:1218: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1219- dev_dbg(dev->dev, "fail, gadget is not bound\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-1222- }
drivers/usb/gadget/legacy/raw_gadget.c:1223: usb_gadget_vbus_draw(dev->gadget, 2 * value);
drivers/usb/gadget/legacy/raw_gadget.c-1224-
--
drivers/usb/gadget/legacy/raw_gadget.c=1247=static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1266- }
drivers/usb/gadget/legacy/raw_gadget.c:1267: if (!dev->gadget) {
drivers/usb/gadget/legacy/raw_gadget.c-1268- dev_dbg(dev->dev, "fail, gadget is not bound\n");
]
|
| 224/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10162
output: 42
thoughts: 121
Thoughts: **Analyzing Potential Race Conditions in `raw_gadget.c`**
Okay, so the question is pretty specific: are there any instances where `dev->gadget` in `drivers/usb/gadget/legacy/raw_gadget.c` is being accessed without the protection of `dev->lock`? This sounds like a potential race condition, which is a classic issue in multi-threaded code.
Before I jump to a direct answer, I want to back up a bit and establish some context. Has there been a recent bug report or a related fix that addresses this exact scenario? I'll probably find more information using the git log. I should search for commits touching `raw_gadget.c` that involve `dev->gadget` or the lock. This should uncover any relevant discussions or fixes that have already been made and potentially lead me directly to the answer, or at least a good starting point for investigation. Knowing the history will probably give valuable hints about what to look for now.
|
| 225/6 |
2026/07/29 08:32 |
tool |
git-log |
0m
Args: map[CodeRegexp:dev->gadget PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:0d48aee69c71 usb: raw-gadget: return -EINVAL if no proper ep address available
c61769bd4777 usb: raw-gadget: support stalling/halting/wedging endpoints
97df5e5758f7 usb: raw-gadget: fix gadget endpoint selection
fdd10499de0d usb: raw-gadget: fix raw_event_queue_fetch locking
f2c2e717642c usb: gadget: add raw-gadget interface
]
|
| 226/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 10678
output: 25
thoughts: 34
Thoughts: Okay, let's dive into this commit, `fdd10499de0d`, specifically the one dealing with USB's raw-gadget functionality β that's a pretty interesting area, especially when it comes to low-level interaction. I need to understand the details of this fix: `usb: raw-gadget: fix raw_event_queue_fetch locking`. So, clearly, the core concern is how the event queue is being managed, specifically with respect to locking. It sounds like there was a concurrency issue. Perhaps a race condition? Maybe the `raw_event_queue_fetch` function wasnβt properly protected when accessing the shared event queue, leading to data corruption or unexpected behavior. I'm going to have to dig into the code and determine exactly where the locking was insufficient. Is it a simple missing mutex? A more complex issue involving spinlocks or read-write locks? The commit message should give me some clues, but I'll probably need to look at the surrounding code to grasp the full picture. My initial assessment is the issue likely involved improperly synchronizing access to the queue when fetching events, thus resulting in potential data corruption.
|
| 227/6 |
2026/07/29 08:32 |
tool |
git-show |
0m
Args:
Results: map[Output:commit fdd10499de0dbcd2cdfe8108ed162bb00fe6f276
Author: Andrey Konovalov <andreyknvl@gmail.com>
Date: Tue Apr 7 16:47:54 2020 +0200
usb: raw-gadget: fix raw_event_queue_fetch locking
If queue->size check in raw_event_queue_fetch() fails (which normally
shouldn't happen, that check is a fail-safe), the function returns
without reenabling interrupts. This patch fixes that issue, along with
propagating the cause of failure to the function caller.
Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Link: https://lore.kernel.org/r/9f7ce7a1472cfb9447f6c5a494186fa1f2670f6f.1586270396.git.andreyknvl@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index e490ffa1f58b..ca7d95bf7397 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -81,6 +81,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue,
static struct usb_raw_event *raw_event_queue_fetch(
struct raw_event_queue *queue)
{
+ int ret;
unsigned long flags;
struct usb_raw_event *event;
@@ -89,11 +90,18 @@ static struct usb_raw_event *raw_event_queue_fetch(
* there's at least one event queued by decrementing the semaphore,
* and then take the lock to protect queue struct fields.
*/
- if (down_interruptible(&queue->sema))
- return NULL;
+ ret = down_interruptible(&queue->sema);
+ if (ret)
+ return ERR_PTR(ret);
spin_lock_irqsave(&queue->lock, flags);
- if (WARN_ON(!queue->size))
- return NULL;
+ /*
+ * queue->size must have the same value as queue->sema counter (before
+ * the down_interruptible() call above), so this check is a fail-safe.
+ */
+ if (WARN_ON(!queue->size)) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ERR_PTR(-ENODEV);
+ }
event = queue->events[0];
queue->size--;
memmove(&queue->events[0], &queue->events[1],
@@ -522,10 +530,17 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
- if (!event) {
+ if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
+ if (IS_ERR(event)) {
+ dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENODEV;
+ }
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length))
return -EFAULT;
]
|
| 228/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 12086
output: 42
thoughts: 305
Thoughts: **Analyzing Potential Race Conditions in Raw Gadget Driver**
Okay, so I'm looking at this `raw_ioctl_event_fetch` function, specifically that block of code. My immediate concern is that we're releasing the `dev->lock` using `spin_unlock_irqrestore(&dev->lock, flags);` *before* dereferencing `dev->gadget` in both `dev_dbg` and `dev_err`. That feels like a potential race condition screaming to be exploited! What if `dev` goes away or changes significantly between the unlock and the debugging calls?
My thought process now is: Is this the *only* place where we see this pattern? Where `dev->gadget` is used without the lock held? This could be a significant vulnerability. We need to analyze the entire driver, or at least the critical sections around `dev->lock` and any access to `dev->gadget`. I need to understand the full context.
My plan is to meticulously go through the `drivers/usb/gadget/legacy/raw_gadget.c` file. I'll need to use my tools to do this efficiently. I'll start with a `read-file` and then use that to find where `dev->lock` is held and released, and precisely where `dev->gadget` is touched. I'll probably start by figuring out the size of the file. That will help me plan how to read it in manageable chunks. Then, I can focus on those areas of interest to determine if this pattern is isolated, or replicated elsewhere. Gotta make sure this isn't a systemic flaw.
|
| 229/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1 LineCount:100]
Results: map[Contents: 1: // SPDX-License-Identifier: GPL-2.0
2: /*
3: * USB Raw Gadget driver.
4: * See Documentation/usb/raw-gadget.rst for more details.
5: *
6: * Copyright (c) 2020 Google, Inc.
7: * Author: Andrey Konovalov <andreyknvl@gmail.com>
8: */
9:
10: #include <linux/compiler.h>
11: #include <linux/ctype.h>
12: #include <linux/debugfs.h>
13: #include <linux/delay.h>
14: #include <linux/idr.h>
15: #include <linux/kref.h>
16: #include <linux/miscdevice.h>
17: #include <linux/module.h>
18: #include <linux/semaphore.h>
19: #include <linux/sched.h>
20: #include <linux/slab.h>
21: #include <linux/uaccess.h>
22: #include <linux/wait.h>
23:
24: #include <linux/usb.h>
25: #include <linux/usb/ch9.h>
26: #include <linux/usb/ch11.h>
27: #include <linux/usb/gadget.h>
28: #include <linux/usb/composite.h>
29:
30: #include <uapi/linux/usb/raw_gadget.h>
31:
32: #define DRIVER_DESC "USB Raw Gadget"
33: #define DRIVER_NAME "raw-gadget"
34:
35: MODULE_DESCRIPTION(DRIVER_DESC);
36: MODULE_AUTHOR("Andrey Konovalov");
37: MODULE_LICENSE("GPL");
38:
39: /*----------------------------------------------------------------------*/
40:
41: static DEFINE_IDA(driver_id_numbers);
42: #define DRIVER_DRIVER_NAME_LENGTH_MAX 32
43: #define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE
44:
45: #define RAW_EVENT_QUEUE_SIZE 16
46:
47: struct raw_event_queue {
48: /* See the comment in raw_event_queue_fetch() for locking details. */
49: spinlock_t lock;
50: struct semaphore sema;
51: struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE];
52: int size;
53: };
54:
55: static void raw_event_queue_init(struct raw_event_queue *queue)
56: {
57: spin_lock_init(&queue->lock);
58: sema_init(&queue->sema, 0);
59: queue->size = 0;
60: }
61:
62: static int raw_event_queue_add(struct raw_event_queue *queue,
63: enum usb_raw_event_type type, size_t length, const void *data)
64: {
65: unsigned long flags;
66: struct usb_raw_event *event;
67:
68: spin_lock_irqsave(&queue->lock, flags);
69: if (queue->size >= RAW_EVENT_QUEUE_SIZE) {
70: spin_unlock_irqrestore(&queue->lock, flags);
71: return -ENOMEM;
72: }
73: event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
74: if (!event) {
75: spin_unlock_irqrestore(&queue->lock, flags);
76: return -ENOMEM;
77: }
78: event->type = type;
79: event->length = length;
80: if (event->length)
81: memcpy(&event->data[0], data, length);
82: queue->events[queue->size] = event;
83: queue->size++;
84: up(&queue->sema);
85: spin_unlock_irqrestore(&queue->lock, flags);
86: return 0;
87: }
88:
89: static struct usb_raw_event *raw_event_queue_fetch(
90: struct raw_event_queue *queue)
91: {
92: int ret;
93: unsigned long flags;
94: struct usb_raw_event *event;
95:
96: /*
97: * This function can be called concurrently. We first check that
98: * there's at least one event queued by decrementing the semaphore,
99: * and then take the lock to protect queue struct fields.
100: */
]
|
| 230/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 14168
output: 44
thoughts: 15
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and delivered in the first person:
**Analyzing the Relevant Section**
Right, let's focus our attention on lines 101 through 250 of this document. My immediate goal is to establish the core arguments presented within this specific section. Given my expertise, I'm already anticipating the potential nuances and complexities that might be contained within this range, so I'll be looking for key evidence, methodologies employed, and any potential points of contention or areas that require deeper analysis or clarification, and any obvious holes or flaws. What are the key contributions in relation to the wider literature and current understanding of the field? I'll also be considering the structure and flow of the argument β how well does it build upon prior points? Are any assumptions clearly stated or perhaps left implicit, and how do these assumptions impact the validity of the reasoning? I'll be particularly watchful for any novel frameworks, data sets, or analytical techniques employed here. I'll need to rapidly assess their value and any limitations.
|
| 231/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:101 LineCount:150]
Results: map[Contents: 101: ret = down_interruptible(&queue->sema);
102: if (ret)
103: return ERR_PTR(ret);
104: spin_lock_irqsave(&queue->lock, flags);
105: /*
106: * queue->size must have the same value as queue->sema counter (before
107: * the down_interruptible() call above), so this check is a fail-safe.
108: */
109: if (WARN_ON(!queue->size)) {
110: spin_unlock_irqrestore(&queue->lock, flags);
111: return ERR_PTR(-ENODEV);
112: }
113: event = queue->events[0];
114: queue->size--;
115: memmove(&queue->events[0], &queue->events[1],
116: queue->size * sizeof(queue->events[0]));
117: spin_unlock_irqrestore(&queue->lock, flags);
118: return event;
119: }
120:
121: static void raw_event_queue_destroy(struct raw_event_queue *queue)
122: {
123: int i;
124:
125: for (i = 0; i < queue->size; i++)
126: kfree(queue->events[i]);
127: queue->size = 0;
128: }
129:
130: /*----------------------------------------------------------------------*/
131:
132: struct raw_dev;
133:
134: enum ep_state {
135: STATE_EP_DISABLED,
136: STATE_EP_ENABLED,
137: };
138:
139: struct raw_ep {
140: struct raw_dev *dev;
141: enum ep_state state;
142: struct usb_ep *ep;
143: u8 addr;
144: struct usb_request *req;
145: bool urb_queued;
146: bool disabling;
147: ssize_t status;
148: };
149:
150: enum dev_state {
151: STATE_DEV_INVALID = 0,
152: STATE_DEV_OPENED,
153: STATE_DEV_INITIALIZED,
154: STATE_DEV_REGISTERING,
155: STATE_DEV_RUNNING,
156: STATE_DEV_CLOSED,
157: STATE_DEV_FAILED
158: };
159:
160: struct raw_dev {
161: struct kref count;
162: spinlock_t lock;
163:
164: const char *udc_name;
165: struct usb_gadget_driver driver;
166:
167: /* Reference to misc device: */
168: struct device *dev;
169:
170: /* Make driver names unique */
171: int driver_id_number;
172:
173: /* Protected by lock: */
174: enum dev_state state;
175: bool gadget_registered;
176: struct usb_gadget *gadget;
177: struct usb_request *req;
178: bool ep0_in_pending;
179: bool ep0_out_pending;
180: bool ep0_urb_queued;
181: ssize_t ep0_status;
182: struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
183: int eps_num;
184:
185: struct completion ep0_done;
186: struct raw_event_queue queue;
187: };
188:
189: static struct raw_dev *dev_new(void)
190: {
191: struct raw_dev *dev;
192:
193: dev = kzalloc_obj(*dev);
194: if (!dev)
195: return NULL;
196: /* Matches kref_put() in raw_release(). */
197: kref_init(&dev->count);
198: spin_lock_init(&dev->lock);
199: init_completion(&dev->ep0_done);
200: raw_event_queue_init(&dev->queue);
]
|
| 232/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 15801
output: 44
thoughts: 15
Thoughts: Okay, here's my interpretation of that request, formatted as requested:
**Diving into the Nuances of Lines 201-350**
Alright, let's get down to brass tacks. Based on the prompt, it seems we're zeroing in on a specific section β lines 201 through 350. My expert eye immediately starts scanning for the critical concepts or arguments likely covered in that range. I'm mentally parsing where this segment sits within the broader text. What precedes it? What comes after? That context is going to be vital for a full understanding.
My initial thought is to look for key terms and the structure of the ideas presented. Am I looking at a definition, an argument, a case study, or a comparison? I'm already formulating questions: Is this a critical juncture in the author's development of their thought? Are they introducing a new perspective? Is this the place where the author substantiates their claims with evidence, perhaps a key set of data, or a complex analogy? What are the key takeaways I'll need to absorb and critically evaluate here? I'm going to carefully read this section with a very keen eye on the details. I know this specific section contains the key to understanding a much larger point. It's time to dig in and do the work.
|
| 233/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:201 LineCount:150]
Results: map[Contents: 201: dev->driver_id_number = -1;
202: return dev;
203: }
204:
205: static void dev_free(struct kref *kref)
206: {
207: struct raw_dev *dev = container_of(kref, struct raw_dev, count);
208: int i;
209:
210: kfree(dev->udc_name);
211: kfree(dev->driver.udc_name);
212: kfree(dev->driver.driver.name);
213: if (dev->driver_id_number >= 0)
214: ida_free(&driver_id_numbers, dev->driver_id_number);
215: if (dev->req) {
216: if (dev->ep0_urb_queued)
217: usb_ep_dequeue(dev->gadget->ep0, dev->req);
218: usb_ep_free_request(dev->gadget->ep0, dev->req);
219: }
220: raw_event_queue_destroy(&dev->queue);
221: for (i = 0; i < dev->eps_num; i++) {
222: if (dev->eps[i].state == STATE_EP_DISABLED)
223: continue;
224: usb_ep_disable(dev->eps[i].ep);
225: usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
226: kfree(dev->eps[i].ep->desc);
227: dev->eps[i].state = STATE_EP_DISABLED;
228: }
229: kfree(dev);
230: }
231:
232: /*----------------------------------------------------------------------*/
233:
234: static int raw_queue_event(struct raw_dev *dev,
235: enum usb_raw_event_type type, size_t length, const void *data)
236: {
237: int ret = 0;
238: unsigned long flags;
239:
240: ret = raw_event_queue_add(&dev->queue, type, length, data);
241: if (ret < 0) {
242: spin_lock_irqsave(&dev->lock, flags);
243: dev->state = STATE_DEV_FAILED;
244: spin_unlock_irqrestore(&dev->lock, flags);
245: }
246: return ret;
247: }
248:
249: static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
250: {
251: struct raw_dev *dev = req->context;
252: unsigned long flags;
253:
254: spin_lock_irqsave(&dev->lock, flags);
255: if (req->status)
256: dev->ep0_status = req->status;
257: else
258: dev->ep0_status = req->actual;
259: if (dev->ep0_in_pending)
260: dev->ep0_in_pending = false;
261: else
262: dev->ep0_out_pending = false;
263: spin_unlock_irqrestore(&dev->lock, flags);
264:
265: complete(&dev->ep0_done);
266: }
267:
268: static u8 get_ep_addr(const char *name)
269: {
270: /* If the endpoint has fixed function (named as e.g. "ep12out-bulk"),
271: * parse the endpoint address from its name. We deliberately use
272: * deprecated simple_strtoul() function here, as the number isn't
273: * followed by '\0' nor '\n'.
274: */
275: if (isdigit(name[2]))
276: return simple_strtoul(&name[2], NULL, 10);
277: /* Otherwise the endpoint is configurable (named as e.g. "ep-a"). */
278: return USB_RAW_EP_ADDR_ANY;
279: }
280:
281: static int gadget_bind(struct usb_gadget *gadget,
282: struct usb_gadget_driver *driver)
283: {
284: int ret = 0, i = 0;
285: struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
286: struct usb_request *req;
287: struct usb_ep *ep;
288: unsigned long flags;
289:
290: if (strcmp(gadget->name, dev->udc_name) != 0)
291: return -ENODEV;
292:
293: set_gadget_data(gadget, dev);
294: req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
295: if (!req) {
296: dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
297: set_gadget_data(gadget, NULL);
298: return -ENOMEM;
299: }
300:
]
|
| 234/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 17723
output: 44
thoughts: 15
Thoughts: Okay, here's my interpretation of that request, formatted as requested:
**Navigating Lines 301-450**
Alright, let's zero in on lines 301 to 450. I know what's likely covered within that range, given the context of the overall document. It's likely a critical juncture, potentially containing a key methodological shift, results from a particular experiment, or a specific argument within the broader theory. Time to put on my analysis hat. I'll be looking for nuances; how the author phrases their points, the justifications they offer, and what assumptions are driving their reasoning. I'll be watching for any areas that could be challenged, or where additional support may be helpful.
|
| 235/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:301 LineCount:150]
Results: map[Contents: 301: spin_lock_irqsave(&dev->lock, flags);
302: dev->req = req;
303: dev->req->context = dev;
304: dev->req->complete = gadget_ep0_complete;
305: dev->gadget = gadget;
306: gadget_for_each_ep(ep, dev->gadget) {
307: dev->eps[i].ep = ep;
308: dev->eps[i].addr = get_ep_addr(ep->name);
309: dev->eps[i].state = STATE_EP_DISABLED;
310: i++;
311: }
312: dev->eps_num = i;
313: spin_unlock_irqrestore(&dev->lock, flags);
314:
315: dev_dbg(&gadget->dev, "gadget connected\n");
316: ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
317: if (ret < 0) {
318: dev_err(&gadget->dev, "failed to queue connect event\n");
319: set_gadget_data(gadget, NULL);
320: return ret;
321: }
322:
323: /* Matches kref_put() in gadget_unbind(). */
324: kref_get(&dev->count);
325: return ret;
326: }
327:
328: static void gadget_unbind(struct usb_gadget *gadget)
329: {
330: struct raw_dev *dev = get_gadget_data(gadget);
331:
332: set_gadget_data(gadget, NULL);
333: /* Matches kref_get() in gadget_bind(). */
334: kref_put(&dev->count, dev_free);
335: }
336:
337: static int gadget_setup(struct usb_gadget *gadget,
338: const struct usb_ctrlrequest *ctrl)
339: {
340: int ret = 0;
341: struct raw_dev *dev = get_gadget_data(gadget);
342: unsigned long flags;
343:
344: spin_lock_irqsave(&dev->lock, flags);
345: if (dev->state != STATE_DEV_RUNNING) {
346: dev_err(&gadget->dev, "ignoring, device is not running\n");
347: ret = -ENODEV;
348: goto out_unlock;
349: }
350: if (dev->ep0_in_pending || dev->ep0_out_pending) {
351: dev_dbg(&gadget->dev, "stalling, request already pending\n");
352: ret = -EBUSY;
353: goto out_unlock;
354: }
355: if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
356: dev->ep0_in_pending = true;
357: else
358: dev->ep0_out_pending = true;
359: spin_unlock_irqrestore(&dev->lock, flags);
360:
361: ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
362: if (ret < 0)
363: dev_err(&gadget->dev, "failed to queue control event\n");
364: goto out;
365:
366: out_unlock:
367: spin_unlock_irqrestore(&dev->lock, flags);
368: out:
369: if (ret == 0 && ctrl->wLength == 0) {
370: /*
371: * Return USB_GADGET_DELAYED_STATUS as a workaround to stop
372: * some UDC drivers (e.g. dwc3) from automatically proceeding
373: * with the status stage for 0-length transfers.
374: * Should be removed once all UDC drivers are fixed to always
375: * delay the status stage until a response is queued to EP0.
376: */
377: return USB_GADGET_DELAYED_STATUS;
378: }
379: return ret;
380: }
381:
382: static void gadget_disconnect(struct usb_gadget *gadget)
383: {
384: struct raw_dev *dev = get_gadget_data(gadget);
385: int ret;
386:
387: dev_dbg(&gadget->dev, "gadget disconnected\n");
388: ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL);
389: if (ret < 0)
390: dev_err(&gadget->dev, "failed to queue disconnect event\n");
391: }
392: static void gadget_suspend(struct usb_gadget *gadget)
393: {
394: struct raw_dev *dev = get_gadget_data(gadget);
395: int ret;
396:
397: dev_dbg(&gadget->dev, "gadget suspended\n");
398: ret = raw_queue_event(dev, USB_RAW_EVENT_SUSPEND, 0, NULL);
399: if (ret < 0)
400: dev_err(&gadget->dev, "failed to queue suspend event\n");
]
|
| 236/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 19567
output: 44
thoughts: 15
Thoughts: Okay, let's dive into this. Here's my thought process as I approach lines 401-550:
**Analyzing a Section: Lines 401-550**
Alright, I'm focusing on lines 401 to 550. My expert eye immediately starts scanning for the critical elements. I'm looking for the key arguments, the methodologies employed, any data presented, and especially, the conclusions drawn within this specific section. Given my deep understanding of the subject matter, I'm already anticipating the potential issues, the strengths of the author's claims, and areas that might require further scrutiny. I'll be evaluating the internal consistency, the external validity (in relation to the broader literature), and checking for any logical fallacies or biases. I need to understand how these lines fit into the larger argument. What's the narrative flow? What's being established here? What questions are being answered, and perhaps even more importantly, which questions are being raised? I'm prepared to critically analyze the author's choices, the evidence they provide, and the implications of their presentation. I'll be forming my own preliminary assessment, looking for connections to my existing knowledge and identifying areas where my expertise can provide valuable insight. Essentially, it's about dissecting the building blocks of the argument and assessing its construction.
|
| 237/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:401 LineCount:150]
Results: map[Contents: 401: }
402: static void gadget_resume(struct usb_gadget *gadget)
403: {
404: struct raw_dev *dev = get_gadget_data(gadget);
405: int ret;
406:
407: dev_dbg(&gadget->dev, "gadget resumed\n");
408: ret = raw_queue_event(dev, USB_RAW_EVENT_RESUME, 0, NULL);
409: if (ret < 0)
410: dev_err(&gadget->dev, "failed to queue resume event\n");
411: }
412: static void gadget_reset(struct usb_gadget *gadget)
413: {
414: struct raw_dev *dev = get_gadget_data(gadget);
415: int ret;
416:
417: dev_dbg(&gadget->dev, "gadget reset\n");
418: ret = raw_queue_event(dev, USB_RAW_EVENT_RESET, 0, NULL);
419: if (ret < 0)
420: dev_err(&gadget->dev, "failed to queue reset event\n");
421: }
422:
423: /*----------------------------------------------------------------------*/
424:
425: static struct miscdevice raw_misc_device;
426:
427: static int raw_open(struct inode *inode, struct file *fd)
428: {
429: struct raw_dev *dev;
430:
431: /* Nonblocking I/O is not supported yet. */
432: if (fd->f_flags & O_NONBLOCK)
433: return -EINVAL;
434:
435: dev = dev_new();
436: if (!dev)
437: return -ENOMEM;
438: fd->private_data = dev;
439: dev->state = STATE_DEV_OPENED;
440: dev->dev = raw_misc_device.this_device;
441: return 0;
442: }
443:
444: static int raw_release(struct inode *inode, struct file *fd)
445: {
446: int ret = 0;
447: struct raw_dev *dev = fd->private_data;
448: unsigned long flags;
449: bool unregister = false;
450:
451: spin_lock_irqsave(&dev->lock, flags);
452: dev->state = STATE_DEV_CLOSED;
453: if (!dev->gadget) {
454: spin_unlock_irqrestore(&dev->lock, flags);
455: goto out_put;
456: }
457: if (dev->gadget_registered)
458: unregister = true;
459: dev->gadget_registered = false;
460: spin_unlock_irqrestore(&dev->lock, flags);
461:
462: if (unregister) {
463: ret = usb_gadget_unregister_driver(&dev->driver);
464: if (ret != 0)
465: dev_err(dev->dev,
466: "usb_gadget_unregister_driver() failed with %d\n",
467: ret);
468: /* Matches kref_get() in raw_ioctl_run(). */
469: kref_put(&dev->count, dev_free);
470: }
471:
472: out_put:
473: /* Matches dev_new() in raw_open(). */
474: kref_put(&dev->count, dev_free);
475: return ret;
476: }
477:
478: /*----------------------------------------------------------------------*/
479:
480: static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
481: {
482: int ret = 0;
483: int driver_id_number;
484: struct usb_raw_init arg;
485: char *udc_driver_name;
486: char *udc_device_name;
487: char *driver_driver_name;
488: unsigned long flags;
489:
490: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
491: return -EFAULT;
492:
493: switch (arg.speed) {
494: case USB_SPEED_UNKNOWN:
495: arg.speed = USB_SPEED_HIGH;
496: break;
497: case USB_SPEED_LOW:
498: case USB_SPEED_FULL:
499: case USB_SPEED_HIGH:
500: case USB_SPEED_SUPER:
]
|
| 238/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 21361
output: 44
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing a Specific Text Segment**
Okay, so I'm diving back into this paper. Given the density of the arguments, I'm going to focus my analysis by homing in on lines 501 through 650. I figure this section likely represents a specific argument or sub-argument, and understanding its nuances in isolation could illuminate the broader framework. I'm anticipating this to be a focused discussion regarding the central thesis. I'll be looking for the assumptions, logical structure, potential weaknesses, and evidence presented. My aim is to efficiently extract the core claims and their supporting arguments within this defined boundary. This approach allows me to examine the argument with greater depth than a superficial review would permit.
|
| 239/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:501 LineCount:150]
Results: map[Contents: 501: break;
502: default:
503: return -EINVAL;
504: }
505:
506: driver_id_number = ida_alloc(&driver_id_numbers, GFP_KERNEL);
507: if (driver_id_number < 0)
508: return driver_id_number;
509:
510: driver_driver_name = kmalloc(DRIVER_DRIVER_NAME_LENGTH_MAX, GFP_KERNEL);
511: if (!driver_driver_name) {
512: ret = -ENOMEM;
513: goto out_free_driver_id_number;
514: }
515: snprintf(driver_driver_name, DRIVER_DRIVER_NAME_LENGTH_MAX,
516: DRIVER_NAME ".%d", driver_id_number);
517:
518: udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
519: if (!udc_driver_name) {
520: ret = -ENOMEM;
521: goto out_free_driver_driver_name;
522: }
523: ret = strscpy(udc_driver_name, &arg.driver_name[0],
524: UDC_NAME_LENGTH_MAX);
525: if (ret < 0)
526: goto out_free_udc_driver_name;
527: ret = 0;
528:
529: udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
530: if (!udc_device_name) {
531: ret = -ENOMEM;
532: goto out_free_udc_driver_name;
533: }
534: ret = strscpy(udc_device_name, &arg.device_name[0],
535: UDC_NAME_LENGTH_MAX);
536: if (ret < 0)
537: goto out_free_udc_device_name;
538: ret = 0;
539:
540: spin_lock_irqsave(&dev->lock, flags);
541: if (dev->state != STATE_DEV_OPENED) {
542: dev_dbg(dev->dev, "fail, device is not opened\n");
543: ret = -EINVAL;
544: goto out_unlock;
545: }
546: dev->udc_name = udc_driver_name;
547:
548: dev->driver.function = DRIVER_DESC;
549: dev->driver.max_speed = arg.speed;
550: dev->driver.setup = gadget_setup;
551: dev->driver.disconnect = gadget_disconnect;
552: dev->driver.bind = gadget_bind;
553: dev->driver.unbind = gadget_unbind;
554: dev->driver.suspend = gadget_suspend;
555: dev->driver.resume = gadget_resume;
556: dev->driver.reset = gadget_reset;
557: dev->driver.driver.name = driver_driver_name;
558: dev->driver.udc_name = udc_device_name;
559: dev->driver.match_existing_only = 1;
560: dev->driver_id_number = driver_id_number;
561:
562: dev->state = STATE_DEV_INITIALIZED;
563: spin_unlock_irqrestore(&dev->lock, flags);
564: return ret;
565:
566: out_unlock:
567: spin_unlock_irqrestore(&dev->lock, flags);
568: out_free_udc_device_name:
569: kfree(udc_device_name);
570: out_free_udc_driver_name:
571: kfree(udc_driver_name);
572: out_free_driver_driver_name:
573: kfree(driver_driver_name);
574: out_free_driver_id_number:
575: ida_free(&driver_id_numbers, driver_id_number);
576: return ret;
577: }
578:
579: static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
580: {
581: int ret = 0;
582: unsigned long flags;
583:
584: if (value)
585: return -EINVAL;
586:
587: spin_lock_irqsave(&dev->lock, flags);
588: if (dev->state != STATE_DEV_INITIALIZED) {
589: dev_dbg(dev->dev, "fail, device is not initialized\n");
590: ret = -EINVAL;
591: goto out_unlock;
592: }
593: dev->state = STATE_DEV_REGISTERING;
594: spin_unlock_irqrestore(&dev->lock, flags);
595:
596: ret = usb_gadget_register_driver(&dev->driver);
597:
598: spin_lock_irqsave(&dev->lock, flags);
599: if (ret) {
600: dev_err(dev->dev,
]
|
| 240/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 23171
output: 44
thoughts: 15
Thoughts: Okay, here's my interpretation of reading lines 601-750, summarized in the first person, assuming I'm an expert:
**Delving into the Subject Matter**
Alright, let's zero in on lines 601 through 750. This section is key, I'm expecting a critical shift, perhaps a refinement of the methodology, or maybe even a confrontation with opposing views. I'm focusing my attention for crucial evidence to support or refute previous assertions. Given my deep understanding of the context, I'm already anticipating the nuances and implicit meanings that might be present. I'm ready to dissect the arguments, identify any logical fallacies, and assess the strength of the evidence presented. I know the field well enough to see the connections to earlier work and the implications for future research, and I know I have to identify the relevant contributions to the study, and if the original claims have been supported or refuted. I'll be making mental notes on how this particular segment contributes to the bigger picture, and how it aligns (or diverges) from my own working hypotheses. I'm also keenly aware of the author's voice and how they are navigating these complex arguments. Time to engage and assess.
|
| 241/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:601 LineCount:150]
Results: map[Contents: 601: "fail, usb_gadget_register_driver returned %d\n", ret);
602: dev->state = STATE_DEV_FAILED;
603: goto out_unlock;
604: }
605: dev->gadget_registered = true;
606: dev->state = STATE_DEV_RUNNING;
607: /* Matches kref_put() in raw_release(). */
608: kref_get(&dev->count);
609:
610: out_unlock:
611: spin_unlock_irqrestore(&dev->lock, flags);
612: return ret;
613: }
614:
615: static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
616: {
617: struct usb_raw_event arg;
618: unsigned long flags;
619: struct usb_raw_event *event;
620: uint32_t length;
621:
622: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
623: return -EFAULT;
624:
625: spin_lock_irqsave(&dev->lock, flags);
626: if (dev->state != STATE_DEV_RUNNING) {
627: dev_dbg(dev->dev, "fail, device is not running\n");
628: spin_unlock_irqrestore(&dev->lock, flags);
629: return -EINVAL;
630: }
631: if (!dev->gadget) {
632: dev_dbg(dev->dev, "fail, gadget is not bound\n");
633: spin_unlock_irqrestore(&dev->lock, flags);
634: return -EBUSY;
635: }
636: spin_unlock_irqrestore(&dev->lock, flags);
637:
638: event = raw_event_queue_fetch(&dev->queue);
639: if (PTR_ERR(event) == -EINTR) {
640: dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
641: return -EINTR;
642: }
643: if (IS_ERR(event)) {
644: dev_err(&dev->gadget->dev, "failed to fetch event\n");
645: spin_lock_irqsave(&dev->lock, flags);
646: dev->state = STATE_DEV_FAILED;
647: spin_unlock_irqrestore(&dev->lock, flags);
648: return -ENODEV;
649: }
650: length = min(arg.length, event->length);
651: if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
652: kfree(event);
653: return -EFAULT;
654: }
655:
656: kfree(event);
657: return 0;
658: }
659:
660: static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
661: bool get_from_user)
662: {
663: void *data;
664:
665: if (copy_from_user(io, ptr, sizeof(*io)))
666: return ERR_PTR(-EFAULT);
667: if (io->ep >= USB_RAW_EPS_NUM_MAX)
668: return ERR_PTR(-EINVAL);
669: if (!usb_raw_io_flags_valid(io->flags))
670: return ERR_PTR(-EINVAL);
671: if (io->length > USB_RAW_IO_LENGTH_MAX)
672: return ERR_PTR(-EINVAL);
673: if (get_from_user)
674: data = memdup_user(ptr + sizeof(*io), io->length);
675: else {
676: data = kmalloc(io->length, GFP_KERNEL);
677: if (!data)
678: data = ERR_PTR(-ENOMEM);
679: }
680: return data;
681: }
682:
683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
]
|
| 242/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 25036
output: 44
thoughts: 15
Thoughts: Okay, here's my interpretation of that request, formatted as requested:
**Navigating the Text: A Targeted Read**
Alright, so the task at hand is to understand a specific portion of the text, specifically lines 701 to 850. My expert intuition tells me that this range likely represents a distinct argument, section, or perhaps even a critical example within the broader context. Therefore, my immediate focus will be on efficiently absorbing the information within that limited scope. I need to quickly identify the central theme or argument presented within those lines. I will be looking for key concepts, logical connections, and any evidence used to support claims. My goal is to extract the essential components and discern their significance. The objective is to grasp the core ideas concisely so that the text can be processed as efficiently as possible.
|
| 243/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:701 LineCount:150]
Results: map[Contents: 701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
757:
758: static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
759: {
760: int ret = 0;
761: void *data;
762: struct usb_raw_ep_io io;
763:
764: data = raw_alloc_io_data(&io, (void __user *)value, true);
765: if (IS_ERR(data))
766: return PTR_ERR(data);
767: ret = raw_process_ep0_io(dev, &io, data, true);
768: kfree(data);
769: return ret;
770: }
771:
772: static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
773: {
774: int ret = 0;
775: void *data;
776: struct usb_raw_ep_io io;
777: unsigned int length;
778:
779: data = raw_alloc_io_data(&io, (void __user *)value, false);
780: if (IS_ERR(data))
781: return PTR_ERR(data);
782: ret = raw_process_ep0_io(dev, &io, data, false);
783: if (ret < 0)
784: goto free;
785:
786: length = min_t(unsigned int, io.length, ret);
787: if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
788: ret = -EFAULT;
789: else
790: ret = length;
791: free:
792: kfree(data);
793: return ret;
794: }
795:
796: static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
797: {
798: int ret = 0;
799: unsigned long flags;
800:
]
|
| 244/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 26791
output: 44
thoughts: 15
Thoughts: Okay, here's my interpretation of reading lines 801 to 950, presented as though these were my own thoughts:
**Analyzing a Segment: Lines 801-950**
Alright, let's dive into this specific section. Lines 801 to 950... I need to see what's being developed here. Based on the context I know so far, I'm expecting *[Insert assumption of likely context based on your expertise here. Example: "more detailed explanation of the proposed methodology," or "a deeper dive into the experimental results"].* I need to focus on *[Insert specific aspect you'd look for. Example: "the causal relationships being established," or "the statistical significance of the claims made"].*
My first scan will be for the key arguments being presented. What are the core assertions, the foundational premises? Where does the author begin building their case? I need to look for the transitions, the linking phrases that connect one idea to the next. Are they using any novel approaches or relying on well-established ones? I'll be keeping an eye out for any potential flaws in the logic, any unsupported assumptions, or any areas that require more rigorous justification.
I'll be paying close attention to any experimental setup or data analysis. Are the methodologies sound? Are the controls appropriate? Are the statistical methods used justified and correctly applied? I'll cross-reference the presented results with my existing understanding of the field, looking for any inconsistencies or surprising findings. Any implications should be made against the backdrop of my expertise.
I'll also consider the author's writing style. Is it clear and concise? Is the argument presented in a logical, coherent fashion? Or is it rambling or obfuscating? The clarity of the writing can be as important as the content itself. Are the author's references up to par? This is an area in which I feel I have a good grasp.
Ultimately, I'm trying to determine the strength of the author's conclusions. Do the lines of reasoning hold up? Are the claims supported by sufficient evidence? What are the implications for the wider field? I expect this section to either validate or challenge my existing understanding, and I am ready for either outcome.
|
| 245/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:801 LineCount:150]
Results: map[Contents: 801: if (value)
802: return -EINVAL;
803: spin_lock_irqsave(&dev->lock, flags);
804: if (dev->state != STATE_DEV_RUNNING) {
805: dev_dbg(dev->dev, "fail, device is not running\n");
806: ret = -EINVAL;
807: goto out_unlock;
808: }
809: if (!dev->gadget) {
810: dev_dbg(dev->dev, "fail, gadget is not bound\n");
811: ret = -EBUSY;
812: goto out_unlock;
813: }
814: if (dev->ep0_urb_queued) {
815: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
816: ret = -EBUSY;
817: goto out_unlock;
818: }
819: if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
820: dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
821: ret = -EBUSY;
822: goto out_unlock;
823: }
824:
825: ret = usb_ep_set_halt(dev->gadget->ep0);
826: if (ret < 0)
827: dev_err(&dev->gadget->dev,
828: "fail, usb_ep_set_halt returned %d\n", ret);
829:
830: if (dev->ep0_in_pending)
831: dev->ep0_in_pending = false;
832: else
833: dev->ep0_out_pending = false;
834:
835: out_unlock:
836: spin_unlock_irqrestore(&dev->lock, flags);
837: return ret;
838: }
839:
840: static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
841: {
842: int ret = 0, i;
843: unsigned long flags;
844: struct usb_endpoint_descriptor *desc;
845: struct raw_ep *ep;
846: bool ep_props_matched = false;
847:
848: desc = memdup_user((void __user *)value, sizeof(*desc));
849: if (IS_ERR(desc))
850: return PTR_ERR(desc);
851:
852: /*
853: * Endpoints with a maxpacket length of 0 can cause crashes in UDC
854: * drivers.
855: */
856: if (usb_endpoint_maxp(desc) == 0) {
857: dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
858: kfree(desc);
859: return -EINVAL;
860: }
861:
862: spin_lock_irqsave(&dev->lock, flags);
863: if (dev->state != STATE_DEV_RUNNING) {
864: dev_dbg(dev->dev, "fail, device is not running\n");
865: ret = -EINVAL;
866: goto out_free;
867: }
868: if (!dev->gadget) {
869: dev_dbg(dev->dev, "fail, gadget is not bound\n");
870: ret = -EBUSY;
871: goto out_free;
872: }
873:
874: for (i = 0; i < dev->eps_num; i++) {
875: ep = &dev->eps[i];
876: if (ep->addr != usb_endpoint_num(desc) &&
877: ep->addr != USB_RAW_EP_ADDR_ANY)
878: continue;
879: if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
880: continue;
881: ep_props_matched = true;
882: if (ep->state != STATE_EP_DISABLED)
883: continue;
884: ep->ep->desc = desc;
885: ret = usb_ep_enable(ep->ep);
886: if (ret < 0) {
887: dev_err(&dev->gadget->dev,
888: "fail, usb_ep_enable returned %d\n", ret);
889: goto out_free;
890: }
891: ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
892: if (!ep->req) {
893: dev_err(&dev->gadget->dev,
894: "fail, usb_ep_alloc_request failed\n");
895: usb_ep_disable(ep->ep);
896: ret = -ENOMEM;
897: goto out_free;
898: }
899: ep->state = STATE_EP_ENABLED;
900: ep->ep->driver_data = ep;
]
|
| 246/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 28870
output: 44
thoughts: 16
Thoughts: Okay, let's dive into this specific section, lines 901 to 1050. I need to quickly establish the context within the larger work. What's the overarching argument being made here? Where does this section fit into the broader narrative? Is this a critical point of evidence, a transitional passage, or perhaps a section where a counter-argument is being addressed?
I'll be looking for key terms, rhetorical strategies, and any significant shifts in tone or style. I'll need to parse the author's logic, noting any assumptions they are making, and evaluating the strength of their claims. Are there any weaknesses in the argumentation that I can identify? Any points where further clarification or evidence is needed?
I'll also keep an eye out for any connections to the existing body of literature in this field β what are they building on, critiquing, or challenging? Are they referencing established figures or theories? How does this section contribute to the ongoing scholarly conversation? This will be interesting, I'm ready.
|
| 247/6 |
2026/07/29 08:32 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:901 LineCount:150]
Results: map[Contents: 901: ret = i;
902: goto out_unlock;
903: }
904:
905: if (!ep_props_matched) {
906: dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
907: ret = -EINVAL;
908: } else {
909: dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
910: ret = -EBUSY;
911: }
912:
913: out_free:
914: kfree(desc);
915: out_unlock:
916: spin_unlock_irqrestore(&dev->lock, flags);
917: return ret;
918: }
919:
920: static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
921: {
922: int ret = 0, i = value;
923: unsigned long flags;
924:
925: spin_lock_irqsave(&dev->lock, flags);
926: if (dev->state != STATE_DEV_RUNNING) {
927: dev_dbg(dev->dev, "fail, device is not running\n");
928: ret = -EINVAL;
929: goto out_unlock;
930: }
931: if (!dev->gadget) {
932: dev_dbg(dev->dev, "fail, gadget is not bound\n");
933: ret = -EBUSY;
934: goto out_unlock;
935: }
936: if (i < 0 || i >= dev->eps_num) {
937: dev_dbg(dev->dev, "fail, invalid endpoint\n");
938: ret = -EBUSY;
939: goto out_unlock;
940: }
941: if (dev->eps[i].state == STATE_EP_DISABLED) {
942: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
943: ret = -EINVAL;
944: goto out_unlock;
945: }
946: if (dev->eps[i].disabling) {
947: dev_dbg(&dev->gadget->dev,
948: "fail, disable already in progress\n");
949: ret = -EINVAL;
950: goto out_unlock;
951: }
952: if (dev->eps[i].urb_queued) {
953: dev_dbg(&dev->gadget->dev,
954: "fail, waiting for urb completion\n");
955: ret = -EINVAL;
956: goto out_unlock;
957: }
958: dev->eps[i].disabling = true;
959: spin_unlock_irqrestore(&dev->lock, flags);
960:
961: usb_ep_disable(dev->eps[i].ep);
962:
963: spin_lock_irqsave(&dev->lock, flags);
964: usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
965: kfree(dev->eps[i].ep->desc);
966: dev->eps[i].state = STATE_EP_DISABLED;
967: dev->eps[i].disabling = false;
968:
969: out_unlock:
970: spin_unlock_irqrestore(&dev->lock, flags);
971: return ret;
972: }
973:
974: static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
975: unsigned long value, bool set, bool halt)
976: {
977: int ret = 0, i = value;
978: unsigned long flags;
979:
980: spin_lock_irqsave(&dev->lock, flags);
981: if (dev->state != STATE_DEV_RUNNING) {
982: dev_dbg(dev->dev, "fail, device is not running\n");
983: ret = -EINVAL;
984: goto out_unlock;
985: }
986: if (!dev->gadget) {
987: dev_dbg(dev->dev, "fail, gadget is not bound\n");
988: ret = -EBUSY;
989: goto out_unlock;
990: }
991: if (i < 0 || i >= dev->eps_num) {
992: dev_dbg(dev->dev, "fail, invalid endpoint\n");
993: ret = -EBUSY;
994: goto out_unlock;
995: }
996: if (dev->eps[i].state == STATE_EP_DISABLED) {
997: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
998: ret = -EINVAL;
999: goto out_unlock;
1000: }
]
|
| 248/6 |
2026/07/29 08:32 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 30662
output: 45
thoughts: 17
Thoughts: Okay, here's the summary, rendered as a first-person thought process, aimed at an expert audience:
**Focused Review of Lines 1001-1150**
Alright, time to dive into this section. Let's get down to brass tacks. I'm focusing specifically on lines 1001 to 1150. Given the preceding context, I need to understand how the author is developing their argument here. I'm looking for the key claims they're making, the evidence they're using to support those claims, and, critically, how this chunk advances the overall thesis. Are they introducing a new perspective? Are they elaborating on a previously stated concept? Are they preemptively refuting potential counterarguments? It's essential to pinpoint the logical thread they're trying to weave in this section. My goal is to grasp the nuances and the overall contribution of these particular lines. I'm especially keen on any potential weaknesses or areas that need further scrutiny. I need to ensure a comprehensive grasp of this section.
|
| 249/6 |
2026/07/29 08:33 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1001 LineCount:150]
Results: map[Contents:1001: if (dev->eps[i].disabling) {
1002: dev_dbg(&dev->gadget->dev,
1003: "fail, disable is in progress\n");
1004: ret = -EINVAL;
1005: goto out_unlock;
1006: }
1007: if (dev->eps[i].urb_queued) {
1008: dev_dbg(&dev->gadget->dev,
1009: "fail, waiting for urb completion\n");
1010: ret = -EINVAL;
1011: goto out_unlock;
1012: }
1013: if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
1014: dev_dbg(&dev->gadget->dev,
1015: "fail, can't halt/wedge ISO endpoint\n");
1016: ret = -EINVAL;
1017: goto out_unlock;
1018: }
1019:
1020: if (set && halt) {
1021: ret = usb_ep_set_halt(dev->eps[i].ep);
1022: if (ret < 0)
1023: dev_err(&dev->gadget->dev,
1024: "fail, usb_ep_set_halt returned %d\n", ret);
1025: } else if (!set && halt) {
1026: ret = usb_ep_clear_halt(dev->eps[i].ep);
1027: if (ret < 0)
1028: dev_err(&dev->gadget->dev,
1029: "fail, usb_ep_clear_halt returned %d\n", ret);
1030: } else if (set && !halt) {
1031: ret = usb_ep_set_wedge(dev->eps[i].ep);
1032: if (ret < 0)
1033: dev_err(&dev->gadget->dev,
1034: "fail, usb_ep_set_wedge returned %d\n", ret);
1035: }
1036:
1037: out_unlock:
1038: spin_unlock_irqrestore(&dev->lock, flags);
1039: return ret;
1040: }
1041:
1042: static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
1043: {
1044: struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
1045: struct raw_dev *dev = r_ep->dev;
1046: unsigned long flags;
1047:
1048: spin_lock_irqsave(&dev->lock, flags);
1049: if (req->status)
1050: r_ep->status = req->status;
1051: else
1052: r_ep->status = req->actual;
1053: spin_unlock_irqrestore(&dev->lock, flags);
1054:
1055: complete((struct completion *)req->context);
1056: }
1057:
1058: static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
1059: void *data, bool in)
1060: {
1061: int ret = 0;
1062: unsigned long flags;
1063: struct raw_ep *ep;
1064: DECLARE_COMPLETION_ONSTACK(done);
1065:
1066: spin_lock_irqsave(&dev->lock, flags);
1067: if (dev->state != STATE_DEV_RUNNING) {
1068: dev_dbg(dev->dev, "fail, device is not running\n");
1069: ret = -EINVAL;
1070: goto out_unlock;
1071: }
1072: if (!dev->gadget) {
1073: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1074: ret = -EBUSY;
1075: goto out_unlock;
1076: }
1077: if (io->ep >= dev->eps_num) {
1078: dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
1079: ret = -EINVAL;
1080: goto out_unlock;
1081: }
1082: ep = &dev->eps[io->ep];
1083: if (ep->state != STATE_EP_ENABLED) {
1084: dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
1085: ret = -EBUSY;
1086: goto out_unlock;
1087: }
1088: if (ep->disabling) {
1089: dev_dbg(&dev->gadget->dev,
1090: "fail, endpoint is already being disabled\n");
1091: ret = -EBUSY;
1092: goto out_unlock;
1093: }
1094: if (ep->urb_queued) {
1095: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
1096: ret = -EBUSY;
1097: goto out_unlock;
1098: }
1099: if (in != usb_endpoint_dir_in(ep->ep->desc)) {
1100: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
]
|
| 250/6 |
2026/07/29 08:33 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 32534
output: 45
thoughts: 17
Thoughts: Okay, here's my attempt at summarizing the thought process, as requested, in the first person and targeted towards an expert:
**Delving into the Code: Lines 1101-1280**
Alright, let's get down to brass tacks. I need to focus my attention on lines 1101 through 1280. No broad strokes, I want to dive deep into this specific block. This range likely encapsulates a crucial function, a specific algorithm implementation, or a section dealing with a particular data structure. Knowing the context, I'll be looking for nuances and potential optimizations that might otherwise slip by in a cursory read. I'm going to pay close attention to variable declarations and assignments within the scope, the control flow constructs, and any calls to other functions or methods. The goal is to fully understand the logic here, not just at a surface level, but with an appreciation for the rationale behind the design choices. I anticipate finding something significant within this range.
|
| 251/6 |
2026/07/29 08:33 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1101 LineCount:180]
Results: map[Contents:1101: ret = -EINVAL;
1102: goto out_unlock;
1103: }
1104:
1105: ep->dev = dev;
1106: ep->req->context = &done;
1107: ep->req->complete = gadget_ep_complete;
1108: ep->req->buf = data;
1109: ep->req->length = io->length;
1110: ep->req->zero = usb_raw_io_flags_zero(io->flags);
1111: ep->urb_queued = true;
1112: spin_unlock_irqrestore(&dev->lock, flags);
1113:
1114: ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1115: if (ret) {
1116: dev_err(&dev->gadget->dev,
1117: "fail, usb_ep_queue returned %d\n", ret);
1118: spin_lock_irqsave(&dev->lock, flags);
1119: goto out_queue_failed;
1120: }
1121:
1122: ret = wait_for_completion_interruptible(&done);
1123: if (ret) {
1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1125: usb_ep_dequeue(ep->ep, ep->req);
1126: wait_for_completion(&done);
1127: spin_lock_irqsave(&dev->lock, flags);
1128: if (ep->status == -ECONNRESET)
1129: ep->status = -EINTR;
1130: goto out_interrupted;
1131: }
1132:
1133: spin_lock_irqsave(&dev->lock, flags);
1134:
1135: out_interrupted:
1136: ret = ep->status;
1137: out_queue_failed:
1138: ep->urb_queued = false;
1139: out_unlock:
1140: spin_unlock_irqrestore(&dev->lock, flags);
1141: return ret;
1142: }
1143:
1144: static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
1145: {
1146: int ret = 0;
1147: char *data;
1148: struct usb_raw_ep_io io;
1149:
1150: data = raw_alloc_io_data(&io, (void __user *)value, true);
1151: if (IS_ERR(data))
1152: return PTR_ERR(data);
1153: ret = raw_process_ep_io(dev, &io, data, true);
1154: kfree(data);
1155: return ret;
1156: }
1157:
1158: static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
1159: {
1160: int ret = 0;
1161: char *data;
1162: struct usb_raw_ep_io io;
1163: unsigned int length;
1164:
1165: data = raw_alloc_io_data(&io, (void __user *)value, false);
1166: if (IS_ERR(data))
1167: return PTR_ERR(data);
1168: ret = raw_process_ep_io(dev, &io, data, false);
1169: if (ret < 0)
1170: goto free;
1171:
1172: length = min_t(unsigned int, io.length, ret);
1173: if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
1174: ret = -EFAULT;
1175: else
1176: ret = length;
1177: free:
1178: kfree(data);
1179: return ret;
1180: }
1181:
1182: static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
1183: {
1184: int ret = 0;
1185: unsigned long flags;
1186:
1187: if (value)
1188: return -EINVAL;
1189: spin_lock_irqsave(&dev->lock, flags);
1190: if (dev->state != STATE_DEV_RUNNING) {
1191: dev_dbg(dev->dev, "fail, device is not running\n");
1192: ret = -EINVAL;
1193: goto out_unlock;
1194: }
1195: if (!dev->gadget) {
1196: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1197: ret = -EBUSY;
1198: goto out_unlock;
1199: }
1200: usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
]
|
| 252/6 |
2026/07/29 08:33 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 34283
output: 45
thoughts: 10
Thoughts: Okay, I'm ready to dive into this. Here's a summary from my perspective:
**Assessing the Landscape**
Alright, I've got the initial prompt, and now it's time to understand the full context. My immediate thought is to delve deeper. I'll need to absorb the entirety of the information presented. I need to read the rest of the file. This complete picture will be crucial for any insightful analysis or response.
|
| 253/6 |
2026/07/29 08:33 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1201 LineCount:100]
Results: map[Contents:1201:
1202: out_unlock:
1203: spin_unlock_irqrestore(&dev->lock, flags);
1204: return ret;
1205: }
1206:
1207: static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
1208: {
1209: int ret = 0;
1210: unsigned long flags;
1211:
1212: spin_lock_irqsave(&dev->lock, flags);
1213: if (dev->state != STATE_DEV_RUNNING) {
1214: dev_dbg(dev->dev, "fail, device is not running\n");
1215: ret = -EINVAL;
1216: goto out_unlock;
1217: }
1218: if (!dev->gadget) {
1219: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1220: ret = -EBUSY;
1221: goto out_unlock;
1222: }
1223: usb_gadget_vbus_draw(dev->gadget, 2 * value);
1224:
1225: out_unlock:
1226: spin_unlock_irqrestore(&dev->lock, flags);
1227: return ret;
1228: }
1229:
1230: static void fill_ep_caps(struct usb_ep_caps *caps,
1231: struct usb_raw_ep_caps *raw_caps)
1232: {
1233: raw_caps->type_control = caps->type_control;
1234: raw_caps->type_iso = caps->type_iso;
1235: raw_caps->type_bulk = caps->type_bulk;
1236: raw_caps->type_int = caps->type_int;
1237: raw_caps->dir_in = caps->dir_in;
1238: raw_caps->dir_out = caps->dir_out;
1239: }
1240:
1241: static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits)
1242: {
1243: limits->maxpacket_limit = ep->maxpacket_limit;
1244: limits->max_streams = ep->max_streams;
1245: }
1246:
1247: static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
1248: {
1249: int ret = 0, i;
1250: unsigned long flags;
1251: struct usb_raw_eps_info *info;
1252: struct raw_ep *ep;
1253:
1254: info = kzalloc_obj(*info);
1255: if (!info) {
1256: ret = -ENOMEM;
1257: goto out;
1258: }
1259:
1260: spin_lock_irqsave(&dev->lock, flags);
1261: if (dev->state != STATE_DEV_RUNNING) {
1262: dev_dbg(dev->dev, "fail, device is not running\n");
1263: ret = -EINVAL;
1264: spin_unlock_irqrestore(&dev->lock, flags);
1265: goto out_free;
1266: }
1267: if (!dev->gadget) {
1268: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1269: ret = -EBUSY;
1270: spin_unlock_irqrestore(&dev->lock, flags);
1271: goto out_free;
1272: }
1273:
1274: for (i = 0; i < dev->eps_num; i++) {
1275: ep = &dev->eps[i];
1276: strscpy(&info->eps[i].name[0], ep->ep->name,
1277: USB_RAW_EP_NAME_MAX);
1278: info->eps[i].addr = ep->addr;
1279: fill_ep_caps(&ep->ep->caps, &info->eps[i].caps);
1280: fill_ep_limits(ep->ep, &info->eps[i].limits);
1281: }
1282: ret = dev->eps_num;
1283: spin_unlock_irqrestore(&dev->lock, flags);
1284:
1285: if (copy_to_user((void __user *)value, info, sizeof(*info)))
1286: ret = -EFAULT;
1287:
1288: out_free:
1289: kfree(info);
1290: out:
1291: return ret;
1292: }
1293:
1294: static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
1295: {
1296: struct raw_dev *dev = fd->private_data;
1297: int ret = 0;
1298:
1299: if (!dev)
1300: return -EBUSY;
]
|
| 254/6 |
2026/07/29 08:33 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 35934
output: 45
thoughts: 11
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Prioritizing Efficiency: Reverse Engineering the Problem**
Right, instead of meticulously parsing the entire file from the beginning, my initial instinct is to jump to the very end. Given my experience, the concluding lines often encapsulate the final state, error messages, or the key results of a process. This allows me to immediately gauge the overall outcome and quickly determine if there's a critical issue that demands immediate attention. It's a pragmatic, bottom-up approach to debugging or understanding a complex dataset. I'm leveraging my pattern recognition to minimize unnecessary cognitive load and focus on what's likely the most relevant information first. It's about optimizing my time and cognitive resources by prioritizing efficiency.
|
| 255/6 |
2026/07/29 08:33 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1301 LineCount:100]
Results: map[Contents:1301:
1302: switch (cmd) {
1303: case USB_RAW_IOCTL_INIT:
1304: ret = raw_ioctl_init(dev, value);
1305: break;
1306: case USB_RAW_IOCTL_RUN:
1307: ret = raw_ioctl_run(dev, value);
1308: break;
1309: case USB_RAW_IOCTL_EVENT_FETCH:
1310: ret = raw_ioctl_event_fetch(dev, value);
1311: break;
1312: case USB_RAW_IOCTL_EP0_WRITE:
1313: ret = raw_ioctl_ep0_write(dev, value);
1314: break;
1315: case USB_RAW_IOCTL_EP0_READ:
1316: ret = raw_ioctl_ep0_read(dev, value);
1317: break;
1318: case USB_RAW_IOCTL_EP_ENABLE:
1319: ret = raw_ioctl_ep_enable(dev, value);
1320: break;
1321: case USB_RAW_IOCTL_EP_DISABLE:
1322: ret = raw_ioctl_ep_disable(dev, value);
1323: break;
1324: case USB_RAW_IOCTL_EP_WRITE:
1325: ret = raw_ioctl_ep_write(dev, value);
1326: break;
1327: case USB_RAW_IOCTL_EP_READ:
1328: ret = raw_ioctl_ep_read(dev, value);
1329: break;
1330: case USB_RAW_IOCTL_CONFIGURE:
1331: ret = raw_ioctl_configure(dev, value);
1332: break;
1333: case USB_RAW_IOCTL_VBUS_DRAW:
1334: ret = raw_ioctl_vbus_draw(dev, value);
1335: break;
1336: case USB_RAW_IOCTL_EPS_INFO:
1337: ret = raw_ioctl_eps_info(dev, value);
1338: break;
1339: case USB_RAW_IOCTL_EP0_STALL:
1340: ret = raw_ioctl_ep0_stall(dev, value);
1341: break;
1342: case USB_RAW_IOCTL_EP_SET_HALT:
1343: ret = raw_ioctl_ep_set_clear_halt_wedge(
1344: dev, value, true, true);
1345: break;
1346: case USB_RAW_IOCTL_EP_CLEAR_HALT:
1347: ret = raw_ioctl_ep_set_clear_halt_wedge(
1348: dev, value, false, true);
1349: break;
1350: case USB_RAW_IOCTL_EP_SET_WEDGE:
1351: ret = raw_ioctl_ep_set_clear_halt_wedge(
1352: dev, value, true, false);
1353: break;
1354: default:
1355: ret = -EINVAL;
1356: }
1357:
1358: return ret;
1359: }
1360:
1361: /*----------------------------------------------------------------------*/
1362:
1363: static const struct file_operations raw_fops = {
1364: .open = raw_open,
1365: .unlocked_ioctl = raw_ioctl,
1366: .compat_ioctl = raw_ioctl,
1367: .release = raw_release,
1368: };
1369:
1370: static struct miscdevice raw_misc_device = {
1371: .minor = MISC_DYNAMIC_MINOR,
1372: .name = DRIVER_NAME,
1373: .fops = &raw_fops,
1374: };
1375:
1376: module_misc_device(raw_misc_device);
]
|
| 256/6 |
2026/07/29 08:33 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 37307
output: 41
thoughts: 4888
Thoughts: **Analysis of `dev->gadget` Dereferencing in `raw_gadget.c`**
Okay, I've meticulously examined every instance of `dev->gadget` dereferencing within `drivers/usb/gadget/legacy/raw_gadget.c` to ascertain whether `dev->lock` is appropriately held. Here's a rundown of my findings, focusing on potential race conditions:
1. **`dev_free`**: The initial check showed that `dev->gadget->ep0` is indeed dereferenced in `dev_free`. I correctly recognized that, because this is called during the destruction of the `dev` object after its reference count drops to zero, no lock is needed as the object is no longer concurrently accessed.
2. **`gadget_bind`**: In `gadget_bind`, `dev->lock` *is* correctly held throughout the scope where `dev->gadget` is used, so no issues here.
3. **`raw_release`**: The check of `dev->gadget` in `raw_release` revealed that it's merely checked for nullity, not dereferenced, while `dev->lock` is held. This is also safe.
4. **`raw_ioctl_event_fetch`**: This is where things became interesting. While the checks for `dev->state` and `dev->gadget` are protected by `dev->lock`, the code subsequently *releases* the lock before dereferencing `dev->gadget` in `dev_dbg` and `dev_err`. This is a clear potential race condition. This needs further scrutiny, and it's likely a bug.
5. **`raw_process_ep0_io`**: My suspicion was confirmed in `raw_process_ep0_io`. The same pattern as `raw_ioctl_event_fetch` is present: `dev->lock` is released before `dev->gadget` is dereferenced in `usb_ep_queue`, `dev_err`, `dev_dbg`, and `usb_ep_dequeue` *within the error paths*. This constitutes a critical race condition. A concurrent thread might be freeing `dev->gadget` while the code is attempting to use it.
6. **`raw_process_ep_io`**: The vulnerability is also present in `raw_process_ep_io`. Specifically, `dev->gadget` is dereferenced *after* `dev->lock` is released. This represents a serious race. The exact same pattern as `raw_process_ep0_io`.
7. **`raw_ioctl_ep_disable`**: Here, `dev->lock` *is* held throughout the dereferencing of `dev->gadget` in the initial error checks. Therefore, there's no race condition in this function.
8. **`raw_ioctl_configure`**: `dev->lock` is correctly held when `dev->gadget` is passed to `usb_gadget_set_state`. No issues.
9. **`raw_ioctl_vbus_draw`**: Similarly, `dev->lock` is held during the call to `usb_gadget_vbus_draw`. No race.
In summary, the functions `raw_ioctl_event_fetch`, `raw_process_ep0_io`, and `raw_process_ep_io` are the locations where `dev->gadget` is dereferenced without the protection of `dev->lock`, representing significant bugs. I need to now perform a `git log` search to see if there are any related commits or discussions. The issue is that the error paths in those functions unlock the lock and then dereference the gadget, potentially after it has been freed by another thread that is also manipulating the device. A fix likely involves re-acquiring the lock before accessing `dev->gadget` in those error paths or, ideally, structuring the error handling so that `dev->gadget` isn't accessed after the lock is released.
|
| 257/6 |
2026/07/29 08:33 |
tool |
git-log |
0m
Args: map[CodeRegexp:dev->lock PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:f2d8c2606825 usb: gadget: Fix non-unique driver names in raw-gadget driver
c61769bd4777 usb: raw-gadget: support stalling/halting/wedging endpoints
97df5e5758f7 usb: raw-gadget: fix gadget endpoint selection
fdd10499de0d usb: raw-gadget: fix raw_event_queue_fetch locking
f2c2e717642c usb: gadget: add raw-gadget interface
]
|
| 258/6 |
2026/07/29 08:33 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 43252
output: 26
thoughts: 33
Thoughts: Here's a first-person summary of the provided text, assuming an expert user:
**Analyzing That USB Commit**
Alright, let's dive into commit `97df5e5758f7 usb: raw-gadget: fix gadget endpoint selection`. Given my experience with USB gadget drivers, the phrase "fix gadget endpoint selection" immediately flags a potential issue with endpoint management. My intuition suggests that the problem likely involves incorrect assignment of USB endpoints, perhaps due to a race condition, an off-by-one error, or improper handling of endpoint descriptors. I'll need to scrutinize the code changes within that commit to confirm my initial hypothesis. Specifically, I'll be looking for clues in the endpoint allocation routines, the device descriptor parsing, and any logic surrounding endpoint configuration during device enumeration or data transfer. I suspect this commit likely targets the `raw-gadget` driver specifically, suggesting that the issue might be related to its specific, more direct control over the USB hardware. My gut feeling is that it's a subtlety within how the raw driver interfaces with the hardware's endpoint registers, which could have been overlooked initially. Time to dig in and see the details of this change.
|
| 259/6 |
2026/07/29 08:33 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 97df5e5758f7d1dd0ca97e3210696818fc45bdb3
Author: Andrey Konovalov <andreyknvl@gmail.com>
Date: Thu May 7 19:06:56 2020 +0200
usb: raw-gadget: fix gadget endpoint selection
Currently automatic gadget endpoint selection based on required features
doesn't work. Raw Gadget tries iterating over the list of available
endpoints and finding one that has the right direction and transfer type.
Unfortunately selecting arbitrary gadget endpoints (even if they satisfy
feature requirements) doesn't work, as (depending on the UDC driver) they
might have fixed addresses, and one also needs to provide matching
endpoint addresses in the descriptors sent to the host.
The composite framework deals with this by assigning endpoint addresses
in usb_ep_autoconfig() before enumeration starts. This approach won't work
with Raw Gadget as the endpoints are supposed to be enabled after a
set_configuration/set_interface request from the host, so it's too late to
patch the endpoint descriptors that had already been sent to the host.
For Raw Gadget we take another approach. Similarly to GadgetFS, we allow
the user to make the decision as to which gadget endpoints to use.
This patch adds another Raw Gadget ioctl USB_RAW_IOCTL_EPS_INFO that
exposes information about all non-control endpoints that a currently
connected UDC has. This information includes endpoints addresses, as well
as their capabilities and limits to allow the user to choose the most
fitting gadget endpoint.
The USB_RAW_IOCTL_EP_ENABLE ioctl is updated to use the proper endpoint
validation routine usb_gadget_ep_match_desc().
These changes affect the portability of the gadgets that use Raw Gadget
when running on different UDCs. Nevertheless, as long as the user relies
on the information provided by USB_RAW_IOCTL_EPS_INFO to dynamically
choose endpoint addresses, UDC-agnostic gadgets can still be written with
Raw Gadget.
Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
diff --git a/Documentation/usb/raw-gadget.rst b/Documentation/usb/raw-gadget.rst
index 9e78cb858f86..4af8b1f15574 100644
--- a/Documentation/usb/raw-gadget.rst
+++ b/Documentation/usb/raw-gadget.rst
@@ -27,9 +27,8 @@ differences are:
3. Raw Gadget provides a way to select a UDC device/driver to bind to,
while GadgetFS currently binds to the first available UDC.
-4. Raw Gadget uses predictable endpoint names (handles) across different
- UDCs (as long as UDCs have enough endpoints of each required transfer
- type).
+4. Raw Gadget explicitly exposes information about endpoints addresses and
+ capabilities allowing a user to write UDC-agnostic gadgets.
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 7b241992ad5a..775f22184aaf 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -7,6 +7,7 @@
*/
#include <linux/compiler.h>
+#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/kref.h>
@@ -123,8 +124,6 @@ static void raw_event_queue_destroy(struct raw_event_queue *queue)
struct raw_dev;
-#define USB_RAW_MAX_ENDPOINTS 32
-
enum ep_state {
STATE_EP_DISABLED,
STATE_EP_ENABLED,
@@ -134,6 +133,7 @@ struct raw_ep {
struct raw_dev *dev;
enum ep_state state;
struct usb_ep *ep;
+ u8 addr;
struct usb_request *req;
bool urb_queued;
bool disabling;
@@ -168,7 +168,8 @@ struct raw_dev {
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
- struct raw_ep eps[USB_RAW_MAX_ENDPOINTS];
+ struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
+ int eps_num;
struct completion ep0_done;
struct raw_event_queue queue;
@@ -202,7 +203,7 @@ static void dev_free(struct kref *kref)
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
+ for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state != STATE_EP_ENABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
@@ -249,12 +250,26 @@ static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
complete(&dev->ep0_done);
}
+static u8 get_ep_addr(const char *name)
+{
+ /* If the endpoint has fixed function (named as e.g. "ep12out-bulk"),
+ * parse the endpoint address from its name. We deliberately use
+ * deprecated simple_strtoul() function here, as the number isn't
+ * followed by '\0' nor '\n'.
+ */
+ if (isdigit(name[2]))
+ return simple_strtoul(&name[2], NULL, 10);
+ /* Otherwise the endpoint is configurable (named as e.g. "ep-a"). */
+ return USB_RAW_EP_ADDR_ANY;
+}
+
static int gadget_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
- int ret = 0;
+ int ret = 0, i = 0;
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
struct usb_request *req;
+ struct usb_ep *ep;
unsigned long flags;
if (strcmp(gadget->name, dev->udc_name) != 0)
@@ -273,6 +288,13 @@ static int gadget_bind(struct usb_gadget *gadget,
dev->req->context = dev;
dev->req->complete = gadget_ep0_complete;
dev->gadget = gadget;
+ gadget_for_each_ep(ep, dev->gadget) {
+ dev->eps[i].ep = ep;
+ dev->eps[i].addr = get_ep_addr(ep->name);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ i++;
+ }
+ dev->eps_num = i;
spin_unlock_irqrestore(&dev->lock, flags);
/* Matches kref_put() in gadget_unbind(). */
@@ -555,7 +577,7 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
if (copy_from_user(io, ptr, sizeof(*io)))
return ERR_PTR(-EFAULT);
- if (io->ep >= USB_RAW_MAX_ENDPOINTS)
+ if (io->ep >= USB_RAW_EPS_NUM_MAX)
return ERR_PTR(-EINVAL);
if (!usb_raw_io_flags_valid(io->flags))
return ERR_PTR(-EINVAL);
@@ -682,40 +704,12 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
return ret;
}
-static bool check_ep_caps(struct usb_ep *ep,
- struct usb_endpoint_descriptor *desc)
-{
- switch (usb_endpoint_type(desc)) {
- case USB_ENDPOINT_XFER_ISOC:
- if (!ep->caps.type_iso)
- return false;
- break;
- case USB_ENDPOINT_XFER_BULK:
- if (!ep->caps.type_bulk)
- return false;
- break;
- case USB_ENDPOINT_XFER_INT:
- if (!ep->caps.type_int)
- return false;
- break;
- default:
- return false;
- }
-
- if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
- return false;
- if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
- return false;
-
- return true;
-}
-
static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i;
unsigned long flags;
struct usb_endpoint_descriptor *desc;
- struct usb_ep *ep = NULL;
+ struct raw_ep *ep;
desc = memdup_user((void __user *)value, sizeof(*desc));
if (IS_ERR(desc))
@@ -743,41 +737,32 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
goto out_free;
}
- for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
- if (dev->eps[i].state == STATE_EP_ENABLED)
+ for (i = 0; i < dev->eps_num; i++) {
+ ep = &dev->eps[i];
+ if (ep->state != STATE_EP_DISABLED)
continue;
- break;
- }
- if (i == USB_RAW_MAX_ENDPOINTS) {
- dev_dbg(&dev->gadget->dev,
- "fail, no device endpoints available\n");
- ret = -EBUSY;
- goto out_free;
- }
-
- gadget_for_each_ep(ep, dev->gadget) {
- if (ep->enabled)
+ if (ep->addr != usb_endpoint_num(desc) &&
+ ep->addr != USB_RAW_EP_ADDR_ANY)
continue;
- if (!check_ep_caps(ep, desc))
+ if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
continue;
- ep->desc = desc;
- ret = usb_ep_enable(ep);
+ ep->ep->desc = desc;
+ ret = usb_ep_enable(ep->ep);
if (ret < 0) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_enable returned %d\n", ret);
goto out_free;
}
- dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (!dev->eps[i].req) {
+ ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
+ if (!ep->req) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_alloc_request failed\n");
- usb_ep_disable(ep);
+ usb_ep_disable(ep->ep);
ret = -ENOMEM;
goto out_free;
}
- dev->eps[i].ep = ep;
- dev->eps[i].state = STATE_EP_ENABLED;
- ep->driver_data = &dev->eps[i];
+ ep->state = STATE_EP_ENABLED;
+ ep->ep->driver_data = ep;
ret = i;
goto out_unlock;
}
@@ -796,10 +781,6 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
- const void *desc;
-
- if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS)
- return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
@@ -812,6 +793,11 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
ret = -EBUSY;
goto out_unlock;
}
+ if (i < 0 || i >= dev->eps_num) {
+ dev_dbg(dev->dev, "fail, invalid endpoint\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
if (dev->eps[i].state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
@@ -836,10 +822,9 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
spin_lock_irqsave(&dev->lock, flags);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- desc = dev->eps[i].ep->desc;
+ kfree(dev->eps[i].ep->desc);
dev->eps[i].ep = NULL;
dev->eps[i].state = STATE_EP_DISABLED;
- kfree(desc);
dev->eps[i].disabling = false;
out_unlock:
@@ -868,7 +853,7 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
{
int ret = 0;
unsigned long flags;
- struct raw_ep *ep = &dev->eps[io->ep];
+ struct raw_ep *ep;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
@@ -882,6 +867,12 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = -EBUSY;
goto out_unlock;
}
+ if (io->ep >= dev->eps_num) {
+ dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ ep = &dev->eps[io->ep];
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
@@ -1027,6 +1018,71 @@ static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
return ret;
}
+static void fill_ep_caps(struct usb_ep_caps *caps,
+ struct usb_raw_ep_caps *raw_caps)
+{
+ raw_caps->type_control = caps->type_control;
+ raw_caps->type_iso = caps->type_iso;
+ raw_caps->type_bulk = caps->type_bulk;
+ raw_caps->type_int = caps->type_int;
+ raw_caps->dir_in = caps->dir_in;
+ raw_caps->dir_out = caps->dir_out;
+}
+
+static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits)
+{
+ limits->maxpacket_limit = ep->maxpacket_limit;
+ limits->max_streams = ep->max_streams;
+}
+
+static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0, i;
+ unsigned long flags;
+ struct usb_raw_eps_info *info;
+ struct raw_ep *ep;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto out_free;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto out_free;
+ }
+
+ memset(info, 0, sizeof(*info));
+ for (i = 0; i < dev->eps_num; i++) {
+ ep = &dev->eps[i];
+ strscpy(&info->eps[i].name[0], ep->ep->name,
+ USB_RAW_EP_NAME_MAX);
+ info->eps[i].addr = ep->addr;
+ fill_ep_caps(&ep->ep->caps, &info->eps[i].caps);
+ fill_ep_limits(ep->ep, &info->eps[i].limits);
+ }
+ ret = dev->eps_num;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (copy_to_user((void __user *)value, info, sizeof(*info)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(info);
+out:
+ return ret;
+}
+
static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
{
struct raw_dev *dev = fd->private_data;
@@ -1069,6 +1125,9 @@ static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
case USB_RAW_IOCTL_VBUS_DRAW:
ret = raw_ioctl_vbus_draw(dev, value);
break;
+ case USB_RAW_IOCTL_EPS_INFO:
+ ret = raw_ioctl_eps_info(dev, value);
+ break;
default:
ret = -EINVAL;
}
diff --git a/include/uapi/linux/usb/raw_gadget.h b/include/uapi/linux/usb/raw_gadget.h
index 02885e021ee5..c89f6341229c 100644
--- a/include/uapi/linux/usb/raw_gadget.h
+++ b/include/uapi/linux/usb/raw_gadget.h
@@ -93,6 +93,64 @@ struct usb_raw_ep_io {
__u8 data[0];
};
+/* Maximum number of non-control endpoints in struct usb_raw_eps_info. */
+#define USB_RAW_EPS_NUM_MAX 30
+
+/* Maximum length of UDC endpoint name in struct usb_raw_ep_info. */
+#define USB_RAW_EP_NAME_MAX 16
+
+/* Used as addr in struct usb_raw_ep_info if endpoint accepts any address. */
+#define USB_RAW_EP_ADDR_ANY 0xff
+
+/*
+ * struct usb_raw_ep_caps - exposes endpoint capabilities from struct usb_ep
+ * (technically from its member struct usb_ep_caps).
+ */
+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 - exposes endpoint limits from struct usb_ep.
+ * @maxpacket_limit: Maximum packet size value supported by this endpoint.
+ * @max_streams: maximum number of streams supported by this endpoint
+ * (actual number is 2^n).
+ * @reserved: Empty, reserved for potential future extensions.
+ */
+struct usb_raw_ep_limits {
+ __u16 maxpacket_limit;
+ __u16 max_streams;
+ __u32 reserved;
+};
+
+/*
+ * struct usb_raw_ep_info - stores information about a gadget endpoint.
+ * @name: Name of the endpoint as it is defined in the UDC driver.
+ * @addr: Address of the endpoint that must be specified in the endpoint
+ * descriptor passed to USB_RAW_IOCTL_EP_ENABLE ioctl.
+ * @caps: Endpoint capabilities.
+ * @limits: Endpoint limits.
+ */
+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 - argument for USB_RAW_IOCTL_EPS_INFO ioctl.
+ * eps: Structures that store information about non-control endpoints.
+ */
+struct usb_raw_eps_info {
+ struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
+};
+
/*
* Initializes a Raw Gadget instance.
* Accepts a pointer to the usb_raw_init struct as an argument.
@@ -126,9 +184,9 @@ struct usb_raw_ep_io {
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
/*
- * Finds an endpoint that supports the transfer type specified in the
- * descriptor and enables it.
- * Accepts a pointer to the usb_endpoint_descriptor struct as an argument.
+ * Finds an endpoint that satisfies the parameters specified in the provided
+ * descriptors (address, transfer type, etc.) and enables it.
+ * Accepts a pointer to the usb_raw_ep_descs struct as an argument.
* Returns enabled endpoint handle on success or negative error code on failure.
*/
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
@@ -165,4 +223,12 @@ struct usb_raw_ep_io {
*/
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
+/*
+ * Fills in the usb_raw_eps_info structure with information about non-control
+ * endpoints available for the currently connected UDC.
+ * Returns the number of available endpoints on success or negative error code
+ * on failure.
+ */
+#define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
+
#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */
]
|
| 260/6 |
2026/07/29 08:33 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 49784
output: 43
thoughts: 9022
Thoughts: **Summary of Findings Regarding `dev->gadget` Dereferencing in `raw_gadget.c`**
Alright, let's break this down systematically. The question is whether `dev->gadget` is dereferenced without `dev->lock` *anywhere else* within `drivers/usb/gadget/legacy/raw_gadget.c` besides a known place. After careful analysis, I've identified the following locations where `dev->gadget` is dereferenced, and where, critically, `dev->lock` is *not* held during those dereferences. Here's the rundown:
1. **`dev_free` (lines 217-218)**: `usb_ep_dequeue(dev->gadget->ep0, dev->req)` and `usb_ep_free_request(dev->gadget->ep0, dev->req)`. Although `dev->lock` isn't held here, this is within `dev_free`, which is called when the device reference count drops to zero. Therefore, no concurrent access is possible in this code path. This is a very important detail that we need to consider.
2. **`raw_ioctl_event_fetch` (lines 640, 644)**: `dev_dbg(&dev->gadget->dev, ...)` and `dev_err(&dev->gadget->dev, ...)`. `dev->lock` is released at line 636 prior to the `dev->gadget` dereference. `dev->lock` is only re-acquired in the `IS_ERR(event)` block at line 645, after the dereference on line 644.
3. **`raw_process_ep0_io` (lines 728, 730, 738, 739)**: `usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL)` and `dev_err(&dev->gadget->dev, ...)` and `usb_ep_dequeue(dev->gadget->ep0, dev->req)`. `dev->lock` is released at line 726 before the first dereference. `dev->lock` is only re-acquired *inside* the `if (ret)` blocks, at lines 732, 741, or 747, not before the dereferences. Note that the line 728 has the following `spin_unlock_irqrestore(&dev->lock, flags);` before the `dev->gadget` is dereferenced.
4. **`raw_process_ep_io` (lines 1116, 1124)**: `dev_err(&dev->gadget->dev, ...)` and `dev_dbg(&dev->gadget->dev, ...)` . `dev->lock` is released at line 1112 prior to these calls. `dev->lock` is re-acquired at line 1118 or 1127 or 1133 *inside* the `if (ret)` blocks, not before the dereferences. Similar `spin_unlock_irqrestore(&dev->lock, flags);`
So, to summarize, the only places where `dev->gadget` is accessed *without* `dev->lock` held, *outside* of `dev_free`, are at lines 640, 644, 728, 730, 738, 739, 1116, and 1124. This is a fairly critical area to review, given that we have identified multiple such places. We have identified how to look at each case and the exact lines to audit, with the crucial points about lock releases.
|
| 261/6 |
2026/07/29 08:34 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:630 LineCount:20]
Results: map[Contents: 630: }
631: if (!dev->gadget) {
632: dev_dbg(dev->dev, "fail, gadget is not bound\n");
633: spin_unlock_irqrestore(&dev->lock, flags);
634: return -EBUSY;
635: }
636: spin_unlock_irqrestore(&dev->lock, flags);
637:
638: event = raw_event_queue_fetch(&dev->queue);
639: if (PTR_ERR(event) == -EINTR) {
640: dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
641: return -EINTR;
642: }
643: if (IS_ERR(event)) {
644: dev_err(&dev->gadget->dev, "failed to fetch event\n");
645: spin_lock_irqsave(&dev->lock, flags);
646: dev->state = STATE_DEV_FAILED;
647: spin_unlock_irqrestore(&dev->lock, flags);
648: return -ENODEV;
649: }
]
|
| 262/6 |
2026/07/29 08:34 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 60035
output: 43
thoughts: 56
Thoughts: Okay, here's my take on those lines of code:
**Checking Line Number References in Raw Gadget Driver**
Alright, lines 640 and 644... Yes, that's what my initial assessment suggested. But I'm not going to rely on a quick memory check for something this critical. Let's make absolutely certain.
I'm going to pull up lines 720 through 745 of `drivers/usb/gadget/legacy/raw_gadget.c`. Time to verify that those line number references are spot-on with the actual code and context. Gotta be meticulous; there could be subtle differences based on revisions or specific configurations. I need to be 100% accurate before I proceed with any further analysis or proposed changes.
|
| 263/6 |
2026/07/29 08:34 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:720 LineCount:25]
Results: map[Contents: 720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
]
|
| 264/6 |
2026/07/29 08:34 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 60748
output: 44
thoughts: 69
Thoughts: Okay, here's my take on those lines of code:
**Verifying Key Code References**
Alright, based on my initial review, I'm pretty confident that lines 728, 730, 738, and 739 are indeed the relevant ones. However, as with anything critical, I need to be completely certain. To eliminate any lingering doubts, I'm going to quickly cross-reference those line numbers within the `drivers/usb/gadget/legacy/raw_gadget.c` file itself. I'll focus on lines 1110-1130, as that should give me enough context to confirm the surrounding code blocks. This is just standard practice for me: double-checking the specifics before I proceed any further. Better safe than sorry when dealing with potentially sensitive areas of the codebase.
|
| 265/6 |
2026/07/29 08:34 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1110 LineCount:20]
Results: map[Contents:1110: ep->req->zero = usb_raw_io_flags_zero(io->flags);
1111: ep->urb_queued = true;
1112: spin_unlock_irqrestore(&dev->lock, flags);
1113:
1114: ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1115: if (ret) {
1116: dev_err(&dev->gadget->dev,
1117: "fail, usb_ep_queue returned %d\n", ret);
1118: spin_lock_irqsave(&dev->lock, flags);
1119: goto out_queue_failed;
1120: }
1121:
1122: ret = wait_for_completion_interruptible(&done);
1123: if (ret) {
1124: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1125: usb_ep_dequeue(ep->ep, ep->req);
1126: wait_for_completion(&done);
1127: spin_lock_irqsave(&dev->lock, flags);
1128: if (ep->status == -ECONNRESET)
1129: ep->status = -EINTR;
]
|
| 266/6 |
2026/07/29 08:34 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 61402
output: 43
thoughts: 58
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Confirming Code References**
Alright, lines 1116 and 1124, yep, those are the right ones. I'm satisfied. But, given my level of scrutiny and what's on the line, let's just double-check that those line numbers are indeed correct, to be completely sure. I'm going to quickly read lines 210 through 225 of `drivers/usb/gadget/legacy/raw_gadget.c`. Just a quick scan to verify, and then we can move on with complete confidence. No need for any surprises. Gotta be thorough.
|
| 267/6 |
2026/07/29 08:34 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:210 LineCount:15]
Results: map[Contents: 210: kfree(dev->udc_name);
211: kfree(dev->driver.udc_name);
212: kfree(dev->driver.driver.name);
213: if (dev->driver_id_number >= 0)
214: ida_free(&driver_id_numbers, dev->driver_id_number);
215: if (dev->req) {
216: if (dev->ep0_urb_queued)
217: usb_ep_dequeue(dev->gadget->ep0, dev->req);
218: usb_ep_free_request(dev->gadget->ep0, dev->req);
219: }
220: raw_event_queue_destroy(&dev->queue);
221: for (i = 0; i < dev->eps_num; i++) {
222: if (dev->eps[i].state == STATE_EP_DISABLED)
223: continue;
224: usb_ep_disable(dev->eps[i].ep);
]
|
| 268/6 |
2026/07/29 08:34 |
llm |
codeexpert |
0m
Model:
Tokens:
input: 61953
output: 941
thoughts: 1292
Thoughts: **Analysis of `dev->gadget` Dereferences in `raw_gadget.c`**
Okay, so I've confirmed that the initial assessment of lines 217 and 218 is accurate. Now, I need to make absolutely sure there aren't any *other* instances where `dev->gadget` is dereferenced without proper locking. The question specifically references the `raw_gadget.c` file, so I need to stick to that.
I went through the code systematically. First, I focused on the list provided. It pointed out these potential areas:
1. `dev_free` (lines 217, 218): The dereferences here are `usb_ep_dequeue` and `usb_ep_free_request`, both using `dev->gadget->ep0`. However, this occurs *within* `dev_free`, which is called when the reference count of the device drops to zero. That makes this inherently safe from concurrent access, so no lock is necessary. I'm satisfied.
2. `raw_ioctl_event_fetch` (lines 640, 644): Here, `dev_dbg` and `dev_err` are called using `dev->gadget->dev`. The key here is the `dev->lock` being released *before* the calls to the logging functions and being re-acquired only in error paths after `raw_event_queue_fetch`. This is properly synchronized.
3. `raw_process_ep0_io` (lines 728, 730, 738, 739): I looked at multiple calls here - `usb_ep_queue`, `dev_err`, `dev_dbg`, and `usb_ep_dequeue`, all referencing `dev->gadget`. I see `dev->lock` is released at the beginning and re-acquired in error paths or after the wait completes. Sounds right.
4. `raw_process_ep_io` (lines 1116, 1124): Again, similar pattern here - multiple calls to functions with dereferences, and I can see the locking scheme is correctly implemented as it was in `raw_process_ep0_io`.
Now, I decided to be thorough. I performed a *complete* scan of the code for any uses of `dev->gadget`, to make sure nothing was missed. I went through the following functions:
* `gadget_bind`: Here, I found the expected: `dev->gadget` being set and used in a loop. Crucially, the code *holds* `dev->lock` throughout the relevant sections, acquired at line 301 and released at line 313. This is correct.
* `raw_ioctl_ep_enable`, `raw_ioctl_ep_disable`, `raw_ioctl_ep_set_clear_halt_wedge`, `raw_ioctl_configure`, `raw_ioctl_vbus_draw`, and `raw_ioctl_ep0_stall`: I checked these functions. In all cases, `dev->gadget` is used and *protected* by `dev->lock` being held. I found many calls that I was expecting (like to `dev_dbg`, and calls using `ep0`). Locking is acquired *before* use (e.g. at line 862, 925, 980, 1189, 1212, and 803) and released later on.
I believe this is a truly exhaustive review of all uses of `dev->gadget` within the specified file. I'm confident in my analysis and will provide these details as the complete, accurate findings. I'm ready to call `set-results`.
|
| 269/6 |
2026/07/29 08:34 |
tool |
set-results |
0m
Args: map[Answer:Yes, there are several other places in `drivers/usb/gadget/legacy/raw_gadget.c` where `dev->gadget` is dereferenced without holding `dev->lock`.
Here is the complete list of those occurrences:
### 1. In `raw_ioctl_event_fetch()`
* **Lines 640 and 644:**
```c
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
```
* **Explanation:** `dev->lock` is released at line 636 (`spin_unlock_irqrestore(&dev->lock, flags);`) before calling `raw_event_queue_fetch()`, and is only re-acquired at line 645 inside the `IS_ERR(event)` block.
---
### 2. In `raw_process_ep0_io()`
* **Lines 728, 730, 738, and 739:**
```c
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
```
* **Explanation:** `dev->lock` is released at line 726 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 732, 741) or after the wait completes (line 747).
---
### 3. In `raw_process_ep_io()`
* **Lines 1116 and 1124:**
```c
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
```
* **Explanation:** `dev->lock` is released at line 1112 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 1118, 1127) or after the wait completes (line 1133).
---
### 4. In `dev_free()` (Safe context, but lock is not held)
* **Lines 217 and 218:**
```c
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
```
* **Explanation:** `dev->lock` is not held here. However, since this is inside `dev_free()` (which is called via `kref_put()` when the reference count of `dev` drops to 0), there is no possibility of concurrent access, making it safe.]
Results: map[Answer:Yes, there are several other places in `drivers/usb/gadget/legacy/raw_gadget.c` where `dev->gadget` is dereferenced without holding `dev->lock`.
Here is the complete list of those occurrences:
### 1. In `raw_ioctl_event_fetch()`
* **Lines 640 and 644:**
```c
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
```
* **Explanation:** `dev->lock` is released at line 636 (`spin_unlock_irqrestore(&dev->lock, flags);`) before calling `raw_event_queue_fetch()`, and is only re-acquired at line 645 inside the `IS_ERR(event)` block.
---
### 2. In `raw_process_ep0_io()`
* **Lines 728, 730, 738, and 739:**
```c
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
```
* **Explanation:** `dev->lock` is released at line 726 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 732, 741) or after the wait completes (line 747).
---
### 3. In `raw_process_ep_io()`
* **Lines 1116 and 1124:**
```c
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
```
* **Explanation:** `dev->lock` is released at line 1112 (`spin_unlock_irqrestore(&dev->lock, flags);`), and is only re-acquired inside the error paths (lines 1118, 1127) or after the wait completes (line 1133).
---
### 4. In `dev_free()` (Safe context, but lock is not held)
* **Lines 217 and 218:**
```c
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
```
* **Explanation:** `dev->lock` is not held here. However, since this is inside `dev_free()` (which is called via `kref_put()` when the reference count of `dev` drops to 0), there is no possibility of concurrent access, making it safe.]
|
| 270/4 |
2026/07/29 08:34 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 104003
output: 42
thoughts: 373
Thoughts: **Debugging a Potential NULL Pointer Dereference**
Okay, so I thought I had squashed all the potential issues related to memory corruption within the raw USB gadget driver. The codeexpert confirmed that the only spots where I needed to focus were in `raw_ioctl_event_fetch`, `raw_process_ep0_io`, and `raw_process_ep_io`. And, I was confident I'd fixed those!
But wait, hold on. I just realized something potentially catastrophic in `dev_free`. Looking at the code:
```c
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
int i;
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
```
It's dereferencing `dev->gadget->ep0`! That's a huge red flag! The key here is to see when `dev_free` is called: it's tied to `kref_put` when the reference count goes to zero. Now, where are the reference count modifications? They're incremented in `gadget_bind` and `raw_ioctl_run`, and decremented in `gadget_unbind` and `raw_release`.
If `gadget_unbind` is called, it *sets* `dev->gadget = NULL` before it calls `kref_put`. Similarly, if `raw_release` is called, it also calls `kref_put`.
The problem is, if `dev_free` is being *called*, it means BOTH `gadget_unbind` AND `raw_release` have been called! But if `gadget_unbind` sets `dev->gadget = NULL` before calling `kref_put`, then `dev_free` is going to try to dereference a NULL pointer! That's a classic NULL pointer dereference, and that's a crash waiting to happen. Time to dig deeper into the call paths of `dev_free`.
|
| 271/4 |
2026/07/29 08:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:dev_free]
Results: map[SourceCode: 205: static void dev_free(struct kref *kref)
206: {
207: struct raw_dev *dev = container_of(kref, struct raw_dev, count);
208: int i;
209:
210: kfree(dev->udc_name);
211: kfree(dev->driver.udc_name);
212: kfree(dev->driver.driver.name);
213: if (dev->driver_id_number >= 0)
214: ida_free(&driver_id_numbers, dev->driver_id_number);
215: if (dev->req) {
216: if (dev->ep0_urb_queued)
217: usb_ep_dequeue(dev->gadget->ep0, dev->req);
218: usb_ep_free_request(dev->gadget->ep0, dev->req);
219: }
220: raw_event_queue_destroy(&dev->queue);
221: for (i = 0; i < dev->eps_num; i++) {
222: if (dev->eps[i].state == STATE_EP_DISABLED)
223: continue;
224: usb_ep_disable(dev->eps[i].ep);
225: usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
226: kfree(dev->eps[i].ep->desc);
227: dev->eps[i].state = STATE_EP_DISABLED;
228: }
229: kfree(dev);
230: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 272/4 |
2026/07/29 08:34 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 105414
output: 655
thoughts: 937
Thoughts: **Analyzing a USB Gadget Driver Vulnerability**
Okay, wait a minute. I'm looking at this USB gadget driver, and I think I've spotted something. If `gadget_unbind` is called and then `raw_release` follows, `dev_free` will eventually be called. The critical point is that `dev_free` uses `dev->gadget->ep0`, but if `gadget_unbind` has run and set `dev->gadget` to `NULL`, that's a classic NULL pointer dereference.
Hold on, how has this not crashed already? The allocation of `dev->req` *is* done in `gadget_bind`. And it's *not* freed in `gadget_unbind`. I thought initially it was leaked, but no, `dev_free` does clean that up! However, the problem remains!
The original `gadget_unbind` code *didn't* set `dev->gadget = NULL`! It only set the driver data to NULL! This means that `dev->gadget` in `dev_free` still points to the *freed* gadget. And, inside `dev_free`, `usb_ep_free_request(dev->gadget->ep0, dev->req);` is called! *That's* a use-after-free vulnerability! If `dev_free` is called after the gadget has already been freed, it's going to dereference a dangling pointer and call `usb_ep_free_request` on it! Another UAF!
So, the solution is obvious: We *must* free `dev->req` in `gadget_unbind`! This is the fix. After freeing, `dev->req = NULL` and `dev_free` won't try to free it again!
But wait, what about the endpoint requests `dev->eps[i].req`? *Those* are also freed by `dev_free`. If `gadget_unbind` has run, all endpoints *should* have been disabled by the UDC core. But, `dev_free` then tries to disable them *again* and free their requests *again*! That's another use-after-free, since the gadget might already be gone by the time `dev_free` runs. The cleanup needs to happen *before* the gadget is freed.
So, *all* endpoint requests, must be handled in `gadget_unbind`, to avoid dereferencing freed resources!
|
| 273/4 |
2026/07/29 08:35 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->state = STATE_DEV_FAILED;
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
spin_unlock_irqrestore(&dev->lock, flags);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
} NewCode:static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(done);
int i;
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
dev->gadget = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state == STATE_EP_DISABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
}
if (dev->req) {
usb_ep_free_request(gadget->ep0, dev->req);
dev->req = NULL;
}
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 274/4 |
2026/07/29 08:35 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 107552
output: 43
thoughts: 80
Thoughts: Okay, let me think this through.
**Analyzing Resource Management in the USB Gadget Driver**
Hmm, hold on a second. If we're releasing `dev->req` inside `gadget_unbind`, then *logically* we shouldn't need to also free it in `dev_free`, right? That seems redundant, and possibly a source of double-free bugs.
But...what about the error case? What if `gadget_bind` fails? If `gadget_bind` errors out, it'll return an error code, and *crucially*, `gadget_unbind` *won't* be invoked. That's the critical point. We need to handle that scenario. This means `dev->req` wouldn't be freed in those circumstances.
Right, so we need to analyze `gadget_bind` more closely. We need to ensure that when `gadget_bind` fails, `dev->req` is cleaned up correctly, probably *there*. Let's go check the code for that function. I need to make sure we're managing resources correctly and completely.
|
| 275/4 |
2026/07/29 08:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:gadget_bind]
Results: map[SourceCode: 281: static int gadget_bind(struct usb_gadget *gadget,
282: struct usb_gadget_driver *driver)
283: {
284: int ret = 0, i = 0;
285: struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
286: struct usb_request *req;
287: struct usb_ep *ep;
288: unsigned long flags;
289:
290: if (strcmp(gadget->name, dev->udc_name) != 0)
291: return -ENODEV;
292:
293: set_gadget_data(gadget, dev);
294: req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
295: if (!req) {
296: dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
297: set_gadget_data(gadget, NULL);
298: return -ENOMEM;
299: }
300:
301: spin_lock_irqsave(&dev->lock, flags);
302: dev->req = req;
303: dev->req->context = dev;
304: dev->req->complete = gadget_ep0_complete;
305: dev->gadget = gadget;
306: gadget_for_each_ep(ep, dev->gadget) {
307: dev->eps[i].ep = ep;
308: dev->eps[i].addr = get_ep_addr(ep->name);
309: dev->eps[i].state = STATE_EP_DISABLED;
310: i++;
311: }
312: dev->eps_num = i;
313: spin_unlock_irqrestore(&dev->lock, flags);
314:
315: dev_dbg(&gadget->dev, "gadget connected\n");
316: ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
317: if (ret < 0) {
318: dev_err(&gadget->dev, "failed to queue connect event\n");
319: set_gadget_data(gadget, NULL);
320: return ret;
321: }
322:
323: /* Matches kref_put() in gadget_unbind(). */
324: kref_get(&dev->count);
325: return ret;
326: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 276/4 |
2026/07/29 08:35 |
llm |
patch-generator |
5m
Model:
Tokens:
input: 108690
output: 363
thoughts: 189
Thoughts: **My Concerns Regarding USB Gadget Binding Failure**
Okay, so I'm running through this code, and a critical issue just jumped out at me. If `raw_queue_event` fails, then `gadget_bind` will return an error, and *crucially* `gadget_unbind` **isn't** called. Now, here's the problem: `dev->req` has already been allocated at this point. Furthermore, `dev->gadget` is set!
The documentation says that if `gadget_bind` *succeeds*, then when `raw_release` is called, `dev_free` will be invoked, which *should* handle freeing `dev->req` using `usb_ep_free_request(dev->gadget->ep0, dev->req)`. But if `gadget_bind` fails, this clean-up doesn't happen *and*, here's the kicker, the gadget itself *might* be freed by the UDC core in response to that failure!
And if the UDC core frees the gadget, `dev->gadget` is now a dangling pointer. Yikes! That's a textbook use-after-free scenario waiting to happen. Therefore, it's clear: if `gadget_bind` fails, we **absolutely must** free `dev->req` *ourselves* and, even more importantly, *set* `dev->gadget = NULL` to prevent any further attempts to use that pointer. Otherwise, we're asking for trouble.
|
| 277/4 |
2026/07/29 08:41 |
tool |
codeeditor |
0m
Args: map[CurrentCode: dev_dbg(&gadget->dev, "gadget connected\n");
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
set_gadget_data(gadget, NULL);
return ret;
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
} NewCode: dev_dbg(&gadget->dev, "gadget connected\n");
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->req = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 278/4 |
2026/07/29 08:41 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 109578
output: 502
thoughts: 268
Thoughts: **Analyzing the Gadget Driver's Resource Management**
Okay, so I'm thinking through this driver's resource management. It looks like `dev->req` is freed either in `gadget_unbind` *or* in the error path of `gadget_bind`. This implies that `dev_free` doesn't *need* to explicitly free `dev->req`. But, hold on a second⦠what if `raw_release` gets called *before* `gadget_unbind`? Let's trace it. If `raw_release` is called first, here's what happens:
```c
if (dev->gadget_registered)
unregister = true;
dev->gadget_registered = false;
spin_unlock_irqrestore(&dev->lock, flags);
if (unregister)
usb_gadget_unregister_driver(&dev->driver);
```
Right, and `usb_gadget_unregister_driver` *will* call `gadget_unbind`! This seems to mean `gadget_unbind` is *always* called before `dev_free`! That makes sense. Wait a minute. What if `usb_gadget_unregister_driver` is called, but the gadget was *never* actually bound in the first place? If it never bound, `dev->req` would be `NULL` and `dev->gadget` would be `NULL`, too. So, in that specific case, `dev_free` wouldn't need to do anything anyway! Okay, time to go double-check `dev_free` itself.
|
| 279/4 |
2026/07/29 08:41 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
raw_event_queue_destroy(&dev->queue);
for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state == STATE_EP_DISABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
}
kfree(dev);
} NewCode:static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
raw_event_queue_destroy(&dev->queue);
kfree(dev);
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 280/4 |
2026/07/29 08:41 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 110704
output: 30
thoughts: 1286
Thoughts: **Refining the USB Gadget Unbind Logic**
Okay, let's break this down. My initial concern was about resource management, specifically `dev->req` and `dev->eps[i].req`. If `gadget_unbind` is called, it explicitly frees these, which makes sense. But what if it *isn't* called? Can we somehow end up in a situation where `gadget_bind` succeeded, but `gadget_unbind` isn't executed? No, because `usb_gadget_unregister_driver` *will* call `gadget_unbind` if `gadget_bind` was successful.
Then the question came to mind, what if `usb_gadget_unregister_driver` is called *without* `gadget_bind` ever being called? In this scenario, `dev->req` is `NULL`, and `dev->eps_num` is zero. Therefore, there's nothing to free! So, there is no need to free these in `dev_free`. This is a much cleaner and safer approach!
Now, revisiting `gadget_unbind`. It's clear that it handles the resource deallocation, but there's a potential race condition around queued URBs (USB Request Blocks). Specifically, I questioned the need for `usb_ep_dequeue` within `gadget_unbind`. My thinking was: is `dev->ep0_urb_queued` true? Should we call `usb_ep_dequeue`? However, `pullup(0)` is always called before this, and `pullup(0)` is explicitly designed to cancel all pending requests. Therefore, `usb_ep_dequeue` is redundant.
If `pullup(0)` *didn't* cancel the request, then the `wait_for_completion` in `raw_process_ep0_io` would block indefinitely. This means that `gadget_unbind` would block indefinitely waiting for `udc_usage` to drop to zero! Since we know that `pullup(0)` *does* cancel the request, this cannot happen! Therefore, `dev->ep0_urb_queued` is always false when `gadget_unbind` proceeds.
What if `raw_process_ep0_io` is interrupted and calls `usb_ep_dequeue` but is waiting for completion? In that case, `udc_usage` is held, so `gadget_unbind` waits. When `raw_process_ep0_io` finishes, `udc_usage` drops, and `gadget_unbind` proceeds. At this point, the request is already dequeued and completed, so it is safe to free `dev->req`. The same logic applies to `dev->eps[i].urb_queued`. `raw_process_ep_io` holds `udc_usage` while waiting, so when `gadget_unbind` proceeds, all pending requests are completed, making `usb_ep_free_request` safe.
Okay, let's solidify this. Is it even *possible* for `raw_process_ep0_io` to queue a request and NOT wait for completion? No. `raw_process_ep0_io` *always* waits for completion. The code clearly shows this: `ret = wait_for_completion_interruptible(&dev->ep0_done);`. If interrupted, it calls `usb_ep_dequeue`, then *still* waits for completion. The same is true for `raw_process_ep_io`. Therefore, there are NEVER any pending requests when `gadget_unbind` proceeds. This means we do *not* need `usb_ep_dequeue` in `gadget_unbind`.
One more edge case: what if a request is queued while `raw_process_ep0_io` is *not* running? Impossible! `raw_process_ep0_io` is a synchronous ioctl, blocking until the request completes. A request can *only* be queued while `raw_process_ep0_io` is running. So, if `raw_process_ep0_io` isn't running, no request is pending! Brilliant! It's all consistent! Now, let's review the patch.
|
| 281/4 |
2026/07/29 08:41 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..0137b3ad1 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -160,28 +160,30 @@ enum dev_state {
struct raw_dev {
struct kref count;
spinlock_t lock;
const char *udc_name;
struct usb_gadget_driver driver;
/* Reference to misc device: */
struct device *dev;
/* Make driver names unique */
int driver_id_number;
/* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
int eps_num;
struct completion ep0_done;
struct raw_event_queue queue;
};
@@ -198,41 +200,27 @@ static struct raw_dev *dev_new(void)
spin_lock_init(&dev->lock);
init_completion(&dev->ep0_done);
raw_event_queue_init(&dev->queue);
dev->driver_id_number = -1;
return dev;
}
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
/*----------------------------------------------------------------------*/
static int raw_queue_event(struct raw_dev *dev,
enum usb_raw_event_type type, size_t length, const void *data)
{
int ret = 0;
unsigned long flags;
@@ -281,60 +269,92 @@ static u8 get_ep_addr(const char *name)
static int gadget_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int ret = 0, i = 0;
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
struct usb_request *req;
struct usb_ep *ep;
unsigned long flags;
if (strcmp(gadget->name, dev->udc_name) != 0)
return -ENODEV;
set_gadget_data(gadget, dev);
req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!req) {
dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
set_gadget_data(gadget, NULL);
return -ENOMEM;
}
spin_lock_irqsave(&dev->lock, flags);
dev->req = req;
dev->req->context = dev;
dev->req->complete = gadget_ep0_complete;
dev->gadget = gadget;
gadget_for_each_ep(ep, dev->gadget) {
dev->eps[i].ep = ep;
dev->eps[i].addr = get_ep_addr(ep->name);
dev->eps[i].state = STATE_EP_DISABLED;
i++;
}
dev->eps_num = i;
spin_unlock_irqrestore(&dev->lock, flags);
dev_dbg(&gadget->dev, "gadget connected\n");
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
}
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
static int gadget_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
int ret = 0;
@@ -615,44 +635,44 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
{
struct usb_raw_event arg;
unsigned long flags;
struct usb_raw_event *event;
uint32_t length;
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
return -EFAULT;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EINVAL;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
kfree(event);
return -EFAULT;
}
kfree(event);
return 0;
}
@@ -683,74 +703,99 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->ep0_urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !dev->ep0_in_pending) ||
(!in && !dev->ep0_out_pending)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EBUSY;
goto out_unlock;
}
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -920,54 +965,58 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (i < 0 || i >= dev->eps_num) {
dev_dbg(dev->dev, "fail, invalid endpoint\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->eps[i].state == STATE_EP_DISABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].disabling) {
dev_dbg(&dev->gadget->dev,
"fail, disable already in progress\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -1058,85 +1107,110 @@ static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
struct raw_ep *ep;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (io->ep >= dev->eps_num) {
dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
ret = -EINVAL;
goto out_unlock;
}
ep = &dev->eps[io->ep];
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->disabling) {
dev_dbg(&dev->gadget->dev,
"fail, endpoint is already being disabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if (in != usb_endpoint_dir_in(ep->ep->desc)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EINVAL;
goto out_unlock;
}
ep->dev = dev;
ep->req->context = &done;
ep->req->complete = gadget_ep_complete;
ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
]
|
| 282/4 |
2026/07/29 08:41 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 118916
output: 796
thoughts: 154
Thoughts: **Patch Review and Thoughts**
Okay, this patch seems pretty solid. My initial assessment is that it looks complete and addresses the core problem β the dreaded Use-After-Free vulnerabilities. It seems like the developer has tackled the concurrency issues in `gadget_unbind` and UDC functions using a `udc_usage` counter, along with a completion mechanism, which is a good, robust way to manage those kinds of race conditions.
Furthermore, they've clearly thought through the issue of dereferencing `dev->gadget` after a call to `gadget_unbind`. Protecting access to `dev->gadget` under `dev->lock` is the right move, and using `dev->dev` for logging instead is a smart precaution. That'll prevent further potential headaches down the line.
Finally, the fix in `dev_free` looks well-considered. Moving the cleanup of `dev->req` and `dev->eps[i].req` to `gadget_unbind` *and* the error path in `gadget_bind` provides comprehensive coverage. This is exactly the kind of preventative measure I'd expect to see.
Overall, it looks like a well-structured and thoughtfully-written patch that addresses the identified vulnerabilities with a good balance of protection and performance. I'm satisfied. Now, let's get that final reply written.
|
| 283/3 |
2026/07/29 08:41 |
action |
test-patch |
29m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..0137b3ad1 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,33 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +657,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +743,47 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -956,11 +1001,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1158,47 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
TestError:------------[ cut here ]------------
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25, CPU#0: syz-executor214/8613
Modules linked in:
CPU: 0 UID: 0 PID: 8613 Comm: syz-executor214 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25
Code: eb 66 85 db 74 3e 83 fb 01 75 4c e8 fb 16 fa fc 48 8d 3d 24 f1 0f 0b 67 48 0f b9 3a eb 4a e8 e8 16 fa fc 48 8d 3d 21 f1 0f 0b <67> 48 0f b9 3a eb 37 e8 d5 16 fa fc 48 8d 3d 1e f1 0f 0b 67 48 0f
RSP: 0018:ffffc900219df968 EFLAGS: 00010293
RAX: ffffffff84cb4ed8 RBX: 0000000000000002 RCX: ffff88811356a540
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffff8fdb4000
RBP: 0000000000000000 R08: ffff88811356a540 R09: 0000000000000005
R10: 0000000000000100 R11: 0000000000000004 R12: ffff8881127d55d8
R13: 0000000000000000 R14: ffff88810c668d90 R15: dffffc0000000000
FS: 000055558f18d400(0000) GS:ffff8881a5c07000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f8a1fbfd7fc CR3: 000000010e082000 CR4: 0000000000352ef0
Call Trace:
<TASK>
__refcount_add include/linux/refcount.h:-1 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
refcount_inc include/linux/refcount.h:383 [inline]
kref_get include/linux/kref.h:45 [inline]
kobject_get+0xfa/0x120 lib/kobject.c:643
get_device drivers/base/core.c:3868 [inline]
device_add+0x24/0xb80 drivers/base/core.c:3648
usb_add_gadget+0x47d/0x700 drivers/usb/gadget/udc/core.c:1461
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f8a1fb966b9
Code: c0 79 93 eb d5 48 8d 7c 1d 00 eb 99 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 d8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffda9aa1da8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000200000000100 RCX: 00007f8a1fb966b9
RDX: 000000000000000b RSI: 0000200000000400 RDI: 0000000000000005
RBP: 00000000000f4240 R08: 0000000000000009 R09: 0000000000000009
R10: 0000000000000009 R11: 0000000000000246 R12: 0000000000072cf8
R13: 00007ffda9aa1dbc R14: 0000200000000104 R15: 00007ffda9aa1dc0
</TASK>
----------------
Code disassembly (best guess):
0: eb 66 jmp 0x68
2: 85 db test %ebx,%ebx
4: 74 3e je 0x44
6: 83 fb 01 cmp $0x1,%ebx
9: 75 4c jne 0x57
b: e8 fb 16 fa fc call 0xfcfa170b
10: 48 8d 3d 24 f1 0f 0b lea 0xb0ff124(%rip),%rdi # 0xb0ff13b
17: 67 48 0f b9 3a ud1 (%edx),%rdi
1c: eb 4a jmp 0x68
1e: e8 e8 16 fa fc call 0xfcfa170b
23: 48 8d 3d 21 f1 0f 0b lea 0xb0ff121(%rip),%rdi # 0xb0ff14b
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: eb 37 jmp 0x68
31: e8 d5 16 fa fc call 0xfcfa170b
36: 48 8d 3d 1e f1 0f 0b lea 0xb0ff11e(%rip),%rdi # 0xb0ff15b
3d: 67 addr32
3e: 48 rex.W
3f: 0f .byte 0xf
]
|
| 284/2 |
2026/07/29 09:10 |
iteration |
1 |
27m
|
| 285/3 |
2026/07/29 09:10 |
agent |
patch-generator |
13m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
The explanation of the root cause of the bug is:
The root cause of this crash is a use-after-free (UAF) vulnerability in the `raw_gadget` driver. It occurs because the driver fails to properly clean up its internal state and synchronize concurrent operations when the underlying USB Device Controller (UDC) is unbound.
### Detailed Sequence of Events
1. **Gadget Initialization and Binding:**
The reproducer opens `/dev/raw-gadget` and initializes it. When the gadget binds to the `dummy_udc` controller, the UDC core invokes the `gadget_bind()` callback. This function saves the `struct usb_gadget` pointer into `dev->gadget` and eventually sets `dev->state = STATE_DEV_RUNNING`.
2. **UDC Unbinding:**
The reproducer then writes to `/sys/bus/platform/drivers/dummy_udc/unbind` via sysfs. This forces the UDC core to unbind the gadget driver, which invokes the `gadget_unbind()` callback in `raw_gadget.c`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
Notice that `gadget_unbind()` **fails to clear `dev->gadget`** and **fails to update `dev->state`**. It only clears the driver data. After this function returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device` (including the device name string allocated via `kvasprintf`).
3. **Dangling Pointer Dereference:**
The reproducer then issues a `USB_RAW_IOCTL_EP0_READ` (or `WRITE`) ioctl, which lands in `raw_process_ep0_io()`:
```c
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, void *data, bool in)
{
// ...
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { // Passes, state was never updated
// ...
}
if (!dev->gadget) { // Passes, dev->gadget is a dangling pointer
// ...
}
// ...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);
// ...
```
Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
4. **The Crash:**
Since the UDC is disconnected/freed, `usb_ep_queue()` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, ...)`. The `dev_err()` macro attempts to print the device name (`dev->gadget->dev.kobj.name`), which points to the freed 16-byte string, resulting in the KASAN `slab-use-after-free` crash observed in the trace.
### How to Fix
To fix this bug, `gadget_unbind()` must clear the `dev->gadget` pointer and update `dev->state` (e.g., to `STATE_DEV_FAILED`) while holding `dev->lock`.
However, simply clearing the pointer is not enough due to race conditions. If an ioctl thread has already passed the `!dev->gadget` check and released `dev->lock`, but hasn't yet called `usb_ep_queue()` (which is called lockless), `gadget_unbind()` could return and free the gadget right under the ioctl's feet.
To prevent this race, `raw_gadget` needs a synchronization mechanism to track ongoing UDC calls and wait for them to finish before returning from `gadget_unbind()`. A common pattern for this (as seen in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`) is to introduce a `udc_usage` counter:
- Increment `udc_usage` under `dev->lock` before releasing the lock to call `usb_ep_queue()`.
- Decrement `udc_usage` under `dev->lock` after `usb_ep_queue()` returns.
- In `gadget_unbind()`, wait for `udc_usage` to drop to `0` before returning.
Historical context on how similar bugs were fixed in the past:
Based on a review of the kernel history for the USB gadget subsystem (specifically within `drivers/usb/gadget/`), there are several established patterns for fixing use-after-free (UAF) and race conditions during the unbind phase.
Here is a summary of the idioms, locking rules, and common patterns used to address similar bugs:
### 1. The Usage Counter Pattern (Legacy Gadgetfs)
As you noted, `drivers/usb/gadget/legacy/inode.c` (`gadgetfs`) uses a `udc_usage` counter. This was introduced in commit `520b72fc64de` ("USB: gadgetfs: Fix crash caused by inadequate synchronization") to fix a very similar crash.
* **How it works:** The driver increments `udc_usage` under a spinlock before releasing the lock to call lockless UDC functions (like `usb_ep_queue`). It decrements it after re-acquiring the lock.
* **The Wait Mechanism:** In `gadgetfs_unbind()`, it uses a polling loop to wait for the counter to drop to zero:
```c
while (dev->udc_usage > 0) {
spin_unlock_irq(&dev->lock);
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
```
* **Takeaway:** While this is the exact precedent in the legacy gadget directory, polling with `usleep_range()` is generally discouraged in modern kernel development if better primitives are available.
### 2. Explicit Synchronization via Completions (Modern Approach)
More recent fixes in the gadget subsystem prefer using `struct completion` to synchronize unbind routines with ongoing asynchronous operations.
* **Example:** Commit `eba2936bbe6b` ("usb: gadget: uvc: fix NULL pointer dereference during unbind race") in `f_uvc.c` fixed a UAF during unbind by introducing a `completion`.
* **How it works:** The unbind function sets a flag (protected by a mutex) to prevent new operations, and then calls `wait_for_completion()`. The ongoing operation calls `complete()` when it finishes its critical section, allowing the unbind to safely proceed and free the memory.
### 3. Mutex Serialization
If the ongoing operation can safely block, simply serializing the unbind and the ioctl/queue operations with a mutex is a common fix.
* **Example:** Commit `6a19da111057` ("usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait") in `f_fs.c` fixed a UAF where `functionfs_unbind` was freeing the request while `ffs_ep0_write` was using it.
* **How it works:** They wrapped the teardown logic in `functionfs_unbind()` with `mutex_lock(&ffs->mutex)`, ensuring it could not run concurrently with the queueing logic.
### 4. Strict State Machine Enforcement
In `raw_gadget.c` itself, previous race conditions have been fixed by strictly enforcing state transitions.
* **Example:** Commit `5f0b5f4d50fa` ("usb: gadget: fix race when gadget driver register via ioctl") fixed a double-registration race by introducing a `STATE_DEV_REGISTERING` state.
* **Takeaway:** For your bug, updating `dev->state` (e.g., to `STATE_DEV_FAILED` or a new `STATE_DEV_UNBOUND`) inside `gadget_unbind()` while holding `dev->lock` is a mandatory first step. This ensures that any *new* ioctl calls will fail the `dev->state == STATE_DEV_RUNNING` check and bail out early.
### Summary Recommendation for the Fix
To fix this bug in `raw_gadget.c` while adhering to kernel idioms:
1. **State Update:** In `gadget_unbind()`, acquire `dev->lock`, clear `dev->gadget`, and set `dev->state = STATE_DEV_FAILED` (or similar).
2. **Synchronization:** To protect *already running* ioctls that have passed the state check, introduce a tracking mechanism.
* You can use the `udc_usage` counter approach for consistency with `gadgetfs`.
* However, instead of a `usleep_range()` polling loop, it is much more idiomatic to use a `wait_queue_head_t` (e.g., `wait_event_lock_irq()`) or a `completion` to put the `gadget_unbind()` thread to sleep until the last ongoing `usb_ep_queue()` finishes and wakes it up.
Another developer tried to fix this bug, and come up with the following strategy for fixing:
The root cause of this bug is a race condition between `gadget_unbind()` and various ioctls (like `raw_process_ep0_io()`, `raw_process_ep_io()`, and `raw_ioctl_ep_disable()`) that drop `dev->lock` to call UDC functions (e.g., `usb_ep_queue()`, `usb_ep_dequeue()`, `usb_ep_disable()`).
If `gadget_unbind()` is invoked concurrently, it clears the gadget driver data and returns, allowing the UDC core to free the `struct usb_gadget`. However, the ioctls might still be running or waiting for completion. When they wake up or fail, they dereference the freed `dev->gadget` pointer (e.g., to print an error message or to call `usb_ep_dequeue()`), leading to a KASAN slab-use-after-free.
Additionally, there was another UAF in `dev_free()`: `gadget_unbind()` did not free the endpoint requests (`dev->req` and `dev->eps[i].req`), leaving them to be freed by `dev_free()` when the device is closed. But `dev_free()` used `dev->gadget->ep0` to free them, which is a UAF if the gadget has already been unbound and freed.
### Fix Details
1. **Synchronization with `udc_usage` and `completion`:**
Similar to `gadgetfs`, we introduce a `udc_usage` counter to track ongoing UDC calls.
- Before dropping `dev->lock` to call a UDC function, we increment `udc_usage`.
- After re-acquiring `dev->lock`, we decrement `udc_usage` and, if it reaches 0, signal a completion.
- In `gadget_unbind()`, we set `dev->state = STATE_DEV_FAILED` and wait for `udc_usage` to drop to 0 using `wait_for_completion()`. This ensures no UDC calls are running when the gadget is unbound.
2. **Avoiding Deadlocks during `wait_for_completion`:**
We strictly hold `udc_usage` *only* while calling UDC functions, and **not** while waiting for the IO completion (`wait_for_completion_interruptible()`). Holding it during the wait could cause a deadlock if the UDC core waits for `gadget_unbind()` to finish before stopping the controller.
3. **Safe Gadget Dereferencing:**
After waking up from `wait_for_completion_interruptible()`, we must re-check `dev->gadget` under `dev->lock` before calling `usb_ep_dequeue()`. If `dev->gadget` is NULL, it means `gadget_unbind()` has already run, the controller is stopped, and the request is already cancelled, so we can safely skip the dequeue.
We also replace `dev_err(&dev->gadget->dev, ...)` with `dev_err(dev->dev, ...)` in paths where `dev->lock` is not held, as `dev->dev` (the misc device) is always safe to access.
4. **Proper Cleanup in `gadget_unbind()`:**
We move the cleanup of `dev->req` and `dev->eps[i].req` from `dev_free()` to `gadget_unbind()` (and the `gadget_bind()` error path). This prevents `dev_free()` from dereferencing the freed `dev->gadget` pointer long after the gadget has been unbound.
and the following patch:
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..0137b3ad1 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,33 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +657,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +743,47 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -956,11 +1001,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1158,47 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ dev->udc_usage++;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
However, the patch testing failed with the following error:
------------[ cut here ]------------
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25, CPU#0: syz-executor214/8613
Modules linked in:
CPU: 0 UID: 0 PID: 8613 Comm: syz-executor214 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25
Code: eb 66 85 db 74 3e 83 fb 01 75 4c e8 fb 16 fa fc 48 8d 3d 24 f1 0f 0b 67 48 0f b9 3a eb 4a e8 e8 16 fa fc 48 8d 3d 21 f1 0f 0b <67> 48 0f b9 3a eb 37 e8 d5 16 fa fc 48 8d 3d 1e f1 0f 0b 67 48 0f
RSP: 0018:ffffc900219df968 EFLAGS: 00010293
RAX: ffffffff84cb4ed8 RBX: 0000000000000002 RCX: ffff88811356a540
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffff8fdb4000
RBP: 0000000000000000 R08: ffff88811356a540 R09: 0000000000000005
R10: 0000000000000100 R11: 0000000000000004 R12: ffff8881127d55d8
R13: 0000000000000000 R14: ffff88810c668d90 R15: dffffc0000000000
FS: 000055558f18d400(0000) GS:ffff8881a5c07000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f8a1fbfd7fc CR3: 000000010e082000 CR4: 0000000000352ef0
Call Trace:
<TASK>
__refcount_add include/linux/refcount.h:-1 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
refcount_inc include/linux/refcount.h:383 [inline]
kref_get include/linux/kref.h:45 [inline]
kobject_get+0xfa/0x120 lib/kobject.c:643
get_device drivers/base/core.c:3868 [inline]
device_add+0x24/0xb80 drivers/base/core.c:3648
usb_add_gadget+0x47d/0x700 drivers/usb/gadget/udc/core.c:1461
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f8a1fb966b9
Code: c0 79 93 eb d5 48 8d 7c 1d 00 eb 99 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 d8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffda9aa1da8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000200000000100 RCX: 00007f8a1fb966b9
RDX: 000000000000000b RSI: 0000200000000400 RDI: 0000000000000005
RBP: 00000000000f4240 R08: 0000000000000009 R09: 0000000000000009
R10: 0000000000000009 R11: 0000000000000246 R12: 0000000000072cf8
R13: 00007ffda9aa1dbc R14: 0000200000000104 R15: 00007ffda9aa1dc0
</TASK>
----------------
Code disassembly (best guess):
0: eb 66 jmp 0x68
2: 85 db test %ebx,%ebx
4: 74 3e je 0x44
6: 83 fb 01 cmp $0x1,%ebx
9: 75 4c jne 0x57
b: e8 fb 16 fa fc call 0xfcfa170b
10: 48 8d 3d 24 f1 0f 0b lea 0xb0ff124(%rip),%rdi # 0xb0ff13b
17: 67 48 0f b9 3a ud1 (%edx),%rdi
1c: eb 4a jmp 0x68
1e: e8 e8 16 fa fc call 0xfcfa170b
23: 48 8d 3d 21 f1 0f 0b lea 0xb0ff121(%rip),%rdi # 0xb0ff14b
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: eb 37 jmp 0x68
31: e8 d5 16 fa fc call 0xfcfa170b
36: 48 8d 3d 1e f1 0f 0b lea 0xb0ff11e(%rip),%rdi # 0xb0ff15b
3d: 67 addr32
3e: 48 rex.W
3f: 0f .byte 0xf
If the error is fixable, and the fix patch is correct overall,
the create a new fixed patch based on the provided one with the errors fixed.
If the error points to a fundamental issue with the approach in the patch,
then create a new patch from scratch.
Note: in both cases the source tree does not contain the patch yet
(so if you want to create a new fixed patch, you need to recreate it
in its entirety from scratch using the codeeditor tool).
Reply: The `refcount_t: addition on 0; use-after-free` crash in `usb_add_gadget()` is a secondary effect of a use-after-free on the endpoint requests (`dev->req` and `dev->eps[i].req`) caused by the previous patch.
### Root Cause of the Crash
In the previous patch, `gadget_unbind()` was modified to wait for `udc_usage` to drop to 0 before freeing `dev->req` and `dev->eps[i].req`. However, to avoid a deadlock with the UDC core, the patch dropped `udc_usage` while the ioctls were waiting for the I/O completion (`wait_for_completion_interruptible()`).
Because `udc_usage` was 0 while the requests were still queued in the UDC, `gadget_unbind()` would proceed and free the requests *while they were still in use by the UDC core*. When the UDC core later stopped the controller (which happens after `gadget_unbind()` returns), it accessed the freed requests to complete them. This use-after-free corrupted memory, which in this case happened to overwrite the `refcount` of the `struct device` embedded in the gadget, leading to the `refcount_t: addition on 0` crash when the gadget was re-registered.
### The Fix
To fix this properly without deadlocking, we must ensure that `gadget_unbind()` explicitly dequeues all pending requests before waiting for `udc_usage` to drop to 0.
1. **Hold `udc_usage` for the entire I/O duration:**
Instead of dropping `udc_usage` while waiting for completion, we hold it. This ensures `gadget_unbind()` will wait for the ioctls to fully finish.
2. **Explicitly dequeue requests in `gadget_unbind()`:**
To prevent the deadlock, `gadget_unbind()` must explicitly call `usb_ep_dequeue()` on all queued requests before waiting for `udc_usage`. This forces the UDC core to complete the requests immediately. The completion handlers will run, wake up the waiting ioctls, and the ioctls will decrement `udc_usage` and signal the completion.
3. **Handle interruptions correctly:**
If an ioctl is interrupted by a signal, it checks if `dev->state == STATE_DEV_FAILED` (which is set by `gadget_unbind()`). If so, it knows `gadget_unbind()` is handling the dequeue, so it just waits for the completion handler to finish without calling `usb_ep_dequeue()` itself.
4. **Proper cleanup in `gadget_bind()`:**
If `gadget_bind()` fails (e.g., event queue is full), it must clear `dev->gadget` and `dev->req` before freeing the request, as `gadget_unbind()` will not be called in this case.
I have applied these fixes to the patch.
|
| 286/4 |
2026/07/29 09:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 17798
output: 43
thoughts: 403
Thoughts: **Analyzing a KASAN Slab-Use-After-Free in Raw_Gadget.c**
Okay, so we've got a KASAN use-after-free in `raw_gadget.c` stemming from a race condition between `gadget_unbind()` and those pesky ioctls. The previous attempt at a fix, remember that one, introduced `udc_usage` and `unbind_completion`. Unfortunately, that patch created a `refcount_t: addition on 0; use-after-free` error in `usb_add_gadget()`. I need to figure out *why*.
Let's dive into the `gadget_unbind()` and `gadget_bind()` changes from that patch. Scanning the code...
```c
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
```
Wait a second⦠`usb_ep_free_request(gadget->ep0, req);` is *inside* the error path of `gadget_bind()`! And `req` is a local variable within `gadget_bind()`. But `dev->req`? What's going on with `dev->req`? I see the code is setting it to NULL too, in the error path, but why are they freeing a local variable? That doesn't make sense! Let's go back and examine `gadget_bind()` in `drivers/usb/gadget/legacy/raw_gadget.c` more closely and see what we can find.
|
| 287/4 |
2026/07/29 09:10 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:gadget_bind]
Results: map[SourceCode: 281: static int gadget_bind(struct usb_gadget *gadget,
282: struct usb_gadget_driver *driver)
283: {
284: int ret = 0, i = 0;
285: struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
286: struct usb_request *req;
287: struct usb_ep *ep;
288: unsigned long flags;
289:
290: if (strcmp(gadget->name, dev->udc_name) != 0)
291: return -ENODEV;
292:
293: set_gadget_data(gadget, dev);
294: req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
295: if (!req) {
296: dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
297: set_gadget_data(gadget, NULL);
298: return -ENOMEM;
299: }
300:
301: spin_lock_irqsave(&dev->lock, flags);
302: dev->req = req;
303: dev->req->context = dev;
304: dev->req->complete = gadget_ep0_complete;
305: dev->gadget = gadget;
306: gadget_for_each_ep(ep, dev->gadget) {
307: dev->eps[i].ep = ep;
308: dev->eps[i].addr = get_ep_addr(ep->name);
309: dev->eps[i].state = STATE_EP_DISABLED;
310: i++;
311: }
312: dev->eps_num = i;
313: spin_unlock_irqrestore(&dev->lock, flags);
314:
315: dev_dbg(&gadget->dev, "gadget connected\n");
316: ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
317: if (ret < 0) {
318: dev_err(&gadget->dev, "failed to queue connect event\n");
319: set_gadget_data(gadget, NULL);
320: return ret;
321: }
322:
323: /* Matches kref_put() in gadget_unbind(). */
324: kref_get(&dev->count);
325: return ret;
326: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 288/4 |
2026/07/29 09:10 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 19519
output: 43
thoughts: 1015
Thoughts: **Analyzing a USB Gadget Refcount Issue**
Okay, so I'm staring at this crash: `refcount_t: addition on 0; use-after-free` in `usb_add_gadget()`. My initial thought was it might be related to `kref_get(&dev->count)` in `gadget_bind()`, particularly if `raw_queue_event` was failing and skipping the kref increment. But then I realized this isn't about `dev->count`, it's about the device's main `kref`, right? This is in `usb_add_gadget() -> device_add() -> get_device() -> kobject_get() -> kref_get()`. So, the `kref_get()` failing with addition on 0 is on the *gadget's* `struct device`, not some internal counter.
The crash trace makes it clear: `device_add()` is where it's blowing up. And this happens *after* the initial device initialization in `usb_add_gadget()`. I'm going through the code. Why would the device's refcount be zero when `device_add()` is called? I'm getting a mental picture, since the code is pretty straightforward: It *should* be one at this point, set by `device_initialize()`.
Then I start thinking, maybe the `gadget_bind()` fails before, and there's some messy cleanup going on? Nope, `gadget_bind()` is called separately by `udc_bind_to_driver()`. I have to step back and think about the call stack again. `usb_add_gadget()` is called by `dummy_udc_probe()` and is supposed to be adding the UDC. So, `device_add()` in `usb_add_gadget()` is where the `struct device` is being added.
The problem, then, is that the `gadget->dev` refcount is 0 *when* `device_add()` is called. That's impossible if it has been properly initialized unless something else is happening. So the issue has to be that the gadget struct has been freed before device_add is called.
Let's dive back into `drivers/usb/gadget/udc/core.c` and trace the execution path from `usb_add_gadget()` to see what could be causing this. There must be some scenario where the device's lifecycle is being mangled, potentially related to error handling or race conditions. I need to scrutinize how the device is initialized and, more importantly, *when* it's being uninitialized or potentially freed. I'm going to follow the control flow closely, specifically looking at any error paths and potential race conditions in the context of the device's refcount.
|
| 289/4 |
2026/07/29 09:11 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_add_gadget]
Results: map[SourceCode:1403: /**
1404: * usb_add_gadget - adds a new gadget to the udc class driver list
1405: * @gadget: the gadget to be added to the list.
1406: *
1407: * Returns zero on success, negative errno otherwise.
1408: * Does not do a final usb_put_gadget() if an error occurs.
1409: */
1410: int usb_add_gadget(struct usb_gadget *gadget)
1411: {
1412: struct usb_udc *udc;
1413: int ret = -ENOMEM;
1414:
1415: udc = kzalloc_obj(*udc);
1416: if (!udc)
1417: goto error;
1418:
1419: device_initialize(&udc->dev);
1420: udc->dev.release = usb_udc_release;
1421: udc->dev.class = &udc_class;
1422: udc->dev.groups = usb_udc_attr_groups;
1423: udc->dev.parent = gadget->dev.parent;
1424: ret = dev_set_name(&udc->dev, "%s",
1425: kobject_name(&gadget->dev.parent->kobj));
1426: if (ret)
1427: goto err_put_udc;
1428:
1429: udc->gadget = gadget;
1430: gadget->udc = udc;
1431: mutex_init(&udc->connect_lock);
1432:
1433: udc->started = false;
1434: /*
1435: * Align decoupled lifecycles: take a UDC reference to ensure it
1436: * remains allocated until the gadget is released, requiring an
1437: * override of the gadget's release routine to drop it.
1438: */
1439: udc->gadget_release = gadget->dev.release;
1440: gadget->dev.release = usb_gadget_release;
1441: get_device(&udc->dev);
1442:
1443: mutex_lock(&udc_lock);
1444: list_add_tail(&udc->list, &udc_list);
1445: mutex_unlock(&udc_lock);
1446: INIT_WORK(&udc->vbus_work, vbus_event_work);
1447:
1448: ret = device_add(&udc->dev);
1449: if (ret)
1450: goto err_unlist_udc;
1451:
1452: usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
1453: udc->vbus = true;
1454:
1455: ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL);
1456: if (ret < 0)
1457: goto err_del_udc;
1458: gadget->id_number = ret;
1459: dev_set_name(&gadget->dev, "gadget.%d", ret);
1460:
1461: ret = device_add(&gadget->dev);
1462: if (ret)
1463: goto err_free_id;
1464:
1465: ret = sysfs_create_link(&udc->dev.kobj,
1466: &gadget->dev.kobj, "gadget");
1467: if (ret)
1468: goto err_del_gadget;
1469:
1470: return 0;
1471:
1472: err_del_gadget:
1473: device_del(&gadget->dev);
1474:
1475: err_free_id:
1476: ida_free(&gadget_id_numbers, gadget->id_number);
1477:
1478: err_del_udc:
1479: flush_work(&gadget->work);
1480: device_del(&udc->dev);
1481:
1482: err_unlist_udc:
1483: mutex_lock(&udc_lock);
1484: list_del(&udc->list);
1485: mutex_unlock(&udc_lock);
1486: /*
1487: * Revert the override and drop the UDC reference to prevent
1488: * leaking the UDC if the gadget was statically allocated.
1489: */
1490: gadget->dev.release = udc->gadget_release;
1491: put_device(&udc->dev);
1492:
1493: err_put_udc:
1494: put_device(&udc->dev);
1495:
1496: error:
1497: return ret;
1498: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 290/4 |
2026/07/29 09:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 22637
output: 46
thoughts: 1629
Thoughts: **Debugging a Use-After-Free in a USB Gadget Driver**
Okay, so `device_add()` is failing with a refcount of 0 for `gadget->dev`. This is a classic sign of a double-free or a use-after-free. Why is the refcount zero? Well, the UDC driver initializes this (`dummy_udc` in this case), so it *should* be valid. Let's trace the lifecycle.
Ah, a recent patch introduced `usb_ep_free_request(gadget->ep0, req)` in `gadget_bind()`. But `req` is *local* to `gadget_bind()`. Wait a second... in `gadget_bind()`, `req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);` occurs first. Then, if `raw_queue_event` fails, the patch *also* does:
```c
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->req = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
```
Okay, so `req` is *assigned* to `dev->req` *before* the check and possible free. The free *seems* correct there: `usb_ep_free_request(gadget->ep0, req);`. But wait, what about `dev_free()`? The previous patch *removed* some `dev->req` frees. So if `gadget_bind()` fails, `dev->req` is set to NULL and freed, but that's normal.
But, if `gadget_bind()` *succeeds* and then `gadget_unbind()` is called, what happens? Inside `gadget_unbind()`:
```c
for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state == STATE_EP_DISABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
}
if (dev->req) {
usb_ep_free_request(gadget->ep0, dev->req);
dev->req = NULL;
}
```
Okay, `usb_ep_free_request(gadget->ep0, dev->req)` *is* called in `gadget_unbind()`. `gadget_unbind()` is called when the gadget unbinds. So now, the device requests are freed, but what if `dev->req` was already freed? No, the code flow makes this impossible. Then `dev_free()` is called later and doesn't free `dev->req`!
But, what if `raw_ioctl_ep0_read()` is running concurrently? If `raw_ioctl_ep0_read()` is running, it might still be using `dev->req`. If `gadget_unbind()` frees `dev->req` while `raw_ioctl_ep0_read()` is using it, *that's* a use-after-free.
Wait a second, `gadget_unbind()` waits for `udc_usage` to drop to 0, which means no UDC calls are running. But, `raw_process_ep0_io()`:
```c
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
spin_unlock_irqrestore(&dev->lock, flags);
ret = wait_for_completion_interruptible(&dev->ep0_done);
```
While waiting for `dev->ep0_done`, `udc_usage` *is* 0! So `gadget_unbind()` *can* proceed and free `dev->req`! Then, when the `wait_for_completion_interruptible` returns, `raw
|
| 291/4 |
2026/07/29 09:11 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_udc_probe]
Results: map[SourceCode:1101: static int dummy_udc_probe(struct platform_device *pdev)
1102: {
1103: struct dummy *dum;
1104: int rc;
1105:
1106: dum = *((void **)dev_get_platdata(&pdev->dev));
1107: /* Clear usb_gadget region for new registration to udc-core */
1108: memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
1109: dum->gadget.name = gadget_name;
1110: dum->gadget.ops = &dummy_ops;
1111: if (mod_data.is_super_speed)
1112: dum->gadget.max_speed = USB_SPEED_SUPER;
1113: else if (mod_data.is_high_speed)
1114: dum->gadget.max_speed = USB_SPEED_HIGH;
1115: else
1116: dum->gadget.max_speed = USB_SPEED_FULL;
1117:
1118: dum->gadget.dev.parent = &pdev->dev;
1119: init_dummy_udc_hw(dum);
1120:
1121: rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget);
1122: if (rc < 0)
1123: goto err_udc;
1124:
1125: rc = device_create_file(&dum->gadget.dev, &dev_attr_function);
1126: if (rc < 0)
1127: goto err_dev;
1128: platform_set_drvdata(pdev, dum);
1129: return rc;
1130:
1131: err_dev:
1132: usb_del_gadget_udc(&dum->gadget);
1133: err_udc:
1134: return rc;
1135: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 292/4 |
2026/07/29 09:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25933
output: 48
thoughts: 143
Thoughts: **Analyzing the USB Gadget Initialization Flow**
Okay, let's break this down. First, it looks like `usb_add_gadget_udc()` functions as a kind of wrapper. I see it calls `usb_add_gadget()`, which is important to keep in mind. I need to understand the relationship between those two. Now, I see `dummy_udc_probe()` clears the `gadget` struct with `memzero_explicit()`. However, it doesn't initialize the underlying device structure with `device_initialize()`, which is immediately suspect. Then I'm reminded that `usb_add_gadget()` *does* call `device_initialize()` on the UDC's `dev` field. But what about the `gadget->dev` field itself? Something's missing there and that warrants further investigation. I think it's worth checking out `usb_add_gadget_udc_release()` to see if cleanup handles what might be missing during probe.
|
| 293/4 |
2026/07/29 09:11 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_add_gadget_udc_release]
Results: map[SourceCode:1501: /**
1502: * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
1503: * @parent: the parent device to this udc. Usually the controller driver's
1504: * device.
1505: * @gadget: the gadget to be added to the list.
1506: * @release: a gadget release function.
1507: *
1508: * Returns zero on success, negative errno otherwise.
1509: * Calls the gadget release function in the latter case.
1510: */
1511: int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
1512: void (*release)(struct device *dev))
1513: {
1514: int ret;
1515:
1516: usb_initialize_gadget(parent, gadget, release);
1517: ret = usb_add_gadget(gadget);
1518: if (ret)
1519: usb_put_gadget(gadget);
1520: return ret;
1521: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 294/4 |
2026/07/29 09:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26698
output: 45
thoughts: 663
Thoughts: **Debugging a USB Gadget Refcount Issue**
Okay, so I'm looking at this crash in `device_add()` and the error message "refcount_t: addition on 0". The call stack points to `usb_add_gadget()`, so that's where I need to focus. I see `usb_initialize_gadget()` calls `device_initialize(&gadget->dev)`, which sets the refcount to 1. Then we call `usb_add_gadget()`, which in turn calls `device_add(&gadget->dev)`. The crash happens *inside* `device_add()`, meaning the refcount must be zero *before* `device_add()` finishes its work, not after.
Now, `device_add()` calls `get_device(&gadget->dev)`. If the refcount is zero here, it means someone called `put_device()` on the gadget's device structure *before* `device_add()` could properly increment it through `get_device()`. Who the heck is calling `put_device()`?
I started to wonder if `dev_set_name()` could trigger something that calls `put_device()` but the crash trace doesn't go there. Let me re-examine the stack trace in more detail. I see the crash is in `kobject_get()`, called from `get_device()`, which means `dev->kobj.kref.refcount` is zero. But, `device_initialize()` sets it to one. What could possibly be decrementing it to zero before `device_add()`? It has to be another `put_device()` call, but where?
`usb_add_gadget()` calls `device_add()` for the UDC device (`udc->dev`) and then for the gadget device itself (`gadget->dev`). It's unlikely that adding the UDC device would somehow lead to premature release of the gadget device. But, what if adding a gadget driver and UDC device interact in some way?
Aha! We have `udc_bind_to_driver()` being called, especially if there's already a `raw_gadget` registered! Maybe that's it! If we have a gadget driver registered *before* the UDC is added, `udc_bind_to_driver()` could be called *at the wrong time*. This is an interesting hypothesis. Let's dig into `udc_bind_to_driver()` and see what's going on in there!
|
| 295/4 |
2026/07/29 09:11 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:udc_bind_to_driver]
Results: map[SourceCode: SourceFile:]
|
| 296/4 |
2026/07/29 09:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 27968
output: 22
thoughts: 17
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Navigating the USB Gadget Driver Landscape**
Right, let's dive into this. My initial thought is to zero in on a critical point of entry: the `usb_gadget_probe_driver()` function, or something analogous within the USB gadget subsystem. That's the logical starting point, isn't it? It's where the rubber meets the road, where the driver attempts to hook itself up to a newly connected or configured gadget. I'm thinking that function, or its equivalent in the specific architecture or gadget framework we're dealing with, will offer clues. Is it failing? If so, why? Are there error codes or logs that give hints about the mismatch between the driver and the hardware?
I'm mentally running through the typical probe sequence β the driver registration, the device matching, the resource allocation... I need to see if the device identification (VID/PID, perhaps other descriptors) is going as planned. Are we even *finding* the correct gadget? This is likely where the devil is hiding. Maybe there's a problem with the driver's device table, or the gadget's descriptors are messed up. It could also be a timing issue.
|
| 297/4 |
2026/07/29 09:11 |
tool |
grepper |
0m
Error:
Args: map[Expression:udc_bind_to_driver]
Results:
|
| 298/4 |
2026/07/29 09:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 28293
output: 43
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Gadget Driver Binding - A Quick Look**
Okay, so I see `gadget_bind_driver()`. That immediately signals a key point in the USB gadget driver lifecycle. We're dealing with the mechanism where a specific USB driver is being associated with a particular USB device, right? This is where the magic happens β the kernel figuring out which driver handles which device. I'm thinking about the arguments involved: the `gadget` struct, the driver struct, probably some device IDs to match... We're likely at the point where the device description is parsed, and a decision is being made if this driver is a good fit. I need to refresh my memory on the underlying data structures, but I know the general flow. Is this during probe, or is it a delayed bind? I should probably check the surrounding code for clues. Gotta make sure the resource allocation is happening correctly and that there aren't any potential race conditions here. Gotta think about error handling, too: what happens if the bind fails? Are we gracefully releasing resources, or potentially leaking something? Need to review the surrounding code to ensure it's robust.
|
| 299/4 |
2026/07/29 09:11 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:gadget_bind_driver]
Results: map[SourceCode:1641: static int gadget_bind_driver(struct device *dev)
1642: {
1643: struct usb_gadget *gadget = dev_to_usb_gadget(dev);
1644: struct usb_udc *udc = gadget->udc;
1645: struct usb_gadget_driver *driver = container_of(dev->driver,
1646: struct usb_gadget_driver, driver);
1647: int ret = 0;
1648:
1649: mutex_lock(&udc_lock);
1650: if (driver->is_bound) {
1651: mutex_unlock(&udc_lock);
1652: return -ENXIO; /* Driver binds to only one gadget */
1653: }
1654: driver->is_bound = true;
1655: udc->driver = driver;
1656: mutex_unlock(&udc_lock);
1657:
1658: dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function);
1659:
1660: usb_gadget_udc_set_speed(udc, driver->max_speed);
1661:
1662: ret = driver->bind(udc->gadget, driver);
1663: if (ret)
1664: goto err_bind;
1665:
1666: mutex_lock(&udc->connect_lock);
1667: ret = usb_gadget_udc_start_locked(udc);
1668: if (ret) {
1669: mutex_unlock(&udc->connect_lock);
1670: goto err_start;
1671: }
1672: usb_gadget_enable_async_callbacks(udc);
1673: udc->allow_connect = true;
1674: ret = usb_udc_connect_control_locked(udc);
1675: if (ret)
1676: goto err_connect_control;
1677:
1678: mutex_unlock(&udc->connect_lock);
1679:
1680: kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
1681: return 0;
1682:
1683: err_connect_control:
1684: udc->allow_connect = false;
1685: usb_gadget_disable_async_callbacks(udc);
1686: if (gadget->irq)
1687: synchronize_irq(gadget->irq);
1688: usb_gadget_udc_stop_locked(udc);
1689: mutex_unlock(&udc->connect_lock);
1690:
1691: err_start:
1692: driver->unbind(udc->gadget);
1693:
1694: err_bind:
1695: if (ret != -EISNAM)
1696: dev_err(&udc->dev, "failed to start %s: %d\n",
1697: driver->function, ret);
1698:
1699: mutex_lock(&udc_lock);
1700: udc->driver = NULL;
1701: driver->is_bound = false;
1702: mutex_unlock(&udc_lock);
1703:
1704: return ret;
1705: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 300/4 |
2026/07/29 09:11 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29608
output: 43
thoughts: 452
Thoughts: **Debugging a USB Gadget Driver Crash**
Okay, let me think this through. I see `gadget_bind_driver()` is called by `really_probe()`. The crash trace points to `device_driver_attach()` and `bind_store()`. Wait a minute, the crash is actually inside `usb_add_gadget()`, and that's called by `dummy_udc_probe()`. And `dummy_udc_probe()` is *also* called by `really_probe()`! So `really_probe()` is probing the `dummy_udc` driver. Makes sense, so `dummy_udc_probe()` is running.
Okay, so it calls `usb_add_gadget_udc_release()`, which then calls `usb_add_gadget()`. I'm starting to get the picture. Now, `usb_add_gadget()` calls `device_add(&udc->dev)`. When that happens, does it trigger anything odd? No, that should be fine, `udc->dev` gets added. Then, *after* that, `usb_add_gadget()` calls `device_add(&gadget->dev)`.
Wait, the crash is in that `device_add(&gadget->dev)` call? Let's confirm the line number in `usb_add_gadget()`. Okay, the line number is 1461, and that is indeed the call to `device_add(&gadget->dev)`. *That's* the culprit! The crash is happening in `device_add(&gadget->dev)` because it's hitting a refcount issue. So the crash is in the same function call as the first device add. Hmm, why is `gadget->dev`'s refcount zero? This seems like a release issue.
Okay, back to the code. I recall that `usb_add_gadget_udc_release()` also calls `usb_initialize_gadget()`. I need to review that function.
|
| 301/4 |
2026/07/29 09:12 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_initialize_gadget]
Results: map[SourceCode:1378: /**
1379: * usb_initialize_gadget - initialize a gadget and its embedded struct device
1380: * @parent: the parent device to this udc. Usually the controller driver's
1381: * device.
1382: * @gadget: the gadget to be initialized.
1383: * @release: a gadget release function.
1384: */
1385: void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
1386: void (*release)(struct device *dev))
1387: {
1388: spin_lock_init(&gadget->state_lock);
1389: gadget->teardown = false;
1390: INIT_WORK(&gadget->work, usb_gadget_state_work);
1391: gadget->dev.parent = parent;
1392:
1393: if (release)
1394: gadget->dev.release = release;
1395: else
1396: gadget->dev.release = usb_udc_nop_release;
1397:
1398: device_initialize(&gadget->dev);
1399: gadget->dev.bus = &gadget_bus_type;
1400: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 302/4 |
2026/07/29 09:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 30935
output: 45
thoughts: 418
Thoughts: **Analyzing the USB Gadget Initialization Flow**
Okay, so I'm looking at this initialization sequence, and it starts with `device_initialize(&gadget->dev)`. That sets the refcount to 1, as expected. Then, `usb_add_gadget()` is invoked. Now, inside `usb_add_gadget()`, my attention is drawn to a few lines. Specifically, it's making a few modifications, setting `udc->gadget_release` to point to the gadget's release function and then changing the gadget release function to `usb_gadget_release`. A get device is performed. Then there's the standard mutex lock and list addition.
But hold on, it looks like `list_add_tail(&udc->list, &udc_list);` is called **before** `device_add(&udc->dev)`. That's the key. My mind immediately jumps to a concurrency issue. Could another thread potentially see this UDC in `udc_list` before it's fully initialized, and crucially, before its driver is attached? Specifically, bind a gadget driver to it.
The function `usb_gadget_probe_driver()` springs to mind. I remember that one iterates through `udc_list` looking for a UDC to bind to. If a driver probe happens concurrently, the fact that the UDC is added to the list *before* `device_add()` is called presents a race condition. The driver could be bound to the UDC before it's fully ready. I'll need to confirm if my suspicions are correct and take a closer look at `usb_gadget_probe_driver()` to be sure.
|
| 303/4 |
2026/07/29 09:12 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_gadget_probe_driver]
Results: map[SourceCode: SourceFile:]
|
| 304/4 |
2026/07/29 09:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 31782
output: 47
thoughts: 18
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience and presented in the first person:
**Navigating the USB Gadget Driver Register Function**
Alright, let's see... the code's pointing me at the function responsible for registering a gadget driver, right? Hold on a second, that's not quite right. It looks like it's specifically `usb_gadget_register_driver_owner()` that's the key function I need to focus on. Not just the generic registration, but the one with the owner context built in. Makes sense - thinking through the implications of this one, the details of the driver's permissions for the device are handled at the registration level. Right, so now I need to dive into its parameters and implications.
|
| 305/4 |
2026/07/29 09:12 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_gadget_register_driver_owner]
Results: map[SourceCode:1740: int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
1741: struct module *owner, const char *mod_name)
1742: {
1743: int ret;
1744:
1745: if (!driver || !driver->bind || !driver->setup)
1746: return -EINVAL;
1747:
1748: driver->driver.bus = &gadget_bus_type;
1749: driver->driver.owner = owner;
1750: driver->driver.mod_name = mod_name;
1751: driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
1752: ret = driver_register(&driver->driver);
1753: if (ret) {
1754: pr_warn("%s: driver registration failed: %d\n",
1755: driver->function, ret);
1756: return ret;
1757: }
1758:
1759: mutex_lock(&udc_lock);
1760: if (!driver->is_bound) {
1761: if (driver->match_existing_only) {
1762: pr_warn("%s: couldn't find an available UDC or it's busy\n",
1763: driver->function);
1764: ret = -EBUSY;
1765: } else {
1766: pr_info("%s: couldn't find an available UDC\n",
1767: driver->function);
1768: ret = 0;
1769: }
1770: }
1771: mutex_unlock(&udc_lock);
1772:
1773: if (ret)
1774: driver_unregister(&driver->driver);
1775: return ret;
1776: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 306/4 |
2026/07/29 09:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 32573
output: 45
thoughts: 185
Thoughts: **My Analysis of the USB Gadget Driver Registration**
Okay, let me break this down. It seems I was initially a bit off track. I was tracing the registration process, and I thought I understood it, but let me re-evaluate. I'm going through this in my head again: `driver_register()` calls `bus_add_driver()`, which in turn calls `driver_attach()`. This ultimately leads to `bus_for_each_dev()`, right?
Right. Now, the devices on `gadget_bus_type` are represented by `gadget->dev`. But, and this is the crux of it, `gadget->dev` is *added* to the bus only when `usb_add_gadget()` is called, specifically:
```c
1461: ret = device_add(&gadget->dev);
```
Aha! So, `gadget->dev` isn't actually *on* the bus until that `device_add()` call executes. This presents a problem. If `gadget->dev` isn't on the bus yet, then `driver_attach()` will completely miss it. The driver won't see the device and won't be able to bind to it!
I'm probably missing something. Hold on... I remember seeing `udc_list` somewhere. I need to go back and check where that's used. Maybe there's some sort of deferred handling or a pending driver list I haven't accounted for. Okay, let's look at `check_pending_gadget_drivers()`.
|
| 307/4 |
2026/07/29 09:12 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:check_pending_gadget_drivers]
Results: map[SourceCode: SourceFile:]
|
| 308/4 |
2026/07/29 09:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 33168
output: 22
thoughts: 16
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Re-examining `udc_bind_to_driver()`**
Okay, time to circle back to `udc_bind_to_driver()`. I've been through this before, but it's crucial to ensure we haven't missed anything, or perhaps that some assumptions made previously were incorrect. Given the issues we're seeing with [insert relevant issue/context based on your field here - e.g., device enumeration, power management, data transfer rates], I need to meticulously review this function again. Let's start by⦠[describe your thought process on how you'll approach the review, e.g., "tracing the execution path with this specific scenario", "confirming the register settings are as expected", or "reviewing the locking mechanisms here"]. I'll be looking specifically at⦠[mention key areas of concern - e.g., resource allocation, error handling, device probe sequence]. Gotta make sure it's interacting with the driver correctly based on the current context. Let's see if this fresh look reveals anything new.
|
| 309/4 |
2026/07/29 09:12 |
tool |
grepper |
0m
Error:
Args: map[Expression:udc_bind_to_driver]
Results:
|
| 310/4 |
2026/07/29 09:12 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 33460
output: 43
thoughts: 240
Thoughts: **Tracing the Probe Path**
Okay, let's see... I'm looking at this crash dump, and it's pointing to `gadget_bind_driver()` being involved. Hmm, it looks like `really_probe()` is the one that calls it. Let's trace that a bit further... `really_probe+0x254/0xae0` ...and the call stack shows `__driver_probe_device+0x1e8/0x360`, then `device_driver_attach+0xe0/0x1d0`, and finally `bind_store+0x1d3/0x220`. Ah ha! The reproducer's writing to `/sys/bus/gadget/drivers/raw-gadget/bind`. That's the key! `bind_store()` is being called.
`bind_store()` in turn calls `device_driver_attach()`. And that function calls `__driver_probe_device()` which then calls `really_probe()`. That's where the magic happens. Now, if I remember correctly, `really_probe()` then calls the bus probe function. So, is that `gadget_probe()`? Gotta verify that. Let's just double check the `gadget_bus_type` structure to be sure.
|
| 311/4 |
2026/07/29 09:12 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:gadget_bus_type]
Results: map[SourceCode:1951: static const struct bus_type gadget_bus_type = {
1952: .name = "gadget",
1953: .probe = gadget_bind_driver,
1954: .remove = gadget_unbind_driver,
1955: .match = gadget_match_driver,
1956: };
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 312/4 |
2026/07/29 09:12 |
llm |
patch-generator |
1m
Model:
Tokens:
input: 34157
output: 46
thoughts: 2502
Thoughts: **Debugging a USB Gadget Driver Refcount Issue**
Okay, so I'm diving into this crash in `device_add()` when dealing with the USB gadget driver. The probe function is `gadget_bind_driver()`, but there's this `bind_store()` function that's taking a device name, looking up "gadget.0" on the bus. This device name is only added after the call to `device_add(&gadget->dev)`, so the order of operations seems important. If `device_add` is running simultaneously with `bind_store()`, there might be a race.
`device_add()` adds the device to the bus via `bus_add_device()`, and this suggests that once added, `bind_store()` can find it. But the crash log from `device_add()` is happening right at the start, inside `get_device(dev)`, with the refcount of `dev->kobj.kref.refcount` equal to 0. It's indicating that `dev` is already freed or its refcount is down to zero!
I see that `usb_initialize_gadget()` is called before `device_add()` and is followed by `usb_add_gadget()`. I need to figure out how the refcount gets dropped between these calls.
Looking at `usb_add_gadget()`, it initializes a UDC device (`udc`) and then calls `device_initialize(&udc->dev)`. Then, `device_add(&udc->dev)` is called, followed by the second call to `device_add(&gadget->dev)`. Could `device_add(&udc->dev)` trigger something that messes with the refcount of `gadget->dev`? If `device_add(&udc->dev)` generates a uevent, could userspace action be the culprit? But, no, `gadget->dev` isn't yet exposed to userspace.
If `device_add(&udc->dev)` fails, it jumps to `err_unlist_udc`, which calls `usb_put_gadget()` and releases `udc`. But the crash is in the second call to `device_add(&gadget->dev)`, so this can't be it. The refcount HAS to be 0!
Now, the crash trace is crucial: two stacks are shown. One is a refcount addition on zero, a use-after-free, in the sequence `kobject_get` -> `get_device` -> `device_add` -> `usb_add_gadget`. The other stack shows `bind_store` calling `device_driver_attach()`, which is linked to `dummy_udc_probe()` being called from `bind_store()`. This is because I am writing to `/sys/bus/platform/drivers/dummy_udc/bind`, which is triggering this probe.
`dummy_udc_probe()` is called, and it initializes a `dum` variable and then calls `usb_add_gadget_udc()`. Wait a minute, where is `dum` allocated? It's using `dev_get_platdata()` to get `dum`! Now, I see... let's check `dummy_udc_probe()` again.
|
| 313/4 |
2026/07/29 09:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_udc_probe]
Results: map[SourceCode:1101: static int dummy_udc_probe(struct platform_device *pdev)
1102: {
1103: struct dummy *dum;
1104: int rc;
1105:
1106: dum = *((void **)dev_get_platdata(&pdev->dev));
1107: /* Clear usb_gadget region for new registration to udc-core */
1108: memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
1109: dum->gadget.name = gadget_name;
1110: dum->gadget.ops = &dummy_ops;
1111: if (mod_data.is_super_speed)
1112: dum->gadget.max_speed = USB_SPEED_SUPER;
1113: else if (mod_data.is_high_speed)
1114: dum->gadget.max_speed = USB_SPEED_HIGH;
1115: else
1116: dum->gadget.max_speed = USB_SPEED_FULL;
1117:
1118: dum->gadget.dev.parent = &pdev->dev;
1119: init_dummy_udc_hw(dum);
1120:
1121: rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget);
1122: if (rc < 0)
1123: goto err_udc;
1124:
1125: rc = device_create_file(&dum->gadget.dev, &dev_attr_function);
1126: if (rc < 0)
1127: goto err_dev;
1128: platform_set_drvdata(pdev, dum);
1129: return rc;
1130:
1131: err_dev:
1132: usb_del_gadget_udc(&dum->gadget);
1133: err_udc:
1134: return rc;
1135: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 314/4 |
2026/07/29 09:13 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 38010
output: 46
thoughts: 371
Thoughts: **Debugging a Refcount Mystery**
Okay, so I'm digging into this code and something's not adding up. First, I need to make sure I understand where `dum` is coming from. It looks like it's a global or something allocated elsewhere, then cast and dereferenced. It's initializing `dum->gadget` with `memzero_explicit()`. Hmm, if that gadget was registered and unregistered before, its `dev` member *could* still have references floating around. Clearing `dum->gadget` with `memzero_explicit()` also clears `dum->gadget.dev`!
But, `device_initialize()` should re-initialize that. That's the key. If `device_initialize()` is doing its job, the refcount should be back to 1. Right? That's how those things usually work. And then I hit the problem: Why in the world is `device_add()` seeing a refcount of 0? That makes no sense!
Aha! `device_initialize()` uses `kobject_init()`. Okay, let me go look at `kobject_init()` itself⦠Yep, `kref_init()` sets the refcount to 1. So, `gadget->dev.kobj.kref.refcount` *must* be 1 after `device_initialize()`. I'm even more lost now - how can it be zero when `device_add()` is called?
Wait⦠is there a release function in play? Ah, this is critical...`usb_add_gadget_udc()` calls `usb_add_gadget_udc_release()`, right? No! `dummy_udc_probe()` calls `usb_add_gadget_udc()` itself, NOT the release function! Ok, so it's not being prematurely released. That's a good data point. Now, let's go back and carefully scrutinize `usb_add_gadget_udc()` to find the problem.
|
| 315/4 |
2026/07/29 09:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_add_gadget_udc]
Results: map[SourceCode:1554: /**
1555: * usb_add_gadget_udc - adds a new gadget to the udc class driver list
1556: * @parent: the parent device to this udc. Usually the controller
1557: * driver's device.
1558: * @gadget: the gadget to be added to the list
1559: *
1560: * Returns zero on success, negative errno otherwise.
1561: */
1562: int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
1563: {
1564: return usb_add_gadget_udc_release(parent, gadget, NULL);
1565: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 316/4 |
2026/07/29 09:14 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 39103
output: 22
thoughts: 686
Thoughts: **Analyzing USB Gadget Registration and Potential Race Conditions**
Okay, so I'm looking at this code path, and it starts with `usb_add_gadget_udc_release(parent, gadget, NULL)`. That function then calls `usb_initialize_gadget()`, which in turn calls `device_initialize(&gadget->dev)`. So, `gadget->dev` is initialized, and its refcount is at 1, that makes sense. Then `usb_add_gadget()` is invoked.
Inside `usb_add_gadget()`, I see some interesting things happening. It sets `udc->gadget_release` to `gadget->dev.release`, and then *replaces* `gadget->dev.release` with `usb_gadget_release`. A get_device() is called on the UDC, `mutex_lock(&udc_lock)` is triggered, and the UDC is added to a list. Then, it initializes a work item, and calls `device_add(&udc->dev)`. It proceeds to set the gadget state, set vbus true, allocate an ID, set the gadget name, and finally, it calls `device_add(&gadget->dev)`.
My initial thought was, could `dev_set_name(&gadget->dev, "gadget.%d", ret)` fail and decrement the refcount? No, that's not how `dev_set_name()` works. It doesn't affect the refcount. Then I considered, what if that first `device_add(&udc->dev)` triggered `gadget_bind_driver()`? But no, `udc->dev` is on `udc_class`, not a gadget-specific class, so that shouldn't happen.
However, the line `list_add_tail(&udc->list, &udc_list);` makes the UDC available. That's the critical point. If `raw_gadget` has already registered, and it's waiting for a UDC, it *could* be bound right here. That's when I recall `raw_gadget` registers a gadget driver.
My concern is that when a new UDC is added to `udc_list`, does the UDC core automatically bind any pending gadget drivers? Is there a race condition? I need to check `check_pending_gadget_drivers()` to be sure. This is where the vulnerability may be.
|
| 317/4 |
2026/07/29 09:14 |
tool |
grepper |
0m
Error:
Args: map[Expression:check_pending_gadget_drivers]
Results:
|
| 318/4 |
2026/07/29 09:14 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40365
output: 24
thoughts: 65
Thoughts: Okay, let's dive into this USB gadget driver probe issue. First, I need to revisit `usb_add_gadget()`. I need to be absolutely certain I understand its complete execution flow. Does it explicitly handle the driver binding process? My initial hunch is that it might not be the primary mechanism.
Then, there's `device_add(&udc->dev)`. That certainly looks like it triggers a uevent. But the critical question is, does this uevent *directly* lead to driver binding in the way I'm expecting? I need to be precise here; uevents can do a lot, but I can't assume they handle every aspect of driver attachment automatically.
Finally, I think the critical function I need to pinpoint is something like `usb_gadget_probe_driver()`. That seems like the most likely place where the actual driver probing and binding would occur. I need to examine its implementation closely.
|
| 319/4 |
2026/07/29 09:14 |
tool |
grepper |
0m
Args: map[Expression:list_add_tail.*udc_list]
Results: map[Output:drivers/usb/gadget/udc/core.c=1410=int usb_add_gadget(struct usb_gadget *gadget)
--
drivers/usb/gadget/udc/core.c-1443- mutex_lock(&udc_lock);
drivers/usb/gadget/udc/core.c:1444: list_add_tail(&udc->list, &udc_list);
drivers/usb/gadget/udc/core.c-1445- mutex_unlock(&udc_lock);
]
|
| 320/4 |
2026/07/29 09:14 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40800
output: 34
thoughts: 963
Thoughts: **Debugging a `device_add` Crash in USB Gadget Driver Initialization**
Okay, let's break this down. I'm seeing a crash inside `device_add()` when adding a USB gadget device, which is called right after adding the UDC (`usb_add_gadget()`). The sequence is: `list_add_tail()`, then `device_add(&udc->dev)`, then `device_add(&gadget->dev)`. I initially thought the driver registration and device attachment were the problem, but it looks like the `driver_attach()` path is fine.
The crash happens within `device_add(&gadget->dev)`. I was concerned about a race condition where the gadget device wasn't yet fully initialized when the driver was attaching, and that the `gadget->dev` was not visible on the correct bus, but I can rule this out.
The code for `device_add()` is simple enough. It calls `get_device(dev)` at the very beginning. So, if we crash in `get_device()`, the `dev` is being passed to the function with a zero refcount. Why would the refcount be zero? The crash within `get_device()` is specifically "refcount_t: addition on 0".
I ruled out `dev_set_name()` and `ida_alloc()` as causes for dropping the refcount. I was even trying to figure out if we were accidentally passing the wrong `dev` to `get_device()`. But no, `device_add(&gadget->dev)` *should* be passing the right device.
Wait a second. `device_add()` also calls `get_device(dev->parent)`. Could the parent's refcount be the problem? In this case, the `gadget->dev.parent` is set to `&pdev->dev`, which is the platform device's device. So the question is: is `pdev->dev`'s refcount zero? Well, `dummy_udc_probe()` has to run before the error happens, so that would mean the probe code, and thus the platform device, is valid. So that cannot be it. Also `usb_initialize_gadget()` also sets the `gadget->dev.parent`, and a debug function is used where the parent is always a good reference.
Back to the trace: the crash *is* indeed in `device_add(&gadget->dev)` which is line 1461. Looks like the crash is in the call to `device_add()` itself, not within the `device_add()` function. This is in `drivers/base/core.c`, so I'll need to go look at the implementation of `device_add()` and find out why this `get_device()` call is failing on `gadget->dev`. I'll go see what is going on with the `dev` passed to the function.
|
| 321/4 |
2026/07/29 09:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:device_add]
Results: map[SourceCode:3612: /**
3613: * device_add - add device to device hierarchy.
3614: * @dev: device.
3615: *
3616: * This is part 2 of device_register(), though may be called
3617: * separately _iff_ device_initialize() has been called separately.
3618: *
3619: * This adds @dev to the kobject hierarchy via kobject_add(), adds it
3620: * to the global and sibling lists for the device, then
3621: * adds it to the other relevant subsystems of the driver model.
3622: *
3623: * Do not call this routine or device_register() more than once for
3624: * any device structure. The driver model core is not designed to work
3625: * with devices that get unregistered and then spring back to life.
3626: * (Among other things, it's very hard to guarantee that all references
3627: * to the previous incarnation of @dev have been dropped.) Allocate
3628: * and register a fresh new struct device instead.
3629: *
3630: * NOTE: _Never_ directly free @dev after calling this function, even
3631: * if it returned an error! Always use put_device() to give up your
3632: * reference instead.
3633: *
3634: * Rule of thumb is: if device_add() succeeds, you should call
3635: * device_del() when you want to get rid of it. If device_add() has
3636: * *not* succeeded, use *only* put_device() to drop the reference
3637: * count.
3638: */
3639: int device_add(struct device *dev)
3640: {
3641: struct subsys_private *sp;
3642: struct device *parent;
3643: struct kobject *kobj;
3644: struct class_interface *class_intf;
3645: int error = -EINVAL;
3646: struct kobject *glue_dir = NULL;
3647:
3648: dev = get_device(dev);
3649: if (!dev)
3650: goto done;
3651:
3652: if (!dev->p) {
3653: error = device_private_init(dev);
3654: if (error)
3655: goto done;
3656: }
3657:
3658: /*
3659: * for statically allocated devices, which should all be converted
3660: * some day, we need to initialize the name. We prevent reading back
3661: * the name, and force the use of dev_name()
3662: */
3663: if (dev->init_name) {
3664: error = dev_set_name(dev, "%s", dev->init_name);
3665: dev->init_name = NULL;
3666: }
3667:
3668: if (dev_name(dev))
3669: error = 0;
3670: /* subsystems can specify simple device enumeration */
3671: else if (dev->bus && dev->bus->dev_name)
3672: error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
3673: else
3674: error = -EINVAL;
3675: if (error)
3676: goto name_error;
3677:
3678: pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
3679:
3680: parent = get_device(dev->parent);
3681: kobj = get_device_parent(dev, parent);
3682: if (IS_ERR(kobj)) {
3683: error = PTR_ERR(kobj);
3684: goto parent_error;
3685: }
3686: if (kobj)
3687: dev->kobj.parent = kobj;
3688:
3689: /* use parent numa_node */
3690: if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
3691: set_dev_node(dev, dev_to_node(parent));
3692:
3693: /* first, register with generic layer. */
3694: /* we require the name to be set before, and pass NULL */
3695: error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
3696: if (error) {
3697: glue_dir = kobj;
3698: goto Error;
3699: }
3700:
3701: /* notify platform of device entry */
3702: device_platform_notify(dev);
3703:
3704: error = device_create_file(dev, &dev_attr_uevent);
3705: if (error)
3706: goto attrError;
3707:
3708: error = device_add_class_symlinks(dev);
3709: if (error)
3710: goto SymlinkError;
3711: error = device_add_attrs(dev);
3712: if (error)
3713: goto AttrsError;
3714: error = bus_add_device(dev);
3715: if (error)
3716: goto BusError;
3717: error = dpm_sysfs_add(dev);
3718: if (error)
3719: goto DPMError;
3720: device_pm_add(dev);
3721:
3722: if (MAJOR(dev->devt)) {
3723: error = device_create_file(dev, &dev_attr_dev);
3724: if (error)
3725: goto DevAttrError;
3726:
3727: error = device_create_sys_dev_entry(dev);
3728: if (error)
3729: goto SysEntryError;
3730:
3731: devtmpfs_create_node(dev);
3732: }
3733:
3734: /* Notify clients of device addition. This call must come
3735: * after dpm_sysfs_add() and before kobject_uevent().
3736: */
3737: bus_notify(dev, BUS_NOTIFY_ADD_DEVICE);
3738: kobject_uevent(&dev->kobj, KOBJ_ADD);
3739:
3740: /*
3741: * Check if any of the other devices (consumers) have been waiting for
3742: * this device (supplier) to be added so that they can create a device
3743: * link to it.
3744: *
3745: * This needs to happen after device_pm_add() because device_link_add()
3746: * requires the supplier be registered before it's called.
3747: *
3748: * But this also needs to happen before bus_probe_device() to make sure
3749: * waiting consumers can link to it before the driver is bound to the
3750: * device and the driver sync_state callback is called for this device.
3751: */
3752: if (dev->fwnode && !dev->fwnode->dev) {
3753: dev->fwnode->dev = dev;
3754: fw_devlink_link_device(dev);
3755: }
3756:
3757: /*
3758: * The moment the device was linked into the bus's "klist_devices" in
3759: * bus_add_device() then it's possible that probe could have been
3760: * attempted in a different thread via userspace loading a driver
3761: * matching the device. "ready_to_probe" being unset would have
3762: * blocked those attempts. Now that all of the above initialization has
3763: * happened, unblock probe. If probe happens through another thread
3764: * after this point but before bus_probe_device() runs then it's fine.
3765: * bus_probe_device() -> device_initial_probe() -> __device_attach()
3766: * will notice (under device_lock) that the device is already bound.
3767: */
3768: device_lock(dev);
3769: dev_set_ready_to_probe(dev);
3770: device_unlock(dev);
3771:
3772: bus_probe_device(dev);
3773:
3774: /*
3775: * If all driver registration is done and a newly added device doesn't
3776: * match with any driver, don't block its consumers from probing in
3777: * case the consumer device is able to operate without this supplier.
3778: */
3779: if (dev->fwnode && fw_devlink_drv_reg_done && !dev_can_match(dev))
3780: fw_devlink_unblock_consumers(dev);
3781:
3782: if (parent)
3783: klist_add_tail(&dev->p->knode_parent,
3784: &parent->p->klist_children);
3785:
3786: sp = class_to_subsys(dev->class);
3787: if (sp) {
3788: mutex_lock(&sp->mutex);
3789: /* tie the class to the device */
3790: klist_add_tail(&dev->p->knode_class, &sp->klist_devices);
3791:
3792: /* notify any interfaces that the device is here */
3793: list_for_each_entry(class_intf, &sp->interfaces, node)
3794: if (class_intf->add_dev)
3795: class_intf->add_dev(dev);
3796: mutex_unlock(&sp->mutex);
3797: subsys_put(sp);
3798: }
3799: done:
3800: put_device(dev);
3801: return error;
3802: SysEntryError:
3803: if (MAJOR(dev->devt))
3804: device_remove_file(dev, &dev_attr_dev);
3805: DevAttrError:
3806: device_pm_remove(dev);
3807: dpm_sysfs_remove(dev);
3808: DPMError:
3809: device_set_driver(dev, NULL);
3810: bus_remove_device(dev);
3811: BusError:
3812: device_remove_attrs(dev);
3813: AttrsError:
3814: device_remove_class_symlinks(dev);
3815: SymlinkError:
3816: device_remove_file(dev, &dev_attr_uevent);
3817: attrError:
3818: device_platform_notify_remove(dev);
3819: kobject_uevent(&dev->kobj, KOBJ_REMOVE);
3820: glue_dir = get_glue_dir(dev);
3821: kobject_del(&dev->kobj);
3822: Error:
3823: cleanup_glue_dir(dev, glue_dir);
3824: parent_error:
3825: put_device(parent);
3826: name_error:
3827: kfree(dev->p);
3828: dev->p = NULL;
3829: goto done;
3830: }
SourceFile:drivers/base/core.c]
|
| 322/4 |
2026/07/29 09:14 |
llm |
patch-generator |
1m
Model:
Tokens:
input: 45771
output: 46
thoughts: 1659
Thoughts: **Debugging a Refcount Issue in USB Gadget Device Addition**
Okay, let's break this down. The core problem is a crash during `device_add()`, specifically with a device that has a refcount of 0. My initial thought is to trace the execution and see where this refcount could be going awry. I see that `device_add()` calls `get_device()` which in turn uses `kobject_get()`, which then uses `kref_get()`, all of which will warn if the refcount is already zero, as is happening here.
The obvious question is: why is the refcount zero? It *should* be 1 after `device_initialize()` is called. That means something has called `put_device()` to decrement it to 0. We need to identify that.
Looking at `usb_add_gadget_udc_release()`, there's a potential path involving `usb_add_gadget()` failing and `usb_put_gadget()` being called. But the crash is *inside* `usb_add_gadget()`, so that's not it. Then I investigate `device_add(&udc->dev)` failing within `usb_add_gadget()`, and the subsequent `goto err_unlist_udc` path. But that's not it, because that path calls `put_device()` *after* the `device_add` attempt. It looks like `device_add(&udc->dev)` is successful.
The crash happens when `device_add(&gadget->dev)` is called, so I need to understand why this `gadget->dev` could have a refcount of 0. I examine the code around the second `device_add()` and see that `usb_add_gadget()` has some suspicious lines:
`udc->gadget_release = gadget->dev.release;`
`gadget->dev.release = usb_gadget_release;`
`get_device(&udc->dev);`
`get_device(&udc->dev)` is called, and *then* `device_add(&udc->dev)` is called. I need to be sure the first `device_add()` is successful.
Could adding `udc->dev` trigger a bind? I don't see anything obvious with a probe function. I know `list_add_tail(&udc->list, &udc_list)` is called *before* `device_add(&udc->dev)`. I think, could another thread find it?
I now consider the possibility that the problem isn't the `gadget->dev` itself, but the *parent* device used in the `get_device()` call. Specifically, `get_device(dev->parent)`. For `gadget->dev`, the parent is `&pdev->dev` (the dummy UDC platform device). The probe for `dummy_udc_probe()` is running so this device is good. For `udc->dev`, the parent is also `&pdev->dev`.
Let's check the line numbers of `device_add()` within `usb_add_gadget()` to see where the problem is. The line number matches the call to `device_add(&gadget->dev)`. So, the `gadget->dev` *does* have a refcount of 0.
Wait! I see something suspicious in `dummy_udc_probe()`:
`dum = *((void **)dev_get_platdata(&pdev->dev));`
`memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));`
If `dum->gadget` was previously registered and unregistered, `gadget->dev` could have been freed. I need to verify how `dum` is allocated. I need to track down where `dum` is allocated to confirm if this is the cause.
|
| 323/4 |
2026/07/29 09:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_hcd_probe]
Results: map[SourceCode:2701: static int dummy_hcd_probe(struct platform_device *pdev)
2702: {
2703: struct dummy *dum;
2704: struct usb_hcd *hs_hcd;
2705: struct usb_hcd *ss_hcd;
2706: int retval;
2707:
2708: dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
2709: dum = *((void **)dev_get_platdata(&pdev->dev));
2710:
2711: if (mod_data.is_super_speed)
2712: dummy_hcd.flags = HCD_USB3 | HCD_SHARED;
2713: else if (mod_data.is_high_speed)
2714: dummy_hcd.flags = HCD_USB2;
2715: else
2716: dummy_hcd.flags = HCD_USB11;
2717: hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
2718: if (!hs_hcd)
2719: return -ENOMEM;
2720: hs_hcd->has_tt = 1;
2721:
2722: retval = usb_add_hcd(hs_hcd, 0, 0);
2723: if (retval)
2724: goto put_usb2_hcd;
2725:
2726: if (mod_data.is_super_speed) {
2727: ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev,
2728: dev_name(&pdev->dev), hs_hcd);
2729: if (!ss_hcd) {
2730: retval = -ENOMEM;
2731: goto dealloc_usb2_hcd;
2732: }
2733:
2734: retval = usb_add_hcd(ss_hcd, 0, 0);
2735: if (retval)
2736: goto put_usb3_hcd;
2737: }
2738: return 0;
2739:
2740: put_usb3_hcd:
2741: usb_put_hcd(ss_hcd);
2742: dealloc_usb2_hcd:
2743: usb_remove_hcd(hs_hcd);
2744: put_usb2_hcd:
2745: usb_put_hcd(hs_hcd);
2746: dum->hs_hcd = dum->ss_hcd = NULL;
2747: return retval;
2748: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 324/4 |
2026/07/29 09:15 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 49149
output: 20
thoughts: 28
Thoughts: Okay, here's my interpretation of those thoughts, presented as a first-person expert summary:
**Digging into Device Allocation**
Alright, let me just parse this quickly. Hmm, so `dummy_udc` and `dummy_hcd` are flagged as *platform* devices, which immediately sparks a question in my mind. The terminology here tells me they represent specific hardware components, likely related to USB or something similar, and are managed by the platform's device driver architecture. But here's the kicker: I need to pinpoint where the memory or resources for 'dum' are being allocated. The name `dum` is vague and doesn't explicitly link to any known allocation routine within this context. I'll need to trace its usage to understand its resource footprint. Is it statically allocated, dynamically allocated, or perhaps a pointer to a region managed elsewhere? I need to clarify this to understand the full picture of the device's interaction with the system. That detail is key to understanding the allocation flow.
|
| 325/4 |
2026/07/29 09:15 |
tool |
grepper |
0m
Args: map[Expression:kzalloc.*sizeof.*dummy]
Results: map[Output:Documentation/driver-api/virtio/writing_virtio_drivers.rst=25=like this::
--
Documentation/driver-api/virtio/writing_virtio_drivers.rst-52- /* initialize device data */
Documentation/driver-api/virtio/writing_virtio_drivers.rst:53: dev = kzalloc(sizeof(struct virtio_dummy_dev), GFP_KERNEL);
Documentation/driver-api/virtio/writing_virtio_drivers.rst-54- if (!dev)
--
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c=20=struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
--
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c-28-
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c:29: dummy_crtc = drmm_kzalloc(drm, sizeof(*dummy_crtc), GFP_KERNEL);
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c-30- KUNIT_ASSERT_NOT_NULL(test, dummy_crtc);
--
drivers/gpu/drm/vc4/tests/vc4_mock_output.c=23=struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
--
drivers/gpu/drm/vc4/tests/vc4_mock_output.c-34-
drivers/gpu/drm/vc4/tests/vc4_mock_output.c:35: dummy_output = drmm_kzalloc(drm, sizeof(*dummy_output), GFP_KERNEL);
drivers/gpu/drm/vc4/tests/vc4_mock_output.c-36- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
]
|
| 326/4 |
2026/07/29 09:15 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 49876
output: 21
thoughts: 19
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own:
**Investigating `struct dummy` Allocation in `dummy_hcd.c`**
Alright, time to dig into the `dummy_hcd.c` codebase. My immediate goal is to understand how the `struct dummy` is allocated. I need to trace the allocation path within the dummy HCD driver. Specifically, I want to know: is it statically allocated? Dynamically? Where are the instances created, and under what conditions?
I'm thinking I'll start with a straightforward text search within the file itself. Searching for something as specific as `struct dummy` should give me relevant hits. I want to see how that struct is declared or defined as well as any relevant allocation calls. Hopefully, it's not too convoluted; the simpler the implementation, the better for understanding its purpose and potential performance bottlenecks. I need to rapidly identify where memory is being reserved and initialized. Knowing the allocation method dictates how I'll proceed in debugging if there are allocation issues. I expect to be able to pinpoint its role in the driver's function.
|
| 327/4 |
2026/07/29 09:16 |
tool |
grepper |
0m
Error:
Args: map[Expression:kzalloc.*sizeof.*struct dummy]
Results:
|
| 328/4 |
2026/07/29 09:16 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 50175
output: 17
thoughts: 17
Thoughts: Here's a summary of my thought process, as if I were the one thinking it:
**Digging into the `dummy_hcd.c` Code**
Okay, so I need to find where the memory allocation is happening within this dummy host controller driver. The most direct approach, especially when I'm dealing with a codebase I'm not intimately familiar with, is to search for the allocation calls directly. My first instinct is to look for the ubiquitous `alloc` function calls. It's a broad term, but often a good starting point. I'll search for "alloc" within the `dummy_hcd.c` file. This should help me quickly identify the lines of code where memory is being requested, whether it's kernel memory or something specific to the USB stack. From there, I can trace back to the calling functions and understand the context of those allocations. Hopefully, the search will turn up some key allocation calls, which will allow me to start tracing where the HCD is obtaining resources.
|
| 329/4 |
2026/07/29 09:16 |
tool |
grepper |
0m
Args: map[Expression:alloc.*dummy]
Results: map[Output:
Full output is too long, showing 500 out of 1102 lines.
Use more precise expression if possible.
Documentation/driver-api/virtio/writing_virtio_drivers.rst=25=like this::
--
Documentation/driver-api/virtio/writing_virtio_drivers.rst-52- /* initialize device data */
Documentation/driver-api/virtio/writing_virtio_drivers.rst:53: dev = kzalloc(sizeof(struct virtio_dummy_dev), GFP_KERNEL);
Documentation/driver-api/virtio/writing_virtio_drivers.rst-54- if (!dev)
--
arch/x86/kernel/acpi/madt_wakeup.c=38=static void acpi_mp_cpu_die(unsigned int cpu)
--
arch/x86/kernel/acpi/madt_wakeup.c-62-/* The argument is required to match type of x86_mapping_info::alloc_pgt_page */
arch/x86/kernel/acpi/madt_wakeup.c:63:static void __init *alloc_pgt_page(void *dummy)
arch/x86/kernel/acpi/madt_wakeup.c-64-{
--
arch/x86/kernel/kvm.c=202=static void kvm_async_pf_task_wake(u32 token)
--
arch/x86/kernel/kvm.c-223- raw_spin_unlock(&b->lock);
arch/x86/kernel/kvm.c:224: dummy = kzalloc_obj(*dummy, GFP_ATOMIC);
arch/x86/kernel/kvm.c-225-
--
arch/x86/platform/efi/quirks.c=159=efi_status_t efi_query_variable_store(u32 attributes, unsigned long size,
--
arch/x86/platform/efi/quirks.c-189- unsigned long dummy_size = remaining_size + 1024;
arch/x86/platform/efi/quirks.c:190: void *dummy = kzalloc(dummy_size, GFP_KERNEL);
arch/x86/platform/efi/quirks.c-191-
--
drivers/ata/libata-sff.c=2111=static bool ata_resources_present(struct pci_dev *pdev, int port)
--
drivers/ata/libata-sff.c-2130- * first two ports of @host accordingly. Ports marked dummy are
drivers/ata/libata-sff.c:2131: * skipped and allocation failure makes the port dummy.
drivers/ata/libata-sff.c-2132- *
--
drivers/base/regmap/regmap-debugfs.c=537=void regmap_debugfs_init(struct regmap *map)
--
drivers/base/regmap/regmap-debugfs.c-590- kfree(map->debugfs_name);
drivers/base/regmap/regmap-debugfs.c:591: id = ida_alloc(&dummy_ida, GFP_KERNEL);
drivers/base/regmap/regmap-debugfs.c-592- if (id < 0)
--
drivers/crypto/caam/caamalg_qi2.c=5010=static int __cold dpaa2_dpseci_setup(struct fsl_mc_device *ls_dev)
--
drivers/crypto/caam/caamalg_qi2.c-5119-
drivers/crypto/caam/caamalg_qi2.c:5120: ppriv->net_dev = alloc_netdev_dummy(0);
drivers/crypto/caam/caamalg_qi2.c-5121- if (!ppriv->net_dev) {
--
drivers/crypto/caam/qi.c=730=int caam_qi_init(struct platform_device *caam_pdev)
--
drivers/crypto/caam/qi.c-767-
drivers/crypto/caam/qi.c:768: net_dev = alloc_netdev_dummy(0);
drivers/crypto/caam/qi.c-769- if (!net_dev) {
--
drivers/dma/ti/edma.c=2286=static int edma_probe(struct platform_device *pdev)
--
drivers/dma/ti/edma.c-2447- if (ecc->dummy_slot < 0) {
drivers/dma/ti/edma.c:2448: dev_err(dev, "Can't allocate PaRAM dummy slot\n");
drivers/dma/ti/edma.c-2449- ret = ecc->dummy_slot;
--
drivers/firewire/core-card.c=748=static const struct fw_card_driver dummy_driver_template = {
--
drivers/firewire/core-card.c-756- .write_csr = dummy_write_csr,
drivers/firewire/core-card.c:757: .allocate_iso_context = dummy_allocate_iso_context,
drivers/firewire/core-card.c-758- .start_iso = dummy_start_iso,
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c=11454=static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
--
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-11566- if (!dummy_updates) {
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:11567: drm_err(adev_to_drm(adev), "Failed to allocate memory for dummy_updates.\n");
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c-11568- continue;
--
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c=1132=static int smu_free_memory_pool(struct smu_context *smu)
--
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c-1148-
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c:1149:static int smu_alloc_dummy_read_table(struct smu_context *smu)
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c-1150-{
--
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c-1167- if (ret)
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c:1168: dev_err(adev->dev, "VRAM allocation for dummy read table failed!\n");
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c-1169-
--
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c=1187=static int smu_smc_table_sw_init(struct smu_context *smu)
--
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c-1221-
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c:1222: ret = smu_alloc_dummy_read_table(smu);
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c-1223- if (ret)
--
drivers/gpu/drm/drm_gpuvm.c=1045=static const struct drm_gem_object_funcs drm_gpuvm_object_funcs = {
--
drivers/gpu/drm/drm_gpuvm.c-1049-/**
drivers/gpu/drm/drm_gpuvm.c:1050: * drm_gpuvm_resv_object_alloc() - allocate a dummy &drm_gem_object
drivers/gpu/drm/drm_gpuvm.c-1051- * @drm: the drivers &drm_device
--
drivers/gpu/drm/msm/adreno/a6xx_gmu.c=2274=int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
--
drivers/gpu/drm/msm/adreno/a6xx_gmu.c-2328- /* Allocate memory for the GMU dummy page */
drivers/gpu/drm/msm/adreno/a6xx_gmu.c:2329: ret = a6xx_gmu_memory_alloc(gmu, &gmu->dummy, gmu->dummy.size,
drivers/gpu/drm/msm/adreno/a6xx_gmu.c-2330- 0x60000000, "dummy");
--
drivers/gpu/drm/nouveau/dispnv50/head507d.c=247=head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
--
drivers/gpu/drm/nouveau/dispnv50/head507d.c-260- * being able to display the other layers, or we
drivers/gpu/drm/nouveau/dispnv50/head507d.c:261: * need to allocate a dummy black surface here.
drivers/gpu/drm/nouveau/dispnv50/head507d.c-262- */
--
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c=208=nv44_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size,
--
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c-224- if (!vmm->nullp) {
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c:225: nvkm_warn(subdev, "unable to allocate dummy pages\n");
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c-226- vmm->null = 0;
--
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c=770=static int omap_dmm_probe(struct platform_device *dev)
--
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c-864- if (!omap_dmm->dummy_page) {
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c:865: dev_err(&dev->dev, "could not allocate dummy page\n");
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c-866- ret = -ENOMEM;
--
drivers/gpu/drm/panthor/panthor_mmu.c=2723=panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
--
drivers/gpu/drm/panthor/panthor_mmu.c-2749-
drivers/gpu/drm/panthor/panthor_mmu.c:2750: /* We allocate a dummy GEM for the VM. */
drivers/gpu/drm/panthor/panthor_mmu.c-2751- dummy_gem = drm_gpuvm_resv_object_alloc(&ptdev->base);
--
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c=20=struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
--
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c-28-
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c:29: dummy_crtc = drmm_kzalloc(drm, sizeof(*dummy_crtc), GFP_KERNEL);
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c-30- KUNIT_ASSERT_NOT_NULL(test, dummy_crtc);
--
drivers/gpu/drm/vc4/tests/vc4_mock_output.c=23=struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
--
drivers/gpu/drm/vc4/tests/vc4_mock_output.c-34-
drivers/gpu/drm/vc4/tests/vc4_mock_output.c:35: dummy_output = drmm_kzalloc(drm, sizeof(*dummy_output), GFP_KERNEL);
drivers/gpu/drm/vc4/tests/vc4_mock_output.c-36- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
--
drivers/hwtracing/coresight/coresight-dummy.c=111=static int dummy_probe(struct platform_device *pdev)
--
drivers/hwtracing/coresight/coresight-dummy.c-125-
drivers/hwtracing/coresight/coresight-dummy.c:126: desc.name = coresight_alloc_device_name("dummy_source", dev);
drivers/hwtracing/coresight/coresight-dummy.c-127- if (!desc.name)
--
drivers/hwtracing/coresight/coresight-dummy.c-154- } else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) {
drivers/hwtracing/coresight/coresight-dummy.c:155: desc.name = coresight_alloc_device_name("dummy_sink", dev);
drivers/hwtracing/coresight/coresight-dummy.c-156- if (!desc.name)
--
drivers/hwtracing/coresight/coresight-trace-id.h=117=int coresight_trace_id_get_system_id(void);
--
drivers/hwtracing/coresight/coresight-trace-id.h-121- *
drivers/hwtracing/coresight/coresight-trace-id.h:122: * Used to allocate static IDs for system trace sources such as dummy source.
drivers/hwtracing/coresight/coresight-trace-id.h-123- *
--
drivers/hwtracing/coresight/coresight-trbe.c=1311=static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cpu)
--
drivers/hwtracing/coresight/coresight-trbe.c-1335- * are not real ACPI devices, coresight_get_platform_data()
drivers/hwtracing/coresight/coresight-trbe.c:1336: * ends up failing. Instead let's allocate a dummy zeroed
drivers/hwtracing/coresight/coresight-trbe.c-1337- * coresight_platform_data structure and assign that back
--
drivers/infiniband/hw/hfi1/init.c=1205=static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev,
--
drivers/infiniband/hw/hfi1/init.c-1299-
drivers/infiniband/hw/hfi1/init.c:1300: /* allocate dummy tail memory for all receive contexts */
drivers/infiniband/hw/hfi1/init.c-1301- dd->rcvhdrtail_dummy_kvaddr =
--
drivers/infiniband/hw/hfi1/netdev_rx.c=352=int hfi1_alloc_rx(struct hfi1_devdata *dd)
--
drivers/infiniband/hw/hfi1/netdev_rx.c-361- rx->dd = dd;
drivers/infiniband/hw/hfi1/netdev_rx.c:362: rx->rx_napi = alloc_netdev_dummy(0);
drivers/infiniband/hw/hfi1/netdev_rx.c-363- if (!rx->rx_napi) {
--
drivers/media/dvb-frontends/dvb_dummy_fe.c=112=struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void)
--
drivers/media/dvb-frontends/dvb_dummy_fe.c-116- /* allocate memory for the internal state */
drivers/media/dvb-frontends/dvb_dummy_fe.c:117: state = kzalloc_obj(struct dvb_dummy_fe_state);
drivers/media/dvb-frontends/dvb_dummy_fe.c-118- if (!state)
--
drivers/media/dvb-frontends/dvb_dummy_fe.c=133=struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
--
drivers/media/dvb-frontends/dvb_dummy_fe.c-137- /* allocate memory for the internal state */
drivers/media/dvb-frontends/dvb_dummy_fe.c:138: state = kzalloc_obj(struct dvb_dummy_fe_state);
drivers/media/dvb-frontends/dvb_dummy_fe.c-139- if (!state)
--
drivers/media/dvb-frontends/dvb_dummy_fe.c=154=struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
--
drivers/media/dvb-frontends/dvb_dummy_fe.c-158- /* allocate memory for the internal state */
drivers/media/dvb-frontends/dvb_dummy_fe.c:159: state = kzalloc_obj(struct dvb_dummy_fe_state);
drivers/media/dvb-frontends/dvb_dummy_fe.c-160- if (!state)
--
drivers/media/pci/ddbridge/ddbridge-dummy-fe.c=98=struct dvb_frontend *ddbridge_dummy_fe_qam_attach(void)
--
drivers/media/pci/ddbridge/ddbridge-dummy-fe.c-102- /* allocate memory for the internal state */
drivers/media/pci/ddbridge/ddbridge-dummy-fe.c:103: state = kzalloc_obj(struct ddbridge_dummy_fe_state);
drivers/media/pci/ddbridge/ddbridge-dummy-fe.c-104- if (!state)
--
drivers/media/pci/intel/ipu6/ipu6-mmu.c=165=static void free_dummy_page(struct ipu6_mmu_info *mmu_info)
--
drivers/media/pci/intel/ipu6/ipu6-mmu.c-172-
drivers/media/pci/intel/ipu6/ipu6-mmu.c:173:static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
drivers/media/pci/intel/ipu6/ipu6-mmu.c-174-{
--
drivers/media/pci/intel/ipu6/ipu6-mmu.c=547=static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp)
--
drivers/media/pci/intel/ipu6/ipu6-mmu.c-567-
drivers/media/pci/intel/ipu6/ipu6-mmu.c:568: ret = alloc_dummy_l2_pt(mmu_info);
drivers/media/pci/intel/ipu6/ipu6-mmu.c-569- if (ret)
--
drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c=216=static int c3_isp_cap_dummy_buff_create(struct c3_isp_capture *cap)
--
drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c-228- /* The driver never access vaddr, no mapping is required */
drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c:229: dummy_buff->vaddr = dma_alloc_attrs(cap->isp->dev, dummy_buff->size,
drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c-230- &dummy_buff->dma_addr, GFP_KERNEL,
--
drivers/media/platform/broadcom/bcm2835-unicam.c=2254=static int unicam_register_node(struct unicam_device *unicam,
--
drivers/media/platform/broadcom/bcm2835-unicam.c-2323- if (!node->dummy_buf_cpu_addr) {
drivers/media/platform/broadcom/bcm2835-unicam.c:2324: dev_err(unicam->dev, "Unable to allocate dummy buffer.\n");
drivers/media/platform/broadcom/bcm2835-unicam.c-2325- ret = -ENOMEM;
--
drivers/media/platform/rockchip/rkcif/rkcif-stream.c=105=static int rkcif_stream_init_buffers(struct rkcif_stream *stream)
--
drivers/media/platform/rockchip/rkcif/rkcif-stream.c-110- stream->dummy.vaddr =
drivers/media/platform/rockchip/rkcif/rkcif-stream.c:111: dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size,
drivers/media/platform/rockchip/rkcif/rkcif-stream.c-112- &stream->dummy.buffer.buff_addr[0], GFP_KERNEL,
--
drivers/mfd/mt6360-core.c=27=struct mt6360_ddata {
--
drivers/mfd/mt6360-core.c-70-#define MT6360_ALLOC_READ_SIZE(_size) (_size + 3)
drivers/mfd/mt6360-core.c:71:/* prealloca write size = i2c device addr + i2c reg addr + val ... + crc8 + dummy byte */
drivers/mfd/mt6360-core.c-72-#define MT6360_ALLOC_WRITE_SIZE(_size) (_size + 4)
--
drivers/net/dsa/sja1105/sja1105_tas.c=466=bool sja1105_gating_check_conflicts(struct sja1105_private *priv, int port,
--
drivers/net/dsa/sja1105/sja1105_tas.c-479-
drivers/net/dsa/sja1105/sja1105_tas.c:480: dummy = kzalloc_flex(*dummy, entries, num_entries);
drivers/net/dsa/sja1105/sja1105_tas.c-481- if (!dummy) {
--
drivers/net/dummy.c=152=static int __init dummy_init_one(void)
--
drivers/net/dummy.c-156-
drivers/net/dummy.c:157: dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_ENUM, dummy_setup);
drivers/net/dummy.c-158- if (!dev_dummy)
--
drivers/net/ethernet/airoha/airoha_eth.c=3392=static int airoha_probe(struct platform_device *pdev)
--
drivers/net/ethernet/airoha/airoha_eth.c-3448-
drivers/net/ethernet/airoha/airoha_eth.c:3449: eth->napi_dev = alloc_netdev_dummy(0);
drivers/net/ethernet/airoha/airoha_eth.c-3450- if (!eth->napi_dev)
--
drivers/net/ethernet/apple/mace.c=108=static int mace_probe(struct macio_dev *mdev, const struct of_device_id *match)
--
drivers/net/ethernet/apple/mace.c-133- /*
drivers/net/ethernet/apple/mace.c:134: * lazy allocate the driver-wide dummy buffer. (Note that we
drivers/net/ethernet/apple/mace.c-135- * never have more than one MACE in the system anyway)
--
drivers/net/ethernet/cavium/thunder/thunder_bgx.c=1590=static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
--
drivers/net/ethernet/cavium/thunder/thunder_bgx.c-1655- lmacp = &bgx->lmac[lmac];
drivers/net/ethernet/cavium/thunder/thunder_bgx.c:1656: lmacp->netdev = alloc_netdev_dummy(sizeof(struct lmac *));
drivers/net/ethernet/cavium/thunder/thunder_bgx.c-1657-
--
drivers/net/ethernet/ibm/emac/mal.c=516=static int mal_probe(struct platform_device *ofdev)
--
drivers/net/ethernet/ibm/emac/mal.c-585-
drivers/net/ethernet/ibm/emac/mal.c:586: mal->dummy_dev = alloc_netdev_dummy(0);
drivers/net/ethernet/ibm/emac/mal.c-587- if (!mal->dummy_dev) {
--
drivers/net/ethernet/intel/e1000/e1000_main.c=116=static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
--
drivers/net/ethernet/intel/e1000/e1000_main.c-118- int *work_done, int work_to_do);
drivers/net/ethernet/intel/e1000/e1000_main.c:119:static void e1000_alloc_dummy_rx_buffers(struct e1000_adapter *adapter,
drivers/net/ethernet/intel/e1000/e1000_main.c-120- struct e1000_rx_ring *rx_ring,
--
drivers/net/ethernet/intel/e1000/e1000_main.c=3521=static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
--
drivers/net/ethernet/intel/e1000/e1000_main.c-3545- /* prevent buffers from being reallocated */
drivers/net/ethernet/intel/e1000/e1000_main.c:3546: adapter->alloc_rx_buf = e1000_alloc_dummy_rx_buffers;
drivers/net/ethernet/intel/e1000/e1000_main.c-3547- e1000_down(adapter);
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c=82=static int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data,
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c-158- * i40e_create_dummy_packet - Constructs dummy packet for HW
drivers/net/ethernet/intel/i40e/i40e_txrx.c:159: * @dummy_packet: preallocated space for dummy packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c-160- * @ipv4: is layer 3 packet of version 4 or 6
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c=166=static char *i40e_create_dummy_packet(u8 *dummy_packet, bool ipv4, u8 l4proto,
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c-222- * i40e_create_dummy_udp_packet - helper function to create UDP packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c:223: * @raw_packet: preallocated space for dummy packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c-224- * @ipv4: is layer 3 packet of version 4 or 6
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c=230=static void i40e_create_dummy_udp_packet(u8 *raw_packet, bool ipv4, u8 l4proto,
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c-243- * i40e_create_dummy_tcp_packet - helper function to create TCP packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c:244: * @raw_packet: preallocated space for dummy packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c-245- * @ipv4: is layer 3 packet of version 4 or 6
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c=251=static void i40e_create_dummy_tcp_packet(u8 *raw_packet, bool ipv4, u8 l4proto,
--
drivers/net/ethernet/intel/i40e/i40e_txrx.c-269- * i40e_create_dummy_sctp_packet - helper function to create SCTP packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c:270: * @raw_packet: preallocated space for dummy packet
drivers/net/ethernet/intel/i40e/i40e_txrx.c-271- * @ipv4: is layer 3 packet of version 4 or 6
--
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c=1238=static int otx2_tc_add_flow(struct otx2_nic *nic,
--
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1283- if (is_cn20k(nic->pdev)) {
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1284: rc = cn20k_tc_alloc_entry(nic, tc_flow_cmd, new_node, &dummy);
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1285- if (rc) {
--
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c=614=static int prestera_sdma_switch_init(struct prestera_switch *sw)
--
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c-656-
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c:657: sdma->napi_dev = alloc_netdev_dummy(0);
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c-658- if (!sdma->napi_dev) {
--
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c-660- err = -ENOMEM;
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c:661: goto err_alloc_dummy;
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c-662- }
--
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c-668-
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c:669:err_alloc_dummy:
drivers/net/ethernet/marvell/prestera/prestera_rxtx.c-670- prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX,
--
drivers/net/ethernet/mediatek/mtk_eth_soc.c=5095=static int mtk_probe(struct platform_device *pdev)
--
drivers/net/ethernet/mediatek/mtk_eth_soc.c-5339- */
drivers/net/ethernet/mediatek/mtk_eth_soc.c:5340: eth->dummy_dev = alloc_netdev_dummy(0);
drivers/net/ethernet/mediatek/mtk_eth_soc.c-5341- if (!eth->dummy_dev) {
drivers/net/ethernet/mediatek/mtk_eth_soc.c-5342- err = -ENOMEM;
drivers/net/ethernet/mediatek/mtk_eth_soc.c:5343: dev_err(eth->dev, "failed to allocated dummy device\n");
drivers/net/ethernet/mediatek/mtk_eth_soc.c-5344- goto err_unreg_netdev;
--
drivers/net/ethernet/mellanox/mlxsw/pci.c=142=static int mlxsw_pci_napi_devs_init(struct mlxsw_pci *mlxsw_pci)
--
drivers/net/ethernet/mellanox/mlxsw/pci.c-145-
drivers/net/ethernet/mellanox/mlxsw/pci.c:146: mlxsw_pci->napi_dev_tx = alloc_netdev_dummy(0);
drivers/net/ethernet/mellanox/mlxsw/pci.c-147- if (!mlxsw_pci->napi_dev_tx)
--
drivers/net/ethernet/mellanox/mlxsw/pci.c-151-
drivers/net/ethernet/mellanox/mlxsw/pci.c:152: mlxsw_pci->napi_dev_rx = alloc_netdev_dummy(0);
drivers/net/ethernet/mellanox/mlxsw/pci.c-153- if (!mlxsw_pci->napi_dev_rx) {
--
drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c=1712=static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_dummy_ops = {
--
drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c-1715- .deconfigure = mlxsw_sp_fid_dummy_deconfigure,
drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c:1716: .index_alloc = mlxsw_sp_fid_dummy_index_alloc,
drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c-1717- .compare = mlxsw_sp_fid_dummy_compare,
--
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c=1240=int netxen_init_dummy_dma(struct netxen_adapter *adapter)
--
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c-1253- dev_err(&adapter->pdev->dev,
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c:1254: "ERROR: Could not allocate dummy DMA memory\n");
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c-1255- return -ENOMEM;
--
drivers/net/ethernet/ti/netcp_ethss.c=3112=static void init_secondary_ports(struct gbe_priv *gbe_dev,
--
drivers/net/ethernet/ti/netcp_ethss.c-3159- /* Allocate dummy netdev device for attaching to phy device */
drivers/net/ethernet/ti/netcp_ethss.c:3160: gbe_dev->dummy_ndev = alloc_netdev(sizeof(gbe_dev), "dummy",
drivers/net/ethernet/ti/netcp_ethss.c-3161- NET_NAME_UNKNOWN, ether_setup);
--
drivers/net/ethernet/ti/netcp_ethss.c-3163- dev_err(dev,
drivers/net/ethernet/ti/netcp_ethss.c:3164: "Failed to allocate dummy netdev for secondary ports, skipping phy_connect()...\n");
drivers/net/ethernet/ti/netcp_ethss.c-3165- return;
--
drivers/net/ipa/gsi.c=2357=int gsi_init(struct gsi *gsi, struct platform_device *pdev,
--
drivers/net/ipa/gsi.c-2370- */
drivers/net/ipa/gsi.c:2371: gsi->dummy_dev = alloc_netdev_dummy(0);
drivers/net/ipa/gsi.c-2372- if (!gsi->dummy_dev)
--
drivers/net/macsec.c=4311=static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[],
--
drivers/net/macsec.c-4325-
drivers/net/macsec.c:4326: dummy_tfm = macsec_alloc_tfm(dummy_key,
drivers/net/macsec.c-4327- DEFAULT_SAK_LEN,
--
drivers/net/wireless/ath/ath10k/core.c=3617=struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
--
drivers/net/wireless/ath/ath10k/core.c-3733-
drivers/net/wireless/ath/ath10k/core.c:3734: ar->napi_dev = alloc_netdev_dummy(0);
drivers/net/wireless/ath/ath10k/core.c-3735- if (!ar->napi_dev)
--
drivers/net/wireless/ath/ath11k/ahb.c=524=static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
--
drivers/net/wireless/ath/ath11k/ahb.c-537-
drivers/net/wireless/ath/ath11k/ahb.c:538: irq_grp->napi_ndev = alloc_netdev_dummy(0);
drivers/net/wireless/ath/ath11k/ahb.c-539- if (!irq_grp->napi_ndev)
--
drivers/net/wireless/ath/ath11k/pci.c=949=static int ath11k_pci_probe(struct pci_dev *pdev,
--
drivers/net/wireless/ath/ath11k/pci.c-1128-
drivers/net/wireless/ath/ath11k/pci.c:1129: /* kernel may allocate a dummy vector before request_irq and
drivers/net/wireless/ath/ath11k/pci.c-1130- * then allocate a real vector when request_irq is called.
--
drivers/net/wireless/ath/ath11k/pcic.c=572=static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
--
drivers/net/wireless/ath/ath11k/pcic.c-594- irq_grp->grp_id = i;
drivers/net/wireless/ath/ath11k/pcic.c:595: irq_grp->napi_ndev = alloc_netdev_dummy(0);
drivers/net/wireless/ath/ath11k/pcic.c-596- if (!irq_grp->napi_ndev) {
--
drivers/net/wireless/ath/ath12k/ahb.c=564=static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab)
--
drivers/net/wireless/ath/ath12k/ahb.c-578-
drivers/net/wireless/ath/ath12k/ahb.c:579: irq_grp->napi_ndev = alloc_netdev_dummy(0);
drivers/net/wireless/ath/ath12k/ahb.c-580- if (!irq_grp->napi_ndev)
--
drivers/net/wireless/ath/ath12k/pci.c=538=static int ath12k_pci_ext_irq_config(struct ath12k_base *ab)
--
drivers/net/wireless/ath/ath12k/pci.c-558- irq_grp->grp_id = i;
drivers/net/wireless/ath/ath12k/pci.c:559: irq_grp->napi_ndev = alloc_netdev_dummy(0);
drivers/net/wireless/ath/ath12k/pci.c-560- if (!irq_grp->napi_ndev) {
--
drivers/net/wireless/ath/ath12k/pci.c=1528=static int ath12k_pci_probe(struct pci_dev *pdev,
--
drivers/net/wireless/ath/ath12k/pci.c-1628-
drivers/net/wireless/ath/ath12k/pci.c:1629: /* kernel may allocate a dummy vector before request_irq and
drivers/net/wireless/ath/ath12k/pci.c-1630- * then allocate a real vector when request_irq is called.
--
drivers/net/wireless/ath/wil6210/netdev.c=440=int wil_if_add(struct wil6210_priv *wil)
--
drivers/net/wireless/ath/wil6210/netdev.c-456-
drivers/net/wireless/ath/wil6210/netdev.c:457: wil->napi_ndev = alloc_netdev_dummy(0);
drivers/net/wireless/ath/wil6210/netdev.c-458- if (!wil->napi_ndev) {
drivers/net/wireless/ath/wil6210/netdev.c:459: wil_err(wil, "failed to allocate dummy netdev");
drivers/net/wireless/ath/wil6210/netdev.c-460- rc = -ENOMEM;
--
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c=1714=iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
--
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c-1764- * since cfg80211 doesn't accept an empty regdomain, we need
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c:1765: * to allocate space for at least one rule to add a dummy
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c-1766- * (unusable) rule in this case so we can init.
--
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c=3800=iwl_trans_pcie_alloc(struct pci_dev *pdev,
--
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-3878- */
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c:3879: trans_pcie->napi_dev = alloc_netdev_dummy(sizeof(struct iwl_trans_pcie *));
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c-3880- if (!trans_pcie->napi_dev) {
--
drivers/net/wireless/mediatek/mt76/dma.c=1108=mt76_dma_init(struct mt76_dev *dev,
--
drivers/net/wireless/mediatek/mt76/dma.c-1113-
drivers/net/wireless/mediatek/mt76/dma.c:1114: dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *));
drivers/net/wireless/mediatek/mt76/dma.c-1115- if (!dev->napi_dev)
--
drivers/net/wireless/mediatek/mt76/dma.c-1123-
drivers/net/wireless/mediatek/mt76/dma.c:1124: dev->tx_napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *));
drivers/net/wireless/mediatek/mt76/dma.c-1125- if (!dev->tx_napi_dev) {
--
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c=274=static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
--
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c-374-
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c:375: bus->mux_dev = alloc_netdev_dummy(0);
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c-376- if (!bus->mux_dev) {
--
drivers/net/wireless/realtek/rtw88/fw.c=1593=static int __rtw_build_rsvd_page_from_vifs(struct rtw_dev *rtwdev)
--
drivers/net/wireless/realtek/rtw88/fw.c-1614- if (!dummy_pkt) {
drivers/net/wireless/realtek/rtw88/fw.c:1615: rtw_err(rtwdev, "failed to alloc dummy rsvd page\n");
drivers/net/wireless/realtek/rtw88/fw.c-1616- return -ENOMEM;
--
drivers/net/wireless/realtek/rtw88/pci.c=1699=static int rtw_pci_napi_init(struct rtw_dev *rtwdev)
--
drivers/net/wireless/realtek/rtw88/pci.c-1702-
drivers/net/wireless/realtek/rtw88/pci.c:1703: rtwpci->netdev = alloc_netdev_dummy(0);
drivers/net/wireless/realtek/rtw88/pci.c-1704- if (!rtwpci->netdev)
--
drivers/net/wireless/realtek/rtw89/core.c=4544=int rtw89_core_napi_init(struct rtw89_dev *rtwdev)
drivers/net/wireless/realtek/rtw89/core.c-4545-{
drivers/net/wireless/realtek/rtw89/core.c:4546: rtwdev->netdev = alloc_netdev_dummy(0);
drivers/net/wireless/realtek/rtw89/core.c-4547- if (!rtwdev->netdev)
--
drivers/net/wireless/ti/wlcore/main.c=1356=int wl1271_tx_dummy_packet(struct wl1271 *wl)
--
drivers/net/wireless/ti/wlcore/main.c-1389-
drivers/net/wireless/ti/wlcore/main.c:1390:static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
drivers/net/wireless/ti/wlcore/main.c-1391-{
--
drivers/net/wireless/ti/wlcore/main.c-1400- if (!skb) {
drivers/net/wireless/ti/wlcore/main.c:1401: wl1271_warning("Failed to allocate a dummy packet skb");
drivers/net/wireless/ti/wlcore/main.c-1402- return NULL;
--
drivers/net/wireless/ti/wlcore/main.c=6351=struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
--
drivers/net/wireless/ti/wlcore/main.c-6444-
drivers/net/wireless/ti/wlcore/main.c:6445: wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
drivers/net/wireless/ti/wlcore/main.c-6446- if (!wl->dummy_packet) {
--
drivers/net/wwan/t7xx/t7xx_netdev.c=256=static int t7xx_init_netdev_napi(struct t7xx_ccmni_ctrl *ctlb)
--
drivers/net/wwan/t7xx/t7xx_netdev.c-262- */
drivers/net/wwan/t7xx/t7xx_netdev.c:263: ctlb->dummy_dev = alloc_netdev_dummy(0);
drivers/net/wwan/t7xx/t7xx_netdev.c-264- if (!ctlb->dummy_dev)
--
drivers/pci/setup-bus.c=577=static void __assign_resources_sorted(struct list_head *head,
--
drivers/pci/setup-bus.c-614- if (!realloc_head)
drivers/pci/setup-bus.c:615: realloc_head = &dummy_head;
drivers/pci/setup-bus.c-616-
--
drivers/regulator/dummy.c=65=void __init regulator_dummy_init(void)
--
drivers/regulator/dummy.c-68- if (!dummy_fdev) {
drivers/regulator/dummy.c:69: pr_err("Failed to allocate dummy regulator device\n");
drivers/regulator/dummy.c-70- return;
--
drivers/scsi/ips.c=6815=ips_init_phase1(struct pci_dev *pci_dev, int *indexPtr)
--
]
|
| 330/4 |
2026/07/29 09:16 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 63637
output: 41
thoughts: 98
Thoughts: **Analyzing the Probe Function**
Okay, let's revisit this `dummy_udc_probe()` function. I want to make sure I understand how it's getting its platform data. Let's see... it's using this line:
```c
dum = *((void **)dev_get_platdata(&pdev->dev));
```
Alright, so `dev_get_platdata()` is returning a pointer, which is then cast to `void **` and dereferenced to get `dum`. It's getting the data associated with `pdev`. Now, where's `pdev` coming from? I need to trace back where that's initialized. Hmm, is it in `dummy_hcd_probe()`? No, that's for the host controller driver. Wait, I think both the UDC and HCD are created in the `init()` function. Okay, got it. It's all starting to come back to me. Now, the next thing is going to be the context that the functions are called in, and the relationships that they have with each other.
|
| 331/4 |
2026/07/29 09:16 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:init]
Results: map[SourceCode: SourceFile:]
|
| 332/4 |
2026/07/29 09:16 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 64032
output: 46
thoughts: 13
Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts:
**Analyzing the Code - A Quick Check**
Right, so the task at hand is to analyze a piece of code. Let's start with a crucial function call: `dummy_hcd_init()`. My immediate thought is, "Okay, let's see what that's doing." This likely represents the initialization routine for a dummy Host Controller Driver. Since I know this code, the name itself is highly descriptive, which, as a system architect, is a promising sign. I'll need to understand what steps it takes to initialize the hardware simulation or abstraction it represents. What resources does it allocate? Does it set up any internal data structures? Are there any obvious potential error conditions I should look for *first*? Essentially, it's the foundation, and I need to assess how solid that foundation is. A well-designed `init` function here means less debugging later on. So, `dummy_hcd_init()` it is. I need to understand its behaviour to continue.
|
| 333/4 |
2026/07/29 09:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_hcd_init]
Results: map[SourceCode:2813: static int __init dummy_hcd_init(void)
2814: {
2815: int retval = -ENOMEM;
2816: int i;
2817: struct dummy *dum[MAX_NUM_UDC] = {};
2818:
2819: if (usb_disabled())
2820: return -ENODEV;
2821:
2822: if (!mod_data.is_high_speed && mod_data.is_super_speed)
2823: return -EINVAL;
2824:
2825: if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
2826: pr_err("Number of emulated UDC must be in range of 1...%d\n",
2827: MAX_NUM_UDC);
2828: return -EINVAL;
2829: }
2830:
2831: for (i = 0; i < mod_data.num; i++) {
2832: the_hcd_pdev[i] = platform_device_alloc(driver_name, i);
2833: if (!the_hcd_pdev[i]) {
2834: i--;
2835: while (i >= 0)
2836: platform_device_put(the_hcd_pdev[i--]);
2837: return retval;
2838: }
2839: }
2840: for (i = 0; i < mod_data.num; i++) {
2841: the_udc_pdev[i] = platform_device_alloc(gadget_name, i);
2842: if (!the_udc_pdev[i]) {
2843: i--;
2844: while (i >= 0)
2845: platform_device_put(the_udc_pdev[i--]);
2846: goto err_alloc_udc;
2847: }
2848: }
2849: for (i = 0; i < mod_data.num; i++) {
2850: dum[i] = kzalloc_obj(struct dummy);
2851: if (!dum[i]) {
2852: retval = -ENOMEM;
2853: goto err_add_pdata;
2854: }
2855: retval = platform_device_add_data(the_hcd_pdev[i], &dum[i],
2856: sizeof(void *));
2857: if (retval)
2858: goto err_add_pdata;
2859: retval = platform_device_add_data(the_udc_pdev[i], &dum[i],
2860: sizeof(void *));
2861: if (retval)
2862: goto err_add_pdata;
2863: }
2864:
2865: retval = platform_driver_register(&dummy_hcd_driver);
2866: if (retval < 0)
2867: goto err_add_pdata;
2868: retval = platform_driver_register(&dummy_udc_driver);
2869: if (retval < 0)
2870: goto err_register_udc_driver;
2871:
2872: for (i = 0; i < mod_data.num; i++) {
2873: retval = platform_device_add(the_hcd_pdev[i]);
2874: if (retval < 0) {
2875: i--;
2876: while (i >= 0)
2877: platform_device_del(the_hcd_pdev[i--]);
2878: goto err_add_hcd;
2879: }
2880: }
2881: for (i = 0; i < mod_data.num; i++) {
2882: if (!dum[i]->hs_hcd ||
2883: (!dum[i]->ss_hcd && mod_data.is_super_speed)) {
2884: /*
2885: * The hcd was added successfully but its probe
2886: * function failed for some reason.
2887: */
2888: retval = -EINVAL;
2889: goto err_add_udc;
2890: }
2891: }
2892:
2893: for (i = 0; i < mod_data.num; i++) {
2894: retval = platform_device_add(the_udc_pdev[i]);
2895: if (retval < 0) {
2896: i--;
2897: while (i >= 0)
2898: platform_device_del(the_udc_pdev[i--]);
2899: goto err_add_udc;
2900: }
2901: }
2902:
2903: for (i = 0; i < mod_data.num; i++) {
2904: if (!platform_get_drvdata(the_udc_pdev[i])) {
2905: /*
2906: * The udc was added successfully but its probe
2907: * function failed for some reason.
2908: */
2909: retval = -EINVAL;
2910: goto err_probe_udc;
2911: }
2912: }
2913: return retval;
2914:
2915: err_probe_udc:
2916: for (i = 0; i < mod_data.num; i++)
2917: platform_device_del(the_udc_pdev[i]);
2918: err_add_udc:
2919: for (i = 0; i < mod_data.num; i++)
2920: platform_device_del(the_hcd_pdev[i]);
2921: err_add_hcd:
2922: platform_driver_unregister(&dummy_udc_driver);
2923: err_register_udc_driver:
2924: platform_driver_unregister(&dummy_hcd_driver);
2925: err_add_pdata:
2926: for (i = 0; i < mod_data.num; i++)
2927: kfree(dum[i]);
2928: for (i = 0; i < mod_data.num; i++)
2929: platform_device_put(the_udc_pdev[i]);
2930: err_alloc_udc:
2931: for (i = 0; i < mod_data.num; i++)
2932: platform_device_put(the_hcd_pdev[i]);
2933: return retval;
2934: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 334/4 |
2026/07/29 09:16 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 66290
output: 45
thoughts: 1189
Thoughts: **Debugging a USB Gadget Driver Refcount Issue**
Okay, so here's the situation. I'm looking at this `dummy_hcd` and `dummy_udc` code, and I've stumbled upon a potential refcount issue during re-probing. The `dum` object, allocated in `dummy_hcd_init()`, appears to be reused across probe/remove cycles. `dummy_udc_probe()` clears the `dum->gadget` structure with `memzero_explicit()` on each probe. The `dev` member of `gadget` is managed through registration and unregistration via `device_add()` and related calls, where the refcount starts at 1 via `device_initialize()`.
The central problem seems to be a warning about a refcount addition on zero inside `device_add()`, specifically, a `kref` value of zero when it should be at least one. It appears that the device refcount drops to 0, despite `device_initialize()` setting it to 1 initially, and being later re-initialized through `usb_initialize_gadget()`. `usb_add_gadget_udc_release()` calls this, so it should be valid.
Initially, I suspected a race condition between `probe` and `remove` functions, which could cause a release before the device is fully initialized. But `probe` and `remove` are serialized by the device lock, so this is unlikely. Also, I ruled out a situation where a uevent trigger could cause an unexpected `put_device()` call on `gadget->dev`.
However, looking at the registration process, `usb_add_gadget()` first saves the release function, then changes it, and then calls `get_device(&udc->dev)`. It also adds `udc` to `udc_list` *before* the call to `device_add(&udc->dev)`. This seems critical because adding the `udc` to the list makes it available to the gadget bus. This suggests that the issue could be with the interplay between the `udc_list` and gadget driver binding. Perhaps another driver, such as the `raw_gadget`, is binding prematurely.
Now I need to check `check_pending_gadget_drivers()` to see how drivers on `gadget_bus_type` are bound. It appears to be linked somehow to the `udc_list`.
|
| 335/4 |
2026/07/29 09:16 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:check_pending_gadget_drivers]
Results: map[SourceCode: SourceFile:]
|
| 336/4 |
2026/07/29 09:16 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 68064
output: 46
thoughts: 2845
Thoughts: **Analyzing the USB Gadget Driver Binding Crash**
Okay, let's break this down systematically. I need to figure out why `usb_add_gadget()` is crashing, specifically at `device_add()`. First, I started by tracing the binding process of a gadget driver to a UDC. The initial thought was to look at how the `raw_gadget` driver is registered and attached to a UDC, which is registered with `usb_gadget_register_driver_owner()` that calls `driver_register()`. Then `driver_attach()` calls `bus_for_each_dev(&gadget_bus_type, ...)` to find the `gadget->dev` on the bus, and calls `gadget_bind_driver()`, but it only happens after `device_add(&gadget->dev)` is executed. Then it hit me that `bus_probe_device()` calls `gadget_bind_driver()` synchronously from `device_add(&gadget->dev)`.
I went back to the crash trace, focusing on `device_add+0x24`, which is the `get_device(dev)` at the very beginning of the function. This is BEFORE `bus_probe_device()` is called. It implies `gadget->dev` has a refcount of 0 before `device_add()` is even running! That shouldn't be possible because `device_initialize()` should set it to 1, and there is no `put_device()` between `device_initialize()` and `device_add()`.
I considered if `dev_set_name()` could be the culprit, but its failure would return an error, not drop the refcount. I entertained the idea that the crash might be in `device_add(&udc->dev)` instead. Line numbers in `usb_add_gadget()` are crucial here. I found `device_add(&udc->dev)` followed by `device_add(&gadget->dev)`. I followed the refcount flow on `udc->dev`, which is initialized with `device_initialize` and has `get_device` called on it. So `udc->dev` MUST have refcount 2 before the call to `device_add()`.
The focus then shifted back to `gadget->dev`. I recalled a previous patch where a potential issue could arise in the `gadget_bind()` which is called from `gadget_bind_driver()` when `bus_probe_device()` is called after `device_add(&gadget->dev)`. A condition was identified where the `gadget` could be freed, leading to the crash.
I had the thought, could `usb_add_gadget()` be called twice? That was quickly dismissed, as probe is serialized. Then I considered the crash could be in `get_device(dev->parent)`, but `dev->parent` references a probe being handled, which should never cause an error.
The crash trace confirmed `device_add+0x24` is indeed the `get_device(dev)` for `gadget->dev`. I went back to basics and reviewed `usb_initialize_gadget()`. `device_initialize()` should set refcount to 1 and the critical point is that no `put_device()` call is performed before `device_add()`.
I then considered that maybe `udc->dev` is crashing. In `usb_add_gadget`, `udc->dev` is initialized and has a `get_device` call, ensuring its refcount is at least 2 before `device_add()`. So, I'm confident `udc->dev` can't be at fault.
The only way that `gadget->dev` refcount could be 0 is if it was already freed. I rechecked allocation, and the memory allocated in `dummy_hcd_init()` still exists. I realized `usb_add_gadget()` overwrites `gadget->dev.release` but that doesn't explain the refcount issue. So now I'm back to square one, trying to figure out how `gadget->dev` has a refcount of 0 just before `device_add()` is called. The refcount can only be 0, if another function, between the `device_initialize()` and `device_add()` calls, has inadvertently dropped the refcount, which I can't find.
|
| 337/4 |
2026/07/29 09:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_del_gadget_udc]
Results: map[SourceCode:1606: /**
1607: * usb_del_gadget_udc - unregisters a gadget
1608: * @gadget: the gadget to be unregistered.
1609: *
1610: * Calls usb_del_gadget() and does a final usb_put_gadget().
1611: */
1612: void usb_del_gadget_udc(struct usb_gadget *gadget)
1613: {
1614: usb_del_gadget(gadget);
1615: usb_put_gadget(gadget);
1616: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 338/4 |
2026/07/29 09:16 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 72107
output: 46
thoughts: 232
Thoughts: **My Analysis of Gadget Device Cleanup**
Okay, so I'm tracing the cleanup process for a USB gadget, and it's all making sense now. It looks like `usb_del_gadget_udc()` calls `usb_put_gadget(gadget)`, right? And *that* in turn calls `put_device(&gadget->dev)`. Naturally.
Now, here's where it gets interesting. When `put_device(&gadget->dev)` is executed, the reference count for the device structure gets decremented to zero. This triggers `kobject_cleanup()`, which seems crucial.
Wait a second... inside `kobject_cleanup()`, I see this snippet:
```c
/* remove from sysfs if the caller did not do it */
if (kobj->state_in_sysfs) {
pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
kobject_name(kobj), kobj);
kobject_del(kobj);
}
```
Okay, so it seems to handle removing the object from sysfs if the caller didn't explicitly do it. It cleans up the name and all the standard stuff. However, and this is the key point, this *doesn't* actually free the memory allocated for the `gadget` structure itself! The reason is because `gadget->dev.release` is set to `dummy_gadget_release`. This is a *dummy* release function, which suggests it is likely a no-op that avoids releasing the gadget's memory. Okay, I'm going to have to check what `dummy_gadget_release` actually *does*. This is critical, it looks like it is managing the lifespan of the gadget itself.
|
| 339/4 |
2026/07/29 09:17 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_gadget_release]
Results: map[SourceCode: SourceFile:]
|
| 340/4 |
2026/07/29 09:17 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 72797
output: 45
thoughts: 58
Thoughts: Here's my summary, as the thought process unfolds:
**USB Gadget Initialization Deep Dive**
Okay, let's break this down. I'm focusing on how this USB gadget initialization works. I need to keep the details of the driver's setup in mind. Specifically, I'm tracing the execution path. Right now, I'm fixated on `usb_initialize_gadget()`. What does this function actually do? Ah, it sets `gadget->dev.release`. I understand that `gadget` is the core gadget structure here, and `dev.release` is a key function pointer. Interesting.
And hang on... `release` is set to `release`, which seems intuitive. But what is `release` when this is called? Let me double-check the call chain... Yes! `usb_add_gadget_udc()` passes NULL for `release`. Okay, so `release` is NULL in this context, right? If so then that means that in this instance `gadget->dev.release` becomes `usb_udc_nop_release`. Yep, that makes sense. It's the default null operation if no explicit release function is provided. This is how the device structure's cleanup function is associated. Makes sense!
|
| 341/4 |
2026/07/29 09:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:usb_udc_nop_release]
Results: map[SourceCode:1362: static void usb_udc_nop_release(struct device *dev)
1363: {
1364: dev_vdbg(dev, "%s\n", __func__);
1365: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 342/4 |
2026/07/29 09:17 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 73267
output: 34
thoughts: 734
Thoughts: **The Asynchronous kobject_cleanup() Conundrum**
Okay, so `usb_udc_nop_release` doesn't actually free any memory. That's fine; I've seen that before in similar driver frameworks. So, when `dummy_udc_probe()` gets called again after a removal, it does a `memzero_explicit()` on the gadget structure. Then, `usb_add_gadget_udc()` calls `usb_initialize_gadget()`, which in turn calls `device_initialize()` on what should be a new `struct device`. I pause here, just to make sure I'm not missing something - is it valid to re-initialize a `struct device` like that, even if it might've previously been released?
`device_initialize()` is calling `kobject_init()`, which I know re-initializes the kobject, setting its refcount to 1 with `kref_init()`, creating a clean slate. So the refcount shouldn't be zero by the time `device_add()` is called. That makes sense. Wait a minute... Could it be that `usb_del_gadget_udc()` was called, but the `kobject_cleanup()` is still running asynchronously in a workqueue? `kobject_put()` calls `kref_put()`, which then calls `kobject_release()`, which schedules the `kobject_cleanup()` function using `schedule_delayed_work()` or `schedule_work()`. Bingo!
The lightbulb clicks! If `dummy_udc_probe()` is called immediately after `dummy_udc_remove()`, after `memzero_explicit()` and `device_initialize()` execute, then `device_add()` is called. Meanwhile, that asynchronous workqueue kicks off `kobject_cleanup()` on the *same* `kobject`! Now, I see it. It's likely that `kobject_cleanup()` might be doing something that corrupts the `kobject` or could drop the refcount unexpectedly. If `memzero_explicit()` clears the `kobject`, then the `work_struct` inside it gets cleared too! Now, if the workqueue is *currently* executing `kobject_cleanup()`, it could be accessing a partially or completely cleared `kobject`!
I double checked my assumptions - `kobject_release()` only schedules this work if `CONFIG_DEBUG_KOBJECT_RELEASE` is enabled, or if it's a generic kobject, not a specific device. I'm going to double check `kobject_release()` in `lib/kobject.c` and make sure my understanding of the refcounting and cleanup process is correct. I have a hunch this is the root of the problem.
|
| 343/4 |
2026/07/29 09:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:lib/kobject.c Name:kobject_release]
Results: map[SourceCode: 709: static void kobject_release(struct kref *kref)
710: {
711: struct kobject *kobj = container_of(kref, struct kobject, kref);
712: #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
713: unsigned long delay = HZ + HZ * get_random_u32_below(4);
714: pr_info("'%s' (%p): %s, parent %p (delayed %ld)\n",
715: kobject_name(kobj), kobj, __func__, kobj->parent, delay);
716: INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
717:
718: schedule_delayed_work(&kobj->release, delay);
719: #else
720: kobject_cleanup(kobj);
721: #endif
722: }
SourceFile:lib/kobject.c]
|
| 344/4 |
2026/07/29 09:17 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 74911
output: 20
thoughts: 1599
Thoughts: **Debugging a USB Gadget Driver Refcount Issue**
Okay, so I'm diving into this kernel crash related to `kobject_get` and refcounts during USB gadget driver loading. My initial thought process is this:
1. **Context is Key:** First, I'm thinking about `kobject_cleanup()`. It's normally synchronous, right? But if `CONFIG_DEBUG_KOBJECT_RELEASE` is enabledβwhich is often the case in syzkaller kernelsβit gets delayed. This delay is between 1 and 4 seconds. If `dummy_udc_probe()` happens within this window, it re-initializes the kobject. The delayed work then runs `kobject_cleanup()`.
2. **Refcount Mysteries:** The delayed cleanup is where things get interesting. If the refcount is already zero, as it would be if cleanup happens before re-initialization, there's a problem. The crash is `refcount_t: addition on 0`, meaning it's calling `kobject_get` on a `kobject` that has been freed! I need to determine how the refcount gets to 0.
3. **Narrowing Down the Problem:** My initial focus was on where the crash happens. Let's trace it back. The crash is in `kobject_get+0xfa/0x120` from within `device_add()`, specifically, `usb_add_gadget()`, so where in the gadget's lifetime is this gadget object being called?
* I'm eliminating `udc->dev` because it's a fresh object allocated within `usb_add_gadget()` itself.
* This leaves `gadget->dev`, but that's initialized by `device_initialize`.
4. **The `device_add` Path:** The code path that is failing is `device_add(&gadget->dev)`. I'm going through the call stack again, specifically to `usb_add_gadget()`, because that's where `device_add` is being called with `gadget->dev`. My theory is that `device_add()` triggers some event that is messing with the refcount. I'm focusing on lines from `usb_add_gadget()` :
* Line 1441: `get_device(&udc->dev);`
* Line 1448: `ret = device_add(&udc->dev);`
* Line 1461: `ret = device_add(&gadget->dev);`
5. **Refcount Inconsistencies:** I need to understand when and how `gadget->dev.kobj.kref.refcount` becomes 0. `usb_initialize_gadget()` which calls `device_initialize()` sets refcount to 1 by `kobject_init()`. So `kobject_init` should set it to 1, I need to know where it drops from there. I'm checking, and `usb_add_gadget_udc` calls `usb_initialize_gadget()`, so this is set.
6. **Uevent Implications**: `device_add()` triggers a uevent. Could this uevent lead to a use-after-free situation? Maybe userspace? No, not possible. Because the device isn't fully added yet. Could a driver be loading, or un-loading because of the uevent? Maybe. Is a `gadget` driver binding?
7. **Gadget Driver Binding**: The `raw_gadget` driver is registered, and its driver is on `gadget_bus_type`. This driver binds to `gadget->dev`. If the driver registers after the `udc` is on the `udc_list` but BEFORE `gadget->dev` is added to the bus, does that cause an issue?
* `usb_gadget_register_driver_owner()` iterates over `udc_list`? No. The issue has something to do with `check_pending_gadget_drivers()` in that case.
* `driver_register()` will be called. That then calls `bus_for_each_dev()` . It will not find the `gadget->dev`.
8. **The Core Question**: The core question remains: How is the refcount 0 when it should be 1? Or, is something, somewhere, calling `put_device()`? I'm missing a point in the lifecycle, and need to re-examine the interactions with `device_add()` and the driver binding process to find where the refcount gets improperly decremented.
|
| 345/4 |
2026/07/29 09:17 |
tool |
grepper |
0m
Error:
Args: map[Expression:pending_gadget_drivers]
Results:
|
| 346/4 |
2026/07/29 09:17 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 77564
output: 26
thoughts: 12
Thoughts: Here's a summary of my thought process:
**Diving into `udc_list` Usage**
Okay, time to get my bearings. The task at hand involves understanding how `udc_list` is being used, so let's start there. I need to pinpoint every instance where `udc_list` is accessed, modified, or otherwise interacted with. I'm going to have to carefully trace its flow through the codebase, paying close attention to any potential pitfalls or optimizations. My expertise is such that I want to ensure it is being employed in the best, most performant, and logically sound manner. I'll be looking for opportunities to refactor, improve clarity, or even identify potential bottlenecks. The ultimate goal is to ascertain whether its utilization is optimal, in line with best practices, and free from any subtle bugs that might be lurking. Let's dig in.
|
| 347/4 |
2026/07/29 09:17 |
tool |
grepper |
0m
Args: map[Expression:list_for_each_entry.*udc_list]
Results: map[Output:drivers/usb/gadget/udc/core.c=1536=char *usb_get_gadget_udc_name(void)
--
drivers/usb/gadget/udc/core.c-1542- mutex_lock(&udc_lock);
drivers/usb/gadget/udc/core.c:1543: list_for_each_entry(udc, &udc_list, list) {
drivers/usb/gadget/udc/core.c-1544- if (!udc->driver) {
]
|
| 348/4 |
2026/07/29 09:17 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 77938
output: 43
thoughts: 1432
Thoughts: **Analyzing a Potential Reference Count Leak**
Okay, first things first, let me clarify that `usb_get_gadget_udc_name()` isn't used by `raw_gadget`, so that's a dead end. But, the crash... it *might* not be in `device_add(&gadget->dev)`. I need to re-examine the previous patch for clues.
Let's look at `gadget_bind()`. If `raw_queue_event()` fails, the code returns early. Specifically, I see that the code within this conditional *does* correctly nullify `dev->gadget` and free the request. However, crucially, it *doesn't* call `kref_get(&dev->count)`. This could indicate a potential leak if an error occurs early in the event queueing stage.
Now, `gadget_bind()` is called by `gadget_bind_driver()`. If `gadget_bind()` fails, `gadget_bind_driver()` handles the error. It cleans up the driver state and then returns the error. Okay, so `gadget_bind_driver()` propagates the error to `really_probe()`, which propagates the error to `__driver_probe_device()`, and so on, all the way to `bind_store()`. The key is that this early failure path *doesn't* call `kref_put()` to balance the `kref_get()` that *would* have occurred in the success path. *That* might be it! We have a leaked reference if `raw_queue_event()` fails!
Let's switch gears and consider the success case. When `gadget_bind()` *succeeds*, it calls `kref_get(&dev->count)`. Then, later, `gadget_unbind()` is called. In `gadget_unbind()`, we have the matching `kref_put(&dev->count, dev_free)`. And, critically, the code explicitly nullifies `dev->gadget`.
Now, that patch... ah, here's where it gets interesting. If `dev->udc_usage > 0`, `gadget_unbind()` waits for completion. But how does this work with the ep0 state? I need to analyze `raw_process_ep0_io()` now.
In `raw_process_ep0_io()`, if `wait_for_completion_interruptible()` is interrupted, there's a check for `dev->state != STATE_DEV_FAILED` which is set by `gadget_unbind()`. If the state *is* `STATE_DEV_FAILED`, the code goes to `else`, where it *re*waits for `dev->ep0_done`. But, critically, if the interrupt happened because the UDC core is shutting down, `usb_ep_dequeue()` is *not* called. Who's going to complete that?
However, if the UDC is unbinding, the UDC core *should* stop the controller and complete all requests with `-ESHUTDOWN`. Then `dev->ep0_done` *will* be completed. So that part should be fine. But what if the UDC core is waiting for `gadget_unbind()` to return before shutting down the controller? I need to verify how `gadget_unbind_driver()` interacts with this. This looks like a race...
|
| 349/4 |
2026/07/29 09:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/core.c Name:gadget_unbind_driver]
Results: map[SourceCode:1707: static void gadget_unbind_driver(struct device *dev)
1708: {
1709: struct usb_gadget *gadget = dev_to_usb_gadget(dev);
1710: struct usb_udc *udc = gadget->udc;
1711: struct usb_gadget_driver *driver = udc->driver;
1712:
1713: dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
1714:
1715: udc->allow_connect = false;
1716: cancel_work_sync(&udc->vbus_work);
1717: mutex_lock(&udc->connect_lock);
1718: usb_gadget_disconnect_locked(gadget);
1719: usb_gadget_disable_async_callbacks(udc);
1720: if (gadget->irq)
1721: synchronize_irq(gadget->irq);
1722: mutex_unlock(&udc->connect_lock);
1723:
1724: udc->driver->unbind(gadget);
1725:
1726: mutex_lock(&udc->connect_lock);
1727: usb_gadget_udc_stop_locked(udc);
1728: mutex_unlock(&udc->connect_lock);
1729:
1730: mutex_lock(&udc_lock);
1731: driver->is_bound = false;
1732: udc->driver = NULL;
1733: mutex_unlock(&udc_lock);
1734:
1735: kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
1736: }
SourceFile:drivers/usb/gadget/udc/core.c]
|
| 350/4 |
2026/07/29 09:17 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 80669
output: 47
thoughts: 793
Thoughts: **The Unbinding Conundrum**
Okay, so I'm tracing through this unbinding sequence and I think I see a race condition. It looks like `usb_gadget_udc_stop_locked(udc)` is called *after* `udc->driver->unbind(gadget)`. That means when `gadget_unbind()` is called, the underlying controller isn't stopped yet! That's a problem.
If `raw_process_ep0_io()` is waiting for `dev->ep0_done` *without* calling `usb_ep_dequeue()`, it could hang indefinitely, because the controller is still active and the request is still queued. And the UDC core isn't going to stop the controller until `gadget_unbind()` returns. But `gadget_unbind()` is waiting for `dev->udc_usage` to hit 0.
Wait a second, `dev->udc_usage` *is* 0 while `raw_process_ep0_io()` is blocked waiting for `dev->ep0_done`! So `gadget_unbind()` *will* return, because `udc_usage` is already 0. Then `gadget_unbind()` returns, and `gadget_unbind_driver()` calls `usb_gadget_udc_stop_locked(udc)`. This finally stops the controller and completes the request with `-ESHUTDOWN`. Bingo.
This will complete `dev->ep0_done`! So `raw_process_ep0_io()` will wake up from `wait_for_completion(&dev->ep0_done)`. But that's a problem!
Once `gadget_unbind()` returns, it frees `dev->req`! The following code is in `gadget_unbind()`:
```c
if (dev->req) {
usb_ep_free_request(gadget->ep0, dev->req);
dev->req = NULL;
}
```
But `raw_process_ep0_io()` is still running! When it wakes up, it will access `dev->req`! It will try to access the now-freed `dev->req`:
```c
if (in) {
if (copy_to_user(data, dev->req->buf, dev->req->actual)) {
```
This looks like a textbook use-after-free on `dev->req`! Could *this* be the root cause of the crash? The crash I'm seeing is a `refcount_t: addition on 0; use-after-free.` in `usb_add_gadget()`. I need to figure out how `dev->req` relates to the refcount issue.
Wait, the `dev->req` is a `struct usb_request`, not a `struct device`. I need to re-evaluate how this relates to the refcount issue.
Okay, so `usb_ep_free_request()` frees the request. If `raw_process_ep0_io()` is accessing `dev->req->buf` after the free, that's corrupting memory. Wait a second, the `dev->req->buf` itself isn't freed by `usb_ep_free_request()`! It's allocated separately. The question is, who allocated it? Is it `raw_gadget`, or `raw_ioctl_ep0_read()`, or something else entirely? I need to check `raw_process_ep0_io()` to see when `dev->req->buf` is allocated.
|
| 351/4 |
2026/07/29 09:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_process_ep0_io]
Results: map[SourceCode: 683: static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
684: void *data, bool in)
685: {
686: int ret = 0;
687: unsigned long flags;
688:
689: spin_lock_irqsave(&dev->lock, flags);
690: if (dev->state != STATE_DEV_RUNNING) {
691: dev_dbg(dev->dev, "fail, device is not running\n");
692: ret = -EINVAL;
693: goto out_unlock;
694: }
695: if (!dev->gadget) {
696: dev_dbg(dev->dev, "fail, gadget is not bound\n");
697: ret = -EBUSY;
698: goto out_unlock;
699: }
700: if (dev->ep0_urb_queued) {
701: dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
702: ret = -EBUSY;
703: goto out_unlock;
704: }
705: if ((in && !dev->ep0_in_pending) ||
706: (!in && !dev->ep0_out_pending)) {
707: dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
708: ret = -EBUSY;
709: goto out_unlock;
710: }
711: if (WARN_ON(in && dev->ep0_out_pending)) {
712: ret = -ENODEV;
713: dev->state = STATE_DEV_FAILED;
714: goto out_unlock;
715: }
716: if (WARN_ON(!in && dev->ep0_in_pending)) {
717: ret = -ENODEV;
718: dev->state = STATE_DEV_FAILED;
719: goto out_unlock;
720: }
721:
722: dev->req->buf = data;
723: dev->req->length = io->length;
724: dev->req->zero = usb_raw_io_flags_zero(io->flags);
725: dev->ep0_urb_queued = true;
726: spin_unlock_irqrestore(&dev->lock, flags);
727:
728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
729: if (ret) {
730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 352/4 |
2026/07/29 09:18 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 83505
output: 42
thoughts: 2638
Thoughts: **Analysis of UAF in USB Gadget Driver**
Okay, let's break this down. My initial concern was a potential use-after-free related to `dev->req` after a `wait_for_completion_interruptible()`, but it seems that isn't the issue. The code flow in `raw_process_ep0_io()` protects against that scenario. Specifically, `dev->req->buf` isn't accessed after the wait unless the interrupt path is taken, where a `usb_ep_dequeue()` is called, but it skips that step under failure conditions. And, in any case, `dev->count` guarantees the lifetime of `dev`. So that's good.
My next line of investigation started with why the previous patch caused a crash in `usb_add_gadget()`. I went through the `gadget_bind()` and `gadget_bind_driver()` logic. Specifically, if `gadget_bind()` fails, the driver is unregistered. The key problem here is in the failure path of `gadget_bind()`. If `raw_queue_event()` fails (like if the event queue is full), the driver unregisters, but `dev->eps` and `dev->eps_num` are not cleared. These values are used to hold the gadget endpoints.
The key observation is that `gadget_bind()` is called *before* the UDC is added, but it calls `kref_get(&dev->count)`. So the refcount *should* protect the device object.
I realized that `gadget_bind()` is called during the probe. Then `bus_probe_device()` will probe the device. The probe function is called even if `gadget_bind()` fails. Thus, `usb_add_gadget()` might return successfully even if `gadget_bind()` failed, and the gadget would have pointers to endpoints.
The unbind function is not called until `usb_gadget_unregister_driver` is called. So, the device will still be bound.
Crucially, the previous patch *moved* the endpoint cleanup (disabling, freeing requests, and `kfree`-ing `desc`) from `dev_free()` to `gadget_unbind()`. However, if `gadget_bind()` fails, `gadget_unbind()` is never called, so these endpoints are not cleaned up. This would happen if a user tried to connect an additional UDC or driver on top of the original driver.
The patch seems to get the refcount correct in the failure case in `gadget_bind()` by cleaning up the pointers to the request. The refcount is also handled correctly during the probe, and the UDC is unbound by an unregister operation.
However, the refcount is not balanced because the endpoint cleanup is performed in `gadget_unbind()`. This would cause a resource leak.
So, the issue is not a use-after-free, but a resource leak, because the endpoint resources are leaked when `gadget_bind()` fails *and* the endpoint cleanup has been moved to a path that isn't called when binding fails.
|
| 353/4 |
2026/07/29 09:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_release]
Results: map[SourceCode: 444: static int raw_release(struct inode *inode, struct file *fd)
445: {
446: int ret = 0;
447: struct raw_dev *dev = fd->private_data;
448: unsigned long flags;
449: bool unregister = false;
450:
451: spin_lock_irqsave(&dev->lock, flags);
452: dev->state = STATE_DEV_CLOSED;
453: if (!dev->gadget) {
454: spin_unlock_irqrestore(&dev->lock, flags);
455: goto out_put;
456: }
457: if (dev->gadget_registered)
458: unregister = true;
459: dev->gadget_registered = false;
460: spin_unlock_irqrestore(&dev->lock, flags);
461:
462: if (unregister) {
463: ret = usb_gadget_unregister_driver(&dev->driver);
464: if (ret != 0)
465: dev_err(dev->dev,
466: "usb_gadget_unregister_driver() failed with %d\n",
467: ret);
468: /* Matches kref_get() in raw_ioctl_run(). */
469: kref_put(&dev->count, dev_free);
470: }
471:
472: out_put:
473: /* Matches dev_new() in raw_open(). */
474: kref_put(&dev->count, dev_free);
475: return ret;
476: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 354/4 |
2026/07/29 09:18 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 87376
output: 18
thoughts: 2849
Thoughts: **Analyzing the `refcount_t: addition on 0; use-after-free` Crash in `usb_add_gadget()`**
Okay, here's the breakdown of my thoughts as I dissect this crash:
First, I confirmed that `raw_release()` will indeed call `usb_gadget_unregister_driver()`, which unbinds the gadget and calls `gadget_unbind()`. So, the unbinding path is clear. But, the question remains: why the refcount issue in the first place?
The crash trace points to `usb_add_gadget()`'s call to `device_add(&gadget->dev)`. I need to understand how the refcount of `gadget->dev` gets to zero when it should be at least one. My initial thought was perhaps `gadget->dev.parent` (`&pdev->dev`) was the culprit, but given `dummy_udc_probe()` is running, that's unlikely.
I focused on the call to `get_device(dev)` in `device_add()`. The debugger reveals that `dev`, which is `&gadget->dev`, has a refcount of zero. But, how can this happen after `usb_initialize_gadget()` which calls `device_initialize(&gadget->dev)` and *should* set the refcount to one?
I thought:
- `device_add()` triggers a `gadget_bind_driver()` which could fail, but it's asynchronous or ignores errors.
- What if `gadget_bind_driver()` succeeded, binding `raw_gadget` to the UDC?
- This binding creates a misc device `/dev/raw-gadget`. When this device is opened the driver binds to the UDC and calls `gadget_bind()`.
- Inside `gadget_bind()` is where `dev->gadget = gadget` is set. If there's an issue during a read `ep0` operation, `gadget->dev` might not be initialized and could be the reason for the refcount issue.
- If `/dev/raw-gadget` is closed, `raw_release()` is called, which calls `usb_gadget_unregister_driver()`. That unregisters the driver and calls `gadget_unbind_driver()`, eventually calling `gadget_unbind()`. However, unregistering the driver doesn't necessarily free `gadget->dev`.
I started to suspect concurrent operation; maybe `dummy_udc_remove()` is being called while `usb_add_gadget()` is still running. However, `dummy_udc_probe()` and `dummy_udc_remove()` are serialized by the device lock, so that's not the cause.
I then revisited `usb_add_gadget()`. I wondered: what if `dev_set_name()` fails? And `usb_add_gadget()` doesn't check the return value! If `dev_set_name()` fails, `gadget->dev.kobj.name` will be `NULL`. Then `device_add(&gadget->dev)` is called, the kernel calls `get_device(dev)`. However, `dev_set_name()` doesn't drop the refcount if it fails.
Looking closer at the crash trace, I see `kobject_get()` crashing. That's because `kobj->kref.refcount` is zero. Then I thought: Maybe `gadget->dev` was NEVER initialized. But then I remembered that `usb_initialize_gadget()` calls `device_initialize()`, which initializes the `kref` inside the `kobj`.
The refcount *should* be set to 1. So, something else is decrementing the refcount. I was trying to figure out if another part of the code has access to `gadget->dev` and might be calling `put_device()`. I knew that the `gadget` is passed to `usb_add_gadget()`, and `udc->gadget` and `gadget->udc` are set in the function. Also, the `gadget` is added to a `udc_list`. I need to investigate the usages of the `udc_list`.
|
| 355/4 |
2026/07/29 09:19 |
tool |
grepper |
0m
Args:
Results: map[Output:drivers/usb/gadget/udc/core.c=67=static const struct class udc_class;
drivers/usb/gadget/udc/core.c:68:static LIST_HEAD(udc_list);
drivers/usb/gadget/udc/core.c-69-
drivers/usb/gadget/udc/core.c:70:/* Protects udc_list, udc->driver, driver->is_bound, and related calls */
drivers/usb/gadget/udc/core.c-71-static DEFINE_MUTEX(udc_lock);
--
drivers/usb/gadget/udc/core.c=1410=int usb_add_gadget(struct usb_gadget *gadget)
--
drivers/usb/gadget/udc/core.c-1443- mutex_lock(&udc_lock);
drivers/usb/gadget/udc/core.c:1444: list_add_tail(&udc->list, &udc_list);
drivers/usb/gadget/udc/core.c-1445- mutex_unlock(&udc_lock);
--
drivers/usb/gadget/udc/core.c=1536=char *usb_get_gadget_udc_name(void)
--
drivers/usb/gadget/udc/core.c-1542- mutex_lock(&udc_lock);
drivers/usb/gadget/udc/core.c:1543: list_for_each_entry(udc, &udc_list, list) {
drivers/usb/gadget/udc/core.c-1544- if (!udc->driver) {
]
|
| 356/4 |
2026/07/29 09:19 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 91598
output: 45
thoughts: 1508
Thoughts: **Debugging the USB Gadget Crash: A Refcount Mystery**
Okay, let's break this down. The crash is happening in `kobject_get`, likely due to a use-after-free, specifically with the refcount. I need to figure out how the refcount on `gadget->dev` is becoming zero, when it should be at least one.
First, I examined `usb_get_gadget_udc_name()`, thinking concurrent access to the `udc_list` could be an issue, but that's a dead end; it only reads the name. No race there.
Then, I considered if `device_add(&udc->dev)` was triggering something like a udev rule that unbinds the device. But, a `dummy_udc_probe` is holding the device lock, so that scenario is impossible. No concurrency there. I also checked if adding a device to a class could trigger driver binding, but thatβs not how classes work. However, the parent device is linked, but that also doesnβt explain anything.
Back to the crash trace! It's `kobject_get` and `device_add`. Could the `dev` in `device_add` be the problem? `device_add` gets the device, and if it's NULL, it returns. The device exists, so that isn't it. The pointer is initialized, so it isnβt garbage. Maybe `kobject_get` is crashing because the `kobject` isnβt initialized, but the kernel WARNS if `kobj->state_initialized` is zero, before calling `kref_get`. However, thereβs no warning, which means the kobject WAS initialized.
But, if the kobject WAS initialized, and the refcount isn't zero, it would not crash. So, the refcount must be zero. How can it be zero? It must have been decreased by `kref_put()`. The only way to decrease it is if someone is calling `put_device(&gadget->dev)`. The only place I see that happening within this code path is inside `usb_add_gadget()`, under `err_unlist_udc`, but this path isn't taken.
What's more, `usb_add_gadget_udc_release()` also calls `usb_put_gadget(gadget)` but that would mean that `usb_add_gadget()` failed. That isn't possible because the code is in the middle of executing `usb_add_gadget()`!
Then, I considered if `device_add()` could trigger an unexpected callback leading to unbinding. That also looks impossible.
Now, wait... there was that patch about a failure in `gadget_bind()`. If that failed, it would call `usb_ep_free_request(gadget->ep0, req)`. Could THAT be dropping the refcount? Let me check `dummy_free_request()`. Bingo! I need to see what's going on in that function. That function could be the problem.
|
| 357/4 |
2026/07/29 09:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/udc/dummy_hcd.c Name:dummy_free_request]
Results: map[SourceCode: 700: static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req)
701: {
702: struct dummy_request *req;
703:
704: if (!_ep || !_req) {
705: WARN_ON(1);
706: return;
707: }
708:
709: req = usb_request_to_dummy_request(_req);
710: WARN_ON(!list_empty(&req->queue));
711: kfree(req);
712: }
SourceFile:drivers/usb/gadget/udc/dummy_hcd.c]
|
| 358/4 |
2026/07/29 09:19 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 94027
output: 40
thoughts: 4607
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in USB Gadget Unbinding**
Okay, so I'm digging into this code, trying to understand a possible memory safety issue. I'm focusing on the interaction between `gadget_bind()`, `gadget_unbind()`, and the `USB_RAW_IOCTL_EP0_READ` ioctl. It seems like the crux of the problem lies in how we handle the `dev->req` (USB request) and the UDC usage count (`dev->udc_usage`) during unbinding.
Initially, I see that if `raw_queue_event()` fails during bind, the code correctly frees the allocated request (`req`) and sets `dev->req` to NULL, which is good. I also confirm that the other endpoint requests, `dev->eps[i].req` are allocated elsewhere so they are not involved in this discussion.
Now, the issue arises when `gadget_unbind()` is called. The current logic in the provided patch seems to set `dev->unbind_completion` to `&done`, wait for this completion, clear the gadget pointer, and return. The problem is that, it may free `dev->req` (the request allocated for EP0) *before* the request is actually completed by the UDC core. This is triggered if `udc_usage` is zero which occurs if we are waiting for the endpoint to complete.
Specifically, if the ioctl is waiting for `ep0_done`, and `gadget_unbind()` is called, the gadget will set `dev->req` to null and then free it. When the core signals to the endpoint the completion the code will attempt to access the memory location which is a use-after-free scenario. This likely causes corruption. The refcount crash seems like a likely outcome here.
The patch attempts to avoid a deadlock by decrementing `udc_usage` and waiting for completion on `ep0_done` but the design breaks the crucial link between the UDC core operation and the freeing of the USB request.
So, I'm thinking, if we can't hold the usage count during completion, and if we *can't* free `dev->req` before it's actually finished, what *can* we do? We *must* make sure to dequeue the request before the gadget is unbound.
I'm thinking, in `gadget_unbind()` if we see `dev->ep0_urb_queued` is true, call `usb_ep_dequeue(gadget->ep0, dev->req)` to force the request to complete, and then wait for *that* completion. However, this raises another question about race conditions with the ioctl.
A good example is gadgetfs; it seems to wait until `udc_usage` drops to zero, which includes the pending IOCTL calls.
So in summary: The use-after-free occurs because the request is freed before being dequeued. This needs to be addressed!
|
| 359/4 |
2026/07/29 09:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:ep0_read]
Results: map[SourceCode: 912: static ssize_t
913: ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
914: {
915: struct dev_data *dev = fd->private_data;
916: ssize_t retval;
917: enum ep0_state state;
918:
919: spin_lock_irq (&dev->lock);
920: if (dev->state <= STATE_DEV_OPENED) {
921: retval = -EINVAL;
922: goto done;
923: }
924:
925: /* report fd mode change before acting on it */
926: if (dev->setup_abort) {
927: dev->setup_abort = 0;
928: retval = -EIDRM;
929: goto done;
930: }
931:
932: /* control DATA stage */
933: if ((state = dev->state) == STATE_DEV_SETUP) {
934:
935: if (dev->setup_in) { /* stall IN */
936: VDEBUG(dev, "ep0in stall\n");
937: (void) usb_ep_set_halt (dev->gadget->ep0);
938: retval = -EL2HLT;
939: dev->state = STATE_DEV_CONNECTED;
940:
941: } else if (len == 0) { /* ack SET_CONFIGURATION etc */
942: struct usb_ep *ep = dev->gadget->ep0;
943: struct usb_request *req = dev->req;
944:
945: if ((retval = setup_req (ep, req, 0)) == 0) {
946: ++dev->udc_usage;
947: spin_unlock_irq (&dev->lock);
948: retval = usb_ep_queue (ep, req, GFP_KERNEL);
949: spin_lock_irq (&dev->lock);
950: --dev->udc_usage;
951: }
952: dev->state = STATE_DEV_CONNECTED;
953:
954: /* assume that was SET_CONFIGURATION */
955: if (dev->current_config) {
956: unsigned power;
957:
958: if (gadget_is_dualspeed(dev->gadget)
959: && (dev->gadget->speed
960: == USB_SPEED_HIGH))
961: power = dev->hs_config->bMaxPower;
962: else
963: power = dev->config->bMaxPower;
964: usb_gadget_vbus_draw(dev->gadget, 2 * power);
965: }
966:
967: } else { /* collect OUT data */
968: if ((fd->f_flags & O_NONBLOCK) != 0
969: && !dev->setup_out_ready) {
970: retval = -EAGAIN;
971: goto done;
972: }
973: spin_unlock_irq (&dev->lock);
974: retval = wait_event_interruptible (dev->wait,
975: dev->setup_out_ready != 0);
976:
977: /* FIXME state could change from under us */
978: spin_lock_irq (&dev->lock);
979: if (retval)
980: goto done;
981:
982: if (dev->state != STATE_DEV_SETUP) {
983: retval = -ECANCELED;
984: goto done;
985: }
986: dev->state = STATE_DEV_CONNECTED;
987:
988: if (dev->setup_out_error)
989: retval = -EIO;
990: else {
991: len = min (len, (size_t)dev->req->actual);
992: ++dev->udc_usage;
993: spin_unlock_irq(&dev->lock);
994: if (copy_to_user (buf, dev->req->buf, len))
995: retval = -EFAULT;
996: else
997: retval = len;
998: spin_lock_irq(&dev->lock);
999: --dev->udc_usage;
1000: clean_req (dev->gadget->ep0, dev->req);
1001: /* NOTE userspace can't yet choose to stall */
1002: }
1003: }
1004: goto done;
1005: }
1006:
1007: /* else normal: return event data */
1008: if (len < sizeof dev->event [0]) {
1009: retval = -EINVAL;
1010: goto done;
1011: }
1012: len -= len % sizeof (struct usb_gadgetfs_event);
1013: dev->usermode_setup = 1;
1014:
1015: scan:
1016: /* return queued events right away */
1017: if (dev->ev_next != 0) {
1018: unsigned i, n;
1019:
1020: n = len / sizeof (struct usb_gadgetfs_event);
1021: if (dev->ev_next < n)
1022: n = dev->ev_next;
1023:
1024: /* ep0 i/o has special semantics during STATE_DEV_SETUP */
1025: for (i = 0; i < n; i++) {
1026: if (dev->event [i].type == GADGETFS_SETUP) {
1027: dev->state = STATE_DEV_SETUP;
1028: n = i + 1;
1029: break;
1030: }
1031: }
1032: spin_unlock_irq (&dev->lock);
1033: len = n * sizeof (struct usb_gadgetfs_event);
1034: if (copy_to_user (buf, &dev->event, len))
1035: retval = -EFAULT;
1036: else
1037: retval = len;
1038: if (len > 0) {
1039: /* NOTE this doesn't guard against broken drivers;
1040: * concurrent ep0 readers may lose events.
1041: */
1042: spin_lock_irq (&dev->lock);
1043: if (dev->ev_next > n) {
1044: memmove(&dev->event[0], &dev->event[n],
1045: sizeof (struct usb_gadgetfs_event)
1046: * (dev->ev_next - n));
1047: }
1048: dev->ev_next -= n;
1049: spin_unlock_irq (&dev->lock);
1050: }
1051: return retval;
1052: }
1053: if (fd->f_flags & O_NONBLOCK) {
1054: retval = -EAGAIN;
1055: goto done;
1056: }
1057:
1058: switch (state) {
1059: default:
1060: DBG (dev, "fail %s, state %d\n", __func__, state);
1061: retval = -ESRCH;
1062: break;
1063: case STATE_DEV_UNCONNECTED:
1064: case STATE_DEV_CONNECTED:
1065: spin_unlock_irq (&dev->lock);
1066: DBG (dev, "%s wait\n", __func__);
1067:
1068: /* wait for events */
1069: retval = wait_event_interruptible (dev->wait,
1070: dev->ev_next != 0);
1071: if (retval < 0)
1072: return retval;
1073: spin_lock_irq (&dev->lock);
1074: goto scan;
1075: }
1076:
1077: done:
1078: spin_unlock_irq (&dev->lock);
1079: return retval;
1080: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 360/4 |
2026/07/29 09:19 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 101779
output: 41
thoughts: 704
Thoughts: **Analyzing the GadgetFS USB Request Handling**
Okay, so I'm diving into this `gadgetfs` code and it's hitting me: this thing **doesn't wait** for USB request completion! It just queues the request and then *immediately* returns. I'm seeing this pattern, specifically, in the `setup_req` and `usb_ep_queue` calls. It increments `dev->udc_usage` before queuing, calls `usb_ep_queue()`, and then decrements `dev->udc_usage`. No wait! The completion happens asynchronously, via an interrupt, and a waitqueue is woken up. So, `udc_usage` is only held during `usb_ep_queue()`! That's exactly how the old patch worked, too.
But here's where it gets interesting: if we're not waiting, how is `gadgetfs` preventing use-after-free scenarios on `dev->req`? My thought process is, how does this structure handle it? Okay, I see in `gadgetfs_unbind()` it does this: it waits on `dev->udc_usage`. Then it frees the request.
```c
/* wait for all the ioctls to finish */
while (dev->udc_usage > 0) {
spin_unlock_irq(&dev->lock);
usleep_range(1000, 2000);
spin_lock_irq(&dev->lock);
}
dev->gadget = NULL;
set_gadget_data(gadget, NULL);
spin_unlock_irq(&dev->lock);
ep0_req = dev->req;
dev->req = NULL;
/*
* ...
*/
if (ep0_req)
usb_ep_free_request(gadget->ep0, ep0_req);
```
Okay, so `gadgetfs` does free `ep0_req` in `gadgetfs_unbind()`! But, wait, what if the request is still queued? If it's still actively being used by the UDC core, freeing it now would be a classic use-after-free bug!
And then it hits me: `gadgetfs_unbind()` is called by `gadget_unbind_driver()`. And `gadget_unbind_driver()` calls `usb_gadget_udc_stop_locked()` AFTER the `unbind()` call! So the controller is still running when `gadgetfs_unbind()` frees `ep0_req`!
So, the next question is, is `gadgetfs` vulnerable to the same use-after-free issue? My analysis is that yes, absolutely it seems vulnerable. If a request is queued, and then `unbind` is called, `gadgetfs` is attempting to free that request while it's still being used, or is potentially queued to be used. I had to confirm it, of course.
I see that `gadgetfs` has `destroy_ep_files(dev);` before waiting for `udc_usage`. However, does that function actually call `usb_ep_dequeue()`? It only removes the file handles, which doesn't solve the core issue.
Wait, does it call `usb_ep_dequeue()` anywhere else in `gadgetfs_unbind()`? I need to re-examine the `gadgetfs_unbind()` code itself.
|
| 361/4 |
2026/07/29 09:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/inode.c Name:gadgetfs_unbind]
Results: map[SourceCode:1639: static void
1640: gadgetfs_unbind (struct usb_gadget *gadget)
1641: {
1642: struct dev_data *dev = get_gadget_data (gadget);
1643:
1644: DBG (dev, "%s\n", __func__);
1645:
1646: spin_lock_irq (&dev->lock);
1647: dev->state = STATE_DEV_UNBOUND;
1648: while (dev->udc_usage > 0) {
1649: spin_unlock_irq(&dev->lock);
1650: usleep_range(1000, 2000);
1651: spin_lock_irq(&dev->lock);
1652: }
1653: spin_unlock_irq (&dev->lock);
1654:
1655: destroy_ep_files (dev);
1656: gadget->ep0->driver_data = NULL;
1657: set_gadget_data (gadget, NULL);
1658:
1659: /* we've already been disconnected ... no i/o is active */
1660: if (dev->req)
1661: usb_ep_free_request (gadget->ep0, dev->req);
1662: DBG (dev, "%s done\n", __func__);
1663: put_dev (dev);
1664: }
SourceFile:drivers/usb/gadget/legacy/inode.c]
|
| 362/4 |
2026/07/29 09:20 |
llm |
patch-generator |
1m
Model:
Tokens:
input: 103725
output: 979
thoughts: 11361
Thoughts: **Analyzing and Fixing a USB Gadget Driver Use-After-Free Vulnerability**
Okay, so we've got this crash, and it's got me really digging into how the USB gadget driver is supposed to work. The initial thought was that a `disconnect` wasn't happening before `gadgetfs_unbind()`, but we've established that `usb_gadget_disconnect_locked()` *does* handle that, effectively canceling I/O, at least in the happy path. I was worried about the requests not being cancelled, but diving into the dummy UDC code confirms that `empty_req_queue()` is properly called, ensuring those requests get completed *before* `unbind()` is called. That meant freeing the request in `gadget_unbind()` should be safe, but obviously it wasn't.
Now, that refcount crash... and after a lot of head scratching, it started to click. My initial hunch was that maybe freeing `dev->req` in `gadget_unbind()` was the root cause, but digging deeper, there was no use-after-free for `dev->req` itself. And, after a lot of analysis, the refcount issue was related to race conditions that were not well understood and resolved.
Then it hit me: If we *don't* call `usb_ep_dequeue()` in `gadget_unbind()`, the USB request won't be completed, and might be accessed after `gadget_unbind()` is completed, potentially corrupting memory. The refcount crash was the consequence! That explained the intermittent nature of the crash. So, what if `gadget_unbind()` calls `usb_ep_dequeue()` directly? If that happened, what if the completion handler races with gadget unbind?
The answer is simple: We *have* to dequeue pending requests in `gadget_unbind()` to prevent the UDC core from potentially accessing them after they're freed. And if we do, the UDC core will complete the request. But, `usb_ep_dequeue()` is not supposed to be called while the driver has the lock. So `gadget_unbind` needs to release `dev->lock` before calling `usb_ep_dequeue()` and set the device state to `STATE_DEV_FAILED` which prevent new requests to be queued. This ensures that no new requests are queued. Then we must reacquire it before freeing the requests. The code can then be updated to wait for the UDC core to drop the device usage count, and only THEN free. That, plus dropping the request from the UDC core would guarantee safety, by dequeuing all pending requests and only freeing them after ensuring that they aren't queued to a controller any more. This resolves the refcount crash.
|
| 363/4 |
2026/07/29 09:21 |
tool |
codeeditor |
0m
Args: map[CurrentCode: dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(dev->gadget->ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} NewCode: dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
} else {
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
}
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 364/4 |
2026/07/29 09:21 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 116654
output: 910
thoughts: 11
Thoughts: Okay, I'm ready to dive into this. Let's see... I need to summarize the thoughts, putting myself in the position of processing this `raw_process_ep_io()` function. Here's my take:
**Analyzing the Raw Process I/O Function**
Alright, I'm looking at this `raw_process_ep_io()` function. My immediate thought is, what are we trying to achieve here at the lowest level? This likely deals with the fundamental mechanics of input/output (I/O) operations, probably related to a specific endpoint or communication channel. The "raw" implies it's a direct, potentially unbuffered, method of handling the data flow. I need to keep in mind the potential for performance bottlenecks and low-level data corruption if handled incorrectly. I need to consider the type of data being processed, its format, and the potential implications of any error conditions that could arise during transfer. Further, I should see if there are any error handling procedures or how exceptions are managed. I'm going to look for any clues about the underlying hardware or system architecture. Is this optimized for a specific device, or is it a more generic implementation? Knowing that will provide context and insight. Also, I'm thinking about the implications for concurrency. How does this fit into the broader system architecture? And will it become a bottleneck or be part of a solution?
|
| 365/4 |
2026/07/29 09:21 |
tool |
codeeditor |
0m
Args: map[CurrentCode: ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(&dev->gadget->dev, "wait interrupted\n");
usb_ep_dequeue(ep->ep, ep->req);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} NewCode: ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(usb_ep, ep->req);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
} else {
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 366/4 |
2026/07/29 09:21 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 117872
output: 349
thoughts: 11
Thoughts: Okay, here's my interpretation of that.
**Disabling the Endpoint via raw_ioctl_ep_disable()**
Alright, let's break this down. We're talking about disabling an endpoint, likely within a low-level or embedded system β considering the use of `ioctl` and the "raw" prefix. My understanding, as a seasoned pro in this area, is that we're diving into direct hardware control, bypassing the typical driver layers for this specific endpoint. We're getting right down to the nitty-gritty. The 'ep' indicates an endpoint, which implies some kind of communication channel or interface. This could be USB, a custom hardware interface, or something similar. Disabling it is crucial because it means we're putting this specific connection or stream of data into a dormant state. This usually means that data transfer will be halted, associated interrupts will be masked or disabled, and the hardware resources the endpoint was using will be available, and maybe the driver will remove the enpoint. I imagine this call is very specific in its purpose. Now the immediate question is: Why are we doing this? Is it for power management, error recovery, graceful shutdown, or a safety-critical reason? The context of the larger system dictates the reason, but the `raw_ioctl_ep_disable()` function is the mechanism to get the job done. I'd need to consider the system's interrupt handling, error checking, and potential race conditions in that context.
|
| 367/4 |
2026/07/29 09:21 |
tool |
codeeditor |
0m
Args: map[CurrentCode: dev->eps[i].disabling = true;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
NewCode: dev->eps[i].disabling = true;
dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
dev->udc_usage--;
if (!dev->udc_usage && dev->unbind_completion)
complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 368/4 |
2026/07/29 09:21 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 118548
output: 690
thoughts: 8
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Unbinding the Gadget: A Deep Dive**
Alright, I'm at `gadget_unbind()`. This is where things get interesting, right? I'm thinking about the implications of *this* specific call within the broader driver framework. Weβre clearly in the cleanup phase β the device is going away, or at least being detached. My focus is immediately drawn to the resources managed by the gadget driver. What's the state of the endpoints? Are they properly flushed? Are the DMA buffers deallocated correctly? I need to ensure a clean slate, to avoid any lingering conflicts or memory leaks.
My mind is running through the potential error scenarios. What happens if a transfer is still in progress? How robust is the cancellation mechanism? Is there a race condition here? I need to review the locking strategy employed within this function closely β mutexes, spinlocks, anything that could cause a deadlock if the sequence isn't precise.
And then, beyond the core functionality of the driver itself, I'm considering the higher-level impact. Are there any other subsystems, like the USB stack or perhaps even the kernel's memory management, that rely on the state of this gadget? I need to meticulously verify that all dependencies are cleanly severed. I'm thinking about the potential for dangling pointers or use-after-free bugs if this unbind isn't executed perfectly. It's time to dig deep into the code and start looking for edge cases. I'm considering the potential impact on power management too, and whether any states need to be saved or restored. This is a critical step, and I need to be absolutely certain of its correctness.
|
| 369/4 |
2026/07/29 09:22 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
} NewCode:static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(done);
int i;
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
if (dev->ep0_urb_queued) {
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(gadget->ep0, dev->req);
spin_lock_irqsave(&dev->lock, flags);
}
for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
spin_lock_irqsave(&dev->lock, flags);
}
}
if (dev->udc_usage > 0) {
dev->unbind_completion = &done;
spin_unlock_irqrestore(&dev->lock, flags);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
}
dev->gadget = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state == STATE_EP_DISABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
}
if (dev->req) {
usb_ep_free_request(gadget->ep0, dev->req);
dev->req = NULL;
}
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 370/4 |
2026/07/29 09:22 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 119624
output: 261
thoughts: 8
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing the `gadget_bind()` Function**
Right, so we're looking at `gadget_bind()`. Immediately, my mind starts to dissect it. I'm assuming, given the name, this function is responsible for linking a "gadget" β whatever that's defined as in this context β to something else. The "bind" suggests a connection, perhaps a resource allocation, or a handle assignment. I'll need to know the specific system to really understand what's happening. Is this kernel-level, user-space, a specific framework like a GUI toolkit, or something else entirely? The type of "gadget" is also super important. Is it hardware, a software component, a data structure, or some other object.
My initial questions are all about scope and purpose. What's the interface look like? What parameters does it take? Are there side effects? Does it return a handle, a status code, or something else? Understanding the signature and return type is my immediate priority to start forming my mental model of this function's role in the larger picture. I want to know about potential failure modes; What error conditions are handled, and how? Is there a corresponding `gadget_unbind()` or cleanup mechanism? Security implications should I consider, or am I working at the level where that's already abstracted? I need to analyze this function within its larger ecosystem.
|
| 371/4 |
2026/07/29 09:22 |
tool |
codeeditor |
0m
Args: map[CurrentCode: ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
set_gadget_data(gadget, NULL);
return ret;
} NewCode: ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->gadget = NULL;
dev->req = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 372/4 |
2026/07/29 09:22 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 120219
output: 502
thoughts: 7
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Handling Device Freeing Operations**
Alright, let's break down this `dev_free()` operation. My initial thought process is always: what exactly is this device, and what state is it in? Understanding the context β is this a driver context, a user-space context, a specific subsystem? β dictates the approach I'll take. I need to be *meticulous* about this because incorrect freeing, especially with shared resources, can lead to cascading failures and kernel panics.
First, I'm mentally checking for resource dependencies. Are there any outstanding allocations associated with the device that need to be explicitly freed before I proceed? Buffers, memory maps, DMA mappings, interrupts β these all need to be accounted for. I'm thinking about the order of operations here; releasing resources in the wrong sequence can introduce memory corruption or race conditions. I'm going through the data structures associated with the device, mentally reviewing any reference counts, and ensuring that no other parts of the system are actively using the resources I intend to free.
Next, I'm considering the potential impact on other devices or subsystems. Does this device share any resources with others? If so, I need to coordinate the freeing process carefully to avoid deadlocks or data inconsistencies. Locking primitives β spinlocks, mutexes, read-write locks β are constantly playing in the background of my thought process here. I'm visualizing the potential locking order and looking for any opportunities for optimization or potential contention.
Finally, I'm focusing on the actual freeing mechanism. Are we dealing with `kfree()`, `vfree()`, or something more specialized like a DMA unmap? I'm double-checking the code to ensure the correct free function is being used for the allocated memory. Iβm thinking about what happens if `dev_free` fails, and how I'm handling those errors. Graceful degradation, error reporting, maybe trying to release some resources and then return to an initialized state, if possible.
In any situation, I'm always thinking about being robust and protecting the kernel from any issues.
|
| 373/4 |
2026/07/29 09:22 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
if (dev->req) {
if (dev->ep0_urb_queued)
usb_ep_dequeue(dev->gadget->ep0, dev->req);
usb_ep_free_request(dev->gadget->ep0, dev->req);
}
raw_event_queue_destroy(&dev->queue);
for (i = 0; i < dev->eps_num; i++) {
if (dev->eps[i].state == STATE_EP_DISABLED)
continue;
usb_ep_disable(dev->eps[i].ep);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
}
kfree(dev);
} NewCode:static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
raw_event_queue_destroy(&dev->queue);
kfree(dev);
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 374/4 |
2026/07/29 09:22 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 121195
output: 165
thoughts: 7
Thoughts: Okay, here's my interpretation of that thought process, summarized as you requested:
**Digging into the `struct raw_dev` Structure**
Alright, let's see. "Now `struct raw_dev`..." That's where we're focusing, huh? I imagine the author (or, in this case, *me*, thinking it) has reached a point in the code or analysis where this particular structure, `raw_dev`, has become central to the current investigation. It's the lynchpin, the key piece of data governing whatever the current task is β maybe device management, hardware interaction, data transfer, something along those lines.
I immediately start mentally running through what I already know, or *should* know, about `raw_dev`. What is its purpose? What data does it hold? I'm probably thinking about its key fields. Things like the device identifier, memory addresses, interrupt information, access control data β all the critical bits and pieces that would define how the system interacts with the underlying hardware or resource. I'm likely trying to refresh my memory, mentally reconstruct the structure declaration, and understand how those elements relate to the larger picture. I want to see how this fits into the overall architecture, how it interacts with other structures and functions. It may be part of a larger chain of structures and functions, making it the focal point of data.
I'm probably asking myself the following questions: "Is there anything unusual or particularly complex about its definition? Are there potential pitfalls or areas where errors are common?" and "What's the context? What problem are we trying to solve where this structure plays a role?" Is this a standard structure, or is it custom? Is it specific to a particular device or type of device? Is it well documented?
Essentially, this is a pause, a moment to get my bearings and make sure I fully understand the foundational building block for the next set of operations or analyses. Time to get intimately acquainted with `raw_dev` before proceeding.
|
| 375/4 |
2026/07/29 09:22 |
tool |
codeeditor |
0m
Args: map[CurrentCode: bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
bool ep0_in_pending;
bool ep0_out_pending; NewCode: bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
int udc_usage;
struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending; SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 376/4 |
2026/07/29 09:22 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 121794
output: 334
thoughts: 11
Thoughts: Okay, here's my interpretation of that:
**My Deep Dive into `raw_ioctl_event_fetch()`**
Alright, let's dissect this. "Now `raw_ioctl_event_fetch()`" - this signals we're shifting our focus to a specific function or process. It's like saying, "Okay, we've covered the broader context, now we're zooming in on *this* particular piece of the puzzle."
My mind immediately starts ticking through the implications. I'm thinking, "Okay, `raw_ioctl_event_fetch()`... is this related to a specific hardware interface, perhaps? IOCTL suggests device control, data transfer or status retrieval using the `ioctl()` system call or something similar. The 'raw' part makes me believe it's interacting at a relatively low level β less abstraction, closer to the metal. Likely directly interfacing with the device driver."
I begin speculating further. What kind of events are we expecting to fetch? Are we talking about interrupt signals, status changes, perhaps error reports, data ready notifications, or something else entirely? The function name strongly implies that it's retrieving information about past or pending events related to this specific device. I'm wondering about the data structure that will hold these events. Will it be a defined structure, or possibly a generic buffer? Error handling is, of course, a paramount consideration. How are errors indicated?
I need to know the context of this function. What other functions does it interact with? Is this part of a larger, concurrent processing flow? This is all related to device driver design, kernel interaction, and possibly real-time systems. I need to be thinking about potential race conditions, locking mechanisms, and the overall stability of the system. I also need to understand the function signature: what are the inputs, what are the outputs? That will tell the real story here.
|
| 377/4 |
2026/07/29 09:22 |
tool |
codeeditor |
0m
Error:
Args: map[CurrentCode: event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return PTR_ERR(event);
} NewCode: event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return PTR_ERR(event);
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 378/4 |
2026/07/29 09:22 |
llm |
patch-generator |
1m
Model:
Tokens:
input: 122562
output: 46
thoughts: 31
Thoughts: Okay, let's dive in. Here's my thought process as I approach this:
**Analyzing `raw_ioctl_event_fetch()` in `raw_gadget.c`**
Alright, the task is to understand the event fetching within the legacy raw gadget driver. Specifically, I need to focus on the `raw_ioctl_event_fetch()` function in `drivers/usb/gadget/legacy/raw_gadget.c`. This suggests we're dealing with an older, possibly less refined, method of handling USB gadget events. I need to be prepared for some potentially clunky code, but that's alright, legacy code has its own charms. My immediate assumption is that this function will be the gateway to retrieving data or status related to USB transfers or device activity using an ioctl interface. I need to carefully examine how it's implemented and how it interacts with the underlying USB gadget core. I'll be looking for potential race conditions, error handling strategies, and how data is marshaled between the kernel and userspace. Is this function atomic? Does it use locks? What about buffer management? Hopefully the comments are decent. I'm expecting this to be the point where the ioctl system calls tie into the USB gadget functionality. I'll need to figure out what kinds of events are exposed via the ioctl interface, and how this function translates them for user space access. This will likely involve looking at the associated data structures and any associated locking primitives. Let's dig in and see what treasures (or potential issues) await.
|
| 379/4 |
2026/07/29 09:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/legacy/raw_gadget.c Name:raw_ioctl_event_fetch]
Results: map[SourceCode: 615: static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
616: {
617: struct usb_raw_event arg;
618: unsigned long flags;
619: struct usb_raw_event *event;
620: uint32_t length;
621:
622: if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
623: return -EFAULT;
624:
625: spin_lock_irqsave(&dev->lock, flags);
626: if (dev->state != STATE_DEV_RUNNING) {
627: dev_dbg(dev->dev, "fail, device is not running\n");
628: spin_unlock_irqrestore(&dev->lock, flags);
629: return -EINVAL;
630: }
631: if (!dev->gadget) {
632: dev_dbg(dev->dev, "fail, gadget is not bound\n");
633: spin_unlock_irqrestore(&dev->lock, flags);
634: return -EBUSY;
635: }
636: spin_unlock_irqrestore(&dev->lock, flags);
637:
638: event = raw_event_queue_fetch(&dev->queue);
639: if (PTR_ERR(event) == -EINTR) {
640: dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
641: return -EINTR;
642: }
643: if (IS_ERR(event)) {
644: dev_err(&dev->gadget->dev, "failed to fetch event\n");
645: spin_lock_irqsave(&dev->lock, flags);
646: dev->state = STATE_DEV_FAILED;
647: spin_unlock_irqrestore(&dev->lock, flags);
648: return -ENODEV;
649: }
650: length = min(arg.length, event->length);
651: if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
652: kfree(event);
653: return -EFAULT;
654: }
655:
656: kfree(event);
657: return 0;
658: }
SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
|
| 380/4 |
2026/07/29 09:23 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 123694
output: 332
thoughts: 21
Thoughts: Okay, here's the summary, rendered as a first-person thought process, keeping in mind the user is an expert:
**Refining the Error Handling**
Okay, so I was looking at that snippet of code, and I immediately thought, "wait a minute... why is `return -ENODEV;` in there instead of `return PTR_ERR(event);`?" I mean, it's about returning an error from a device. We *could* just return the generic -ENODEV, but that loses a potentially crucial piece of information. The event pointer has valuable context, right? Using `PTR_ERR(event)` is far more informative. It lets the caller understand that something specifically went wrong with *that particular event*, potentially allowing for more targeted error handling or debugging. It also opens the door to returning a more specific error code later, instead of a generic -ENODEV. It feels like a quick fix might have been implemented, that will be short sighted down the line. We need to be specific for maintainability.
|
| 381/4 |
2026/07/29 09:23 |
tool |
codeeditor |
0m
Args: map[CurrentCode: event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(&dev->gadget->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
} NewCode: event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
} SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 382/4 |
2026/07/29 09:23 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 124273
output: 15
thoughts: 8
Thoughts: Okay, let's dive into this patch diff. I need to get a clear understanding of the changes, especially how they might impact the existing system architecture and, critically, any performance implications. I'm going to focus first on the scope of the modifications β which modules, which functions are affected? Are we talking about a minor tweak or a fundamental shift in behavior? Next, I want to trace the data flow through these changes. What inputs are modified? What outputs are generated? Are there any potential concurrency issues introduced? I need to look for any hidden dependencies, assumptions, or unexpected side effects. Are they following the established coding standards, naming conventions, and best practices relevant to this project? Iβll specifically be searching for any potential regressions: did they inadvertently break anything that used to work? What about testing? Are there sufficient unit tests, integration tests, and performance tests for all the changed parts? Are those tests appropriate and complete? Finally, I'll need to think about the long-term maintainability. Are the changes well-documented and easy to understand for future developers? Is this the most efficient, elegant, and robust solution to the problem? I'll probably also need to consider any potential security vulnerabilities, since this looks like an area we need to be particularly vigilant on.
|
| 383/4 |
2026/07/29 09:23 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..dbbe55570 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -160,28 +160,30 @@ enum dev_state {
struct raw_dev {
struct kref count;
spinlock_t lock;
const char *udc_name;
struct usb_gadget_driver driver;
/* Reference to misc device: */
struct device *dev;
/* Make driver names unique */
int driver_id_number;
/* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
int eps_num;
struct completion ep0_done;
struct raw_event_queue queue;
};
@@ -198,41 +200,27 @@ static struct raw_dev *dev_new(void)
spin_lock_init(&dev->lock);
init_completion(&dev->ep0_done);
raw_event_queue_init(&dev->queue);
dev->driver_id_number = -1;
return dev;
}
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
/*----------------------------------------------------------------------*/
static int raw_queue_event(struct raw_dev *dev,
enum usb_raw_event_type type, size_t length, const void *data)
{
int ret = 0;
unsigned long flags;
@@ -281,60 +269,106 @@ static u8 get_ep_addr(const char *name)
static int gadget_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int ret = 0, i = 0;
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
struct usb_request *req;
struct usb_ep *ep;
unsigned long flags;
if (strcmp(gadget->name, dev->udc_name) != 0)
return -ENODEV;
set_gadget_data(gadget, dev);
req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!req) {
dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
set_gadget_data(gadget, NULL);
return -ENOMEM;
}
spin_lock_irqsave(&dev->lock, flags);
dev->req = req;
dev->req->context = dev;
dev->req->complete = gadget_ep0_complete;
dev->gadget = gadget;
gadget_for_each_ep(ep, dev->gadget) {
dev->eps[i].ep = ep;
dev->eps[i].addr = get_ep_addr(ep->name);
dev->eps[i].state = STATE_EP_DISABLED;
i++;
}
dev->eps_num = i;
spin_unlock_irqrestore(&dev->lock, flags);
dev_dbg(&gadget->dev, "gadget connected\n");
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
}
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
static int gadget_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
int ret = 0;
@@ -615,44 +649,44 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
{
struct usb_raw_event arg;
unsigned long flags;
struct usb_raw_event *event;
uint32_t length;
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
return -EFAULT;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EINVAL;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
kfree(event);
return -EFAULT;
}
kfree(event);
return 0;
}
@@ -683,80 +717,97 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->ep0_urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !dev->ep0_in_pending) ||
(!in && !dev->ep0_out_pending)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EBUSY;
goto out_unlock;
}
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
void *data;
struct usb_raw_ep_io io;
@@ -920,54 +971,58 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (i < 0 || i >= dev->eps_num) {
dev_dbg(dev->dev, "fail, invalid endpoint\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->eps[i].state == STATE_EP_DISABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].disabling) {
dev_dbg(&dev->gadget->dev,
"fail, disable already in progress\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -1058,91 +1113,108 @@ static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
struct raw_ep *ep;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (io->ep >= dev->eps_num) {
dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
ret = -EINVAL;
goto out_unlock;
}
ep = &dev->eps[io->ep];
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->disabling) {
dev_dbg(&dev->gadget->dev,
"fail, endpoint is already being disabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if (in != usb_endpoint_dir_in(ep->ep->desc)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EINVAL;
goto out_unlock;
}
ep->dev = dev;
ep->req->context = &done;
ep->req->complete = gadget_ep_complete;
ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
char *data;
struct usb_raw_ep_io io;
]
|
| 384/4 |
2026/07/29 09:23 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 130669
output: 655
thoughts: 173
Thoughts: Here's my assessment of the patch, distilled into my current thinking:
**Patch Analysis: A Comprehensive Fix**
Okay, so I've just finished reviewing this patch, and my initial impression is that it's spot on. The core issue here was a pretty nasty set of problems related to resource management and race conditions in the USB gadget driver. Looking closely, I can see that it tackles the use-after-free scenarios with precision. Specifically, it explicitly *dequeues* `dev->req` and `dev->eps[i].req` within `gadget_unbind()`. Then it waits for `udc_usage` to decrement down to zero. That makes perfect sense; clear and concise!
The deadlock, which I've seen rear its ugly head in similar contexts, is neatly sidestepped by *not* holding `udc_usage` while waiting for completion. Good call. Thatβs probably the key to avoiding it in the first place.
Moreover, it addresses that annoying `refcount_t: addition on 0` crash, I remember that one! Excellent, the patch prevents the UDC core from even *attempting* to access freed requests. And on the memory leak front, it's cleaning up `dev->eps[i].req` in `gadget_unbind()`. Perfect place to deal with it.
Finally, the added cleanup to the error path in `gadget_bind()` catches and resolves situations by correctly cleaning up `dev->req` and `dev->gadget`. Excellent.
From my perspective, this patch is comprehensive, well-reasoned, and addresses all the critical vulnerabilities in a logical and elegant way. I'm satisfied. I can now proceed to compose my reply.
|
| 385/3 |
2026/07/29 09:24 |
action |
test-patch |
14m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..dbbe55570 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,47 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +671,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +757,36 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -750,6 +798,9 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
@@ -956,11 +1007,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1164,36 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
@@ -1136,6 +1205,9 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
TestError:]
|
| 386/1 |
2026/07/29 09:38 |
action |
apply-patch |
0m
Results:
|
| 387/1 |
2026/07/29 09:38 |
action |
init-style-items |
0m
Results:
|
| 388/1 |
2026/07/29 09:38 |
loop |
|
13m
|
| 389/2 |
2026/07/29 09:38 |
iteration |
0 |
13m
|
| 390/3 |
2026/07/29 09:38 |
agent |
patch-formatter |
1m
Model:
Instruction:
You are an expert Linux kernel developer tasked with formatting a kernel patch.
Your objective is purely formatting: you must ensure the patch complies with the kernel's coding style,
conforms to the surrounding code rules, and passes checkpatch.pl, while preserving the code logic exactly as it is.
You should stop once the requested formatting changes are done and checkpatch.pl is happy.
Do not question the requested changes unless they are obviously wrong.
If the code already conforms to the requested changes, surrounding code rules,
and checkpatch.pl is happy, you should just finish your task.
WARNING: The clang-format tool may break the formatting of the surrounding code (like manual alignment).
Use it with caution. We want to make the change fit into the existing formatting as much as possible.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The current patch diff is:
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..dbbe55570 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,47 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +671,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +757,36 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -750,6 +798,9 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
@@ -956,11 +1007,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1164,36 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
@@ -1136,6 +1205,9 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
Use the provided tools to format the patch.
Reply: I have successfully formatted the patch to comply with the Linux kernel's coding style. The blank lines after the local variable declarations in both `raw_process_ep0_io` and `raw_process_ep_io` have been added.
The patch now passes `checkpatch.pl` with **0 errors and 0 warnings**.
|
| 391/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 7654
output: 9
thoughts: 196
Thoughts: **Kernel Patch Formatting - Initial Thoughts**
Okay, so I've got this kernel patch I need to format properly. First things first, let's see where we stand with the current style. The obvious starting point is to run `checkpatch` on it. That should give me a quick overview of any glaring style violations.
Wait a second, before I jump straight in, let's remind ourselves of what tools are actually available. I've got `checkpatch`, naturally. Then there's `clang-format`, which is useful, but only on specific files. Oh, there's `codeeditor` β that could be handy to make the fixes. I also have the standard git utilities like `patch-diff`, `git-log`, `git-show`, and `git-blame`. And, of course, the ever-useful search tools like `codesearch-dir-index`, `codesearch-file-index`, and such. Ah, and `codeexpert`, which could be useful.
Right, back to the formatting. Ultimately, I'll go with `checkpatch` first, see what comes back, and then start making targeted adjustments using the appropriate tools.
|
| 392/4 |
2026/07/29 09:38 |
tool |
checkpatch |
0m
Results: map[Output:WARNING: Missing a blank line after declarations
#144: FILE: drivers/usb/gadget/legacy/raw_gadget.c:779:
+ struct usb_ep *ep0 = dev->gadget->ep0;
+ spin_unlock_irqrestore(&dev->lock, flags);
WARNING: Missing a blank line after declarations
#212: FILE: drivers/usb/gadget/legacy/raw_gadget.c:1186:
+ struct usb_ep *usb_ep = ep->ep;
+ spin_unlock_irqrestore(&dev->lock, flags);
total: 0 errors, 2 warnings, 221 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
Your patch has style problems, please review.
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
]
|
| 393/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 8356
output: 87
thoughts: 198
Thoughts: **Addressing Checkpatch Warnings in USB Gadget Code**
Okay, so Checkpatch is flagging me with a couple of warnings related to missing blank lines after declarations. Specifically, it's complaining about lines 779 and 1186 in `drivers/usb/gadget/legacy/raw_gadget.c`. I see the snippets:
```c
+ struct usb_ep *ep0 = dev->gadget->ep0;
+ spin_unlock_irqrestore(&dev->lock, flags);
```
and
```c
+ struct usb_ep *usb_ep = ep->ep;
+ spin_unlock_irqrestore(&dev->lock, flags);
```
Right, the classic "missing newline" issue. Before I jump in to fix it blindly, I need context. I'll need to understand what's happening around those lines. Instead of just looking at these lines in isolation, I need to understand the function these are within. So, let's pull up the file and examine the surrounding code. I'll use something like `read-file` or `codesearch-definition-source` to get a good look at the code. This will help me understand why those declarations are there and whether adding the blank line is the right thing to do based on the local style. Need to maintain consistency, of course. Let's see...
|
| 394/4 |
2026/07/29 09:38 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:760 LineCount:40]
Results: map[Contents: 760: int ret = 0;
761: void *data;
762: struct usb_raw_ep_io io;
763:
764: data = raw_alloc_io_data(&io, (void __user *)value, true);
765: if (IS_ERR(data))
766: return PTR_ERR(data);
767: ret = raw_process_ep0_io(dev, &io, data, true);
768: kfree(data);
769: return ret;
770: }
771:
772: static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
773: {
774: int ret = 0;
775: void *data;
776: struct usb_raw_ep_io io;
777: unsigned int length;
778:
779: data = raw_alloc_io_data(&io, (void __user *)value, false);
780: if (IS_ERR(data))
781: return PTR_ERR(data);
782: ret = raw_process_ep0_io(dev, &io, data, false);
783: if (ret < 0)
784: goto free;
785:
786: length = min_t(unsigned int, io.length, ret);
787: if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
788: ret = -EFAULT;
789: else
790: ret = length;
791: free:
792: kfree(data);
793: return ret;
794: }
795:
796: static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
797: {
798: int ret = 0;
799: unsigned long flags;
]
|
| 395/4 |
2026/07/29 09:38 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:1170 LineCount:40]
Results: map[Contents:1170: goto free;
1171:
1172: length = min_t(unsigned int, io.length, ret);
1173: if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
1174: ret = -EFAULT;
1175: else
1176: ret = length;
1177: free:
1178: kfree(data);
1179: return ret;
1180: }
1181:
1182: static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
1183: {
1184: int ret = 0;
1185: unsigned long flags;
1186:
1187: if (value)
1188: return -EINVAL;
1189: spin_lock_irqsave(&dev->lock, flags);
1190: if (dev->state != STATE_DEV_RUNNING) {
1191: dev_dbg(dev->dev, "fail, device is not running\n");
1192: ret = -EINVAL;
1193: goto out_unlock;
1194: }
1195: if (!dev->gadget) {
1196: dev_dbg(dev->dev, "fail, gadget is not bound\n");
1197: ret = -EBUSY;
1198: goto out_unlock;
1199: }
1200: usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
1201:
1202: out_unlock:
1203: spin_unlock_irqrestore(&dev->lock, flags);
1204: return ret;
1205: }
1206:
1207: static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
1208: {
1209: int ret = 0;
]
|
| 396/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 10087
output: 42
thoughts: 119
Thoughts: **Navigating the Codebase After a Patch**
Okay, so I'm looking at this diff and immediately I'm noticing a discrepancy with the line numbers. Right, because the patch has, obviously, changed the line numbers in the `drivers/usb/gadget/legacy/raw_gadget.c` file. I need to pinpoint the exact locations of these two crucial lines of code: `struct usb_ep *ep0 = dev->gadget->ep0;` and `struct usb_ep *usb_ep = ep->ep;`.
I can't rely on the old line numbers from the original file, so I need a more reliable method to identify them post-patch. I'll reach for `grepper`. I'll use it to search for the specific lines, or at least the unique structure declarations of `struct usb_ep *ep0` or `struct usb_ep *usb_ep`. That should be the quickest way to zero in on the relevant code blocks within the updated file.
|
| 397/4 |
2026/07/29 09:38 |
tool |
grepper |
0m
Error:
Args: map[Expression:struct usb_ep \*ep0 PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 398/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 10478
output: 37
thoughts: 127
Thoughts: **Debugging a `grepper` Mystery**
Okay, so `grepper` returned "no matches." That's frustrating. My first instinct is, was the patch actually applied? Maybe it's not yet in the file, or perhaps there's some escaping issue with backslashes that's throwing `grepper` off. Let's start by verifying with `patch-diff`. I always do a quick sanity check to see the modifications before getting too deep.
Ah, hang on a second. The prompt *literally* says "The current patch diff is: ...". Right. The patch is *already* applied in the workspace. That makes things easier, and I should have paid more attention to the prompt!
Okay, so if the patch is applied, and grepper missed it, now it's a matter of the exact search terms. Let me try a more specific search in `drivers/usb/gadget/legacy/raw_gadget.c`. I'll look for the `ep0 = dev->gadget->ep0` string directly. Alternatively, I can just search for `ep0` and scan the results. That should zero in on the issue.
|
| 399/4 |
2026/07/29 09:38 |
tool |
grepper |
0m
Args: map[Expression:ep0 PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=160=struct raw_dev {
--
drivers/usb/gadget/legacy/raw_gadget.c-177- struct usb_request *req;
drivers/usb/gadget/legacy/raw_gadget.c:178: bool ep0_in_pending;
drivers/usb/gadget/legacy/raw_gadget.c:179: bool ep0_out_pending;
drivers/usb/gadget/legacy/raw_gadget.c:180: bool ep0_urb_queued;
drivers/usb/gadget/legacy/raw_gadget.c:181: ssize_t ep0_status;
drivers/usb/gadget/legacy/raw_gadget.c-182- struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
--
drivers/usb/gadget/legacy/raw_gadget.c-184-
drivers/usb/gadget/legacy/raw_gadget.c:185: struct completion ep0_done;
drivers/usb/gadget/legacy/raw_gadget.c-186- struct raw_event_queue queue;
--
drivers/usb/gadget/legacy/raw_gadget.c=189=static struct raw_dev *dev_new(void)
--
drivers/usb/gadget/legacy/raw_gadget.c-198- spin_lock_init(&dev->lock);
drivers/usb/gadget/legacy/raw_gadget.c:199: init_completion(&dev->ep0_done);
drivers/usb/gadget/legacy/raw_gadget.c-200- raw_event_queue_init(&dev->queue);
--
drivers/usb/gadget/legacy/raw_gadget.c=205=static void dev_free(struct kref *kref)
--
drivers/usb/gadget/legacy/raw_gadget.c-215- if (dev->req) {
drivers/usb/gadget/legacy/raw_gadget.c:216: if (dev->ep0_urb_queued)
drivers/usb/gadget/legacy/raw_gadget.c:217: usb_ep_dequeue(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c:218: usb_ep_free_request(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c-219- }
--
drivers/usb/gadget/legacy/raw_gadget.c=234=static int raw_queue_event(struct raw_dev *dev,
--
drivers/usb/gadget/legacy/raw_gadget.c-248-
drivers/usb/gadget/legacy/raw_gadget.c:249:static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
drivers/usb/gadget/legacy/raw_gadget.c-250-{
--
drivers/usb/gadget/legacy/raw_gadget.c-255- if (req->status)
drivers/usb/gadget/legacy/raw_gadget.c:256: dev->ep0_status = req->status;
drivers/usb/gadget/legacy/raw_gadget.c-257- else
drivers/usb/gadget/legacy/raw_gadget.c:258: dev->ep0_status = req->actual;
drivers/usb/gadget/legacy/raw_gadget.c:259: if (dev->ep0_in_pending)
drivers/usb/gadget/legacy/raw_gadget.c:260: dev->ep0_in_pending = false;
drivers/usb/gadget/legacy/raw_gadget.c-261- else
drivers/usb/gadget/legacy/raw_gadget.c:262: dev->ep0_out_pending = false;
drivers/usb/gadget/legacy/raw_gadget.c-263- spin_unlock_irqrestore(&dev->lock, flags);
drivers/usb/gadget/legacy/raw_gadget.c-264-
drivers/usb/gadget/legacy/raw_gadget.c:265: complete(&dev->ep0_done);
drivers/usb/gadget/legacy/raw_gadget.c-266-}
--
drivers/usb/gadget/legacy/raw_gadget.c=281=static int gadget_bind(struct usb_gadget *gadget,
--
drivers/usb/gadget/legacy/raw_gadget.c-293- set_gadget_data(gadget, dev);
drivers/usb/gadget/legacy/raw_gadget.c:294: req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
drivers/usb/gadget/legacy/raw_gadget.c-295- if (!req) {
--
drivers/usb/gadget/legacy/raw_gadget.c-303- dev->req->context = dev;
drivers/usb/gadget/legacy/raw_gadget.c:304: dev->req->complete = gadget_ep0_complete;
drivers/usb/gadget/legacy/raw_gadget.c-305- dev->gadget = gadget;
--
drivers/usb/gadget/legacy/raw_gadget.c=337=static int gadget_setup(struct usb_gadget *gadget,
--
drivers/usb/gadget/legacy/raw_gadget.c-349- }
drivers/usb/gadget/legacy/raw_gadget.c:350: if (dev->ep0_in_pending || dev->ep0_out_pending) {
drivers/usb/gadget/legacy/raw_gadget.c-351- dev_dbg(&gadget->dev, "stalling, request already pending\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-355- if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
drivers/usb/gadget/legacy/raw_gadget.c:356: dev->ep0_in_pending = true;
drivers/usb/gadget/legacy/raw_gadget.c-357- else
drivers/usb/gadget/legacy/raw_gadget.c:358: dev->ep0_out_pending = true;
drivers/usb/gadget/legacy/raw_gadget.c-359- spin_unlock_irqrestore(&dev->lock, flags);
--
drivers/usb/gadget/legacy/raw_gadget.c=660=static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
--
drivers/usb/gadget/legacy/raw_gadget.c-682-
drivers/usb/gadget/legacy/raw_gadget.c:683:static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
drivers/usb/gadget/legacy/raw_gadget.c-684- void *data, bool in)
--
drivers/usb/gadget/legacy/raw_gadget.c-699- }
drivers/usb/gadget/legacy/raw_gadget.c:700: if (dev->ep0_urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c-701- dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-704- }
drivers/usb/gadget/legacy/raw_gadget.c:705: if ((in && !dev->ep0_in_pending) ||
drivers/usb/gadget/legacy/raw_gadget.c:706: (!in && !dev->ep0_out_pending)) {
drivers/usb/gadget/legacy/raw_gadget.c-707- dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-710- }
drivers/usb/gadget/legacy/raw_gadget.c:711: if (WARN_ON(in && dev->ep0_out_pending)) {
drivers/usb/gadget/legacy/raw_gadget.c-712- ret = -ENODEV;
--
drivers/usb/gadget/legacy/raw_gadget.c-715- }
drivers/usb/gadget/legacy/raw_gadget.c:716: if (WARN_ON(!in && dev->ep0_in_pending)) {
drivers/usb/gadget/legacy/raw_gadget.c-717- ret = -ENODEV;
--
drivers/usb/gadget/legacy/raw_gadget.c-724- dev->req->zero = usb_raw_io_flags_zero(io->flags);
drivers/usb/gadget/legacy/raw_gadget.c:725: dev->ep0_urb_queued = true;
drivers/usb/gadget/legacy/raw_gadget.c-726- spin_unlock_irqrestore(&dev->lock, flags);
drivers/usb/gadget/legacy/raw_gadget.c-727-
drivers/usb/gadget/legacy/raw_gadget.c:728: ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
drivers/usb/gadget/legacy/raw_gadget.c-729- if (ret) {
--
drivers/usb/gadget/legacy/raw_gadget.c-735-
drivers/usb/gadget/legacy/raw_gadget.c:736: ret = wait_for_completion_interruptible(&dev->ep0_done);
drivers/usb/gadget/legacy/raw_gadget.c-737- if (ret) {
drivers/usb/gadget/legacy/raw_gadget.c-738- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
drivers/usb/gadget/legacy/raw_gadget.c:739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
drivers/usb/gadget/legacy/raw_gadget.c:740: wait_for_completion(&dev->ep0_done);
drivers/usb/gadget/legacy/raw_gadget.c-741- spin_lock_irqsave(&dev->lock, flags);
drivers/usb/gadget/legacy/raw_gadget.c:742: if (dev->ep0_status == -ECONNRESET)
drivers/usb/gadget/legacy/raw_gadget.c:743: dev->ep0_status = -EINTR;
drivers/usb/gadget/legacy/raw_gadget.c-744- goto out_interrupted;
--
drivers/usb/gadget/legacy/raw_gadget.c-749-out_interrupted:
drivers/usb/gadget/legacy/raw_gadget.c:750: ret = dev->ep0_status;
drivers/usb/gadget/legacy/raw_gadget.c-751-out_queue_failed:
drivers/usb/gadget/legacy/raw_gadget.c:752: dev->ep0_urb_queued = false;
drivers/usb/gadget/legacy/raw_gadget.c-753-out_unlock:
--
drivers/usb/gadget/legacy/raw_gadget.c-757-
drivers/usb/gadget/legacy/raw_gadget.c:758:static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
drivers/usb/gadget/legacy/raw_gadget.c-759-{
--
drivers/usb/gadget/legacy/raw_gadget.c-766- return PTR_ERR(data);
drivers/usb/gadget/legacy/raw_gadget.c:767: ret = raw_process_ep0_io(dev, &io, data, true);
drivers/usb/gadget/legacy/raw_gadget.c-768- kfree(data);
--
drivers/usb/gadget/legacy/raw_gadget.c-771-
drivers/usb/gadget/legacy/raw_gadget.c:772:static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
drivers/usb/gadget/legacy/raw_gadget.c-773-{
--
drivers/usb/gadget/legacy/raw_gadget.c-781- return PTR_ERR(data);
drivers/usb/gadget/legacy/raw_gadget.c:782: ret = raw_process_ep0_io(dev, &io, data, false);
drivers/usb/gadget/legacy/raw_gadget.c-783- if (ret < 0)
--
drivers/usb/gadget/legacy/raw_gadget.c-795-
drivers/usb/gadget/legacy/raw_gadget.c:796:static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
drivers/usb/gadget/legacy/raw_gadget.c-797-{
--
drivers/usb/gadget/legacy/raw_gadget.c-813- }
drivers/usb/gadget/legacy/raw_gadget.c:814: if (dev->ep0_urb_queued) {
drivers/usb/gadget/legacy/raw_gadget.c-815- dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-818- }
drivers/usb/gadget/legacy/raw_gadget.c:819: if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
drivers/usb/gadget/legacy/raw_gadget.c-820- dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
--
drivers/usb/gadget/legacy/raw_gadget.c-824-
drivers/usb/gadget/legacy/raw_gadget.c:825: ret = usb_ep_set_halt(dev->gadget->ep0);
drivers/usb/gadget/legacy/raw_gadget.c-826- if (ret < 0)
--
drivers/usb/gadget/legacy/raw_gadget.c-829-
drivers/usb/gadget/legacy/raw_gadget.c:830: if (dev->ep0_in_pending)
drivers/usb/gadget/legacy/raw_gadget.c:831: dev->ep0_in_pending = false;
drivers/usb/gadget/legacy/raw_gadget.c-832- else
drivers/usb/gadget/legacy/raw_gadget.c:833: dev->ep0_out_pending = false;
drivers/usb/gadget/legacy/raw_gadget.c-834-
--
drivers/usb/gadget/legacy/raw_gadget.c=1294=static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1312- case USB_RAW_IOCTL_EP0_WRITE:
drivers/usb/gadget/legacy/raw_gadget.c:1313: ret = raw_ioctl_ep0_write(dev, value);
drivers/usb/gadget/legacy/raw_gadget.c-1314- break;
drivers/usb/gadget/legacy/raw_gadget.c-1315- case USB_RAW_IOCTL_EP0_READ:
drivers/usb/gadget/legacy/raw_gadget.c:1316: ret = raw_ioctl_ep0_read(dev, value);
drivers/usb/gadget/legacy/raw_gadget.c-1317- break;
--
drivers/usb/gadget/legacy/raw_gadget.c-1339- case USB_RAW_IOCTL_EP0_STALL:
drivers/usb/gadget/legacy/raw_gadget.c:1340: ret = raw_ioctl_ep0_stall(dev, value);
drivers/usb/gadget/legacy/raw_gadget.c-1341- break;
]
|
| 400/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 15128
output: 15
thoughts: 58
Thoughts: **Investigating the Patch Application Status**
Okay, hold on a minute! My grepper search just turned up something interesting. It looks like the patch I was expecting to see applied *isn't* showing up in `drivers/usb/gadget/legacy/raw_gadget.c` yet. That's not what I anticipated. Hmmm, now I need to determine if the changes are still pending. Let's see... I'll quickly check the output of `patch-diff` and get a clear picture of the current state of applied changes.
|
| 401/4 |
2026/07/29 09:38 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..dbbe55570 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -160,28 +160,30 @@ enum dev_state {
struct raw_dev {
struct kref count;
spinlock_t lock;
const char *udc_name;
struct usb_gadget_driver driver;
/* Reference to misc device: */
struct device *dev;
/* Make driver names unique */
int driver_id_number;
/* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
int eps_num;
struct completion ep0_done;
struct raw_event_queue queue;
};
@@ -198,41 +200,27 @@ static struct raw_dev *dev_new(void)
spin_lock_init(&dev->lock);
init_completion(&dev->ep0_done);
raw_event_queue_init(&dev->queue);
dev->driver_id_number = -1;
return dev;
}
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
/*----------------------------------------------------------------------*/
static int raw_queue_event(struct raw_dev *dev,
enum usb_raw_event_type type, size_t length, const void *data)
{
int ret = 0;
unsigned long flags;
@@ -281,60 +269,106 @@ static u8 get_ep_addr(const char *name)
static int gadget_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int ret = 0, i = 0;
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
struct usb_request *req;
struct usb_ep *ep;
unsigned long flags;
if (strcmp(gadget->name, dev->udc_name) != 0)
return -ENODEV;
set_gadget_data(gadget, dev);
req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!req) {
dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
set_gadget_data(gadget, NULL);
return -ENOMEM;
}
spin_lock_irqsave(&dev->lock, flags);
dev->req = req;
dev->req->context = dev;
dev->req->complete = gadget_ep0_complete;
dev->gadget = gadget;
gadget_for_each_ep(ep, dev->gadget) {
dev->eps[i].ep = ep;
dev->eps[i].addr = get_ep_addr(ep->name);
dev->eps[i].state = STATE_EP_DISABLED;
i++;
}
dev->eps_num = i;
spin_unlock_irqrestore(&dev->lock, flags);
dev_dbg(&gadget->dev, "gadget connected\n");
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
}
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
static int gadget_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
int ret = 0;
@@ -615,44 +649,44 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
{
struct usb_raw_event arg;
unsigned long flags;
struct usb_raw_event *event;
uint32_t length;
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
return -EFAULT;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EINVAL;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
kfree(event);
return -EFAULT;
}
kfree(event);
return 0;
}
@@ -683,80 +717,97 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->ep0_urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !dev->ep0_in_pending) ||
(!in && !dev->ep0_out_pending)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EBUSY;
goto out_unlock;
}
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
void *data;
struct usb_raw_ep_io io;
@@ -920,54 +971,58 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (i < 0 || i >= dev->eps_num) {
dev_dbg(dev->dev, "fail, invalid endpoint\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->eps[i].state == STATE_EP_DISABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].disabling) {
dev_dbg(&dev->gadget->dev,
"fail, disable already in progress\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -1058,91 +1113,108 @@ static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
struct raw_ep *ep;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (io->ep >= dev->eps_num) {
dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
ret = -EINVAL;
goto out_unlock;
}
ep = &dev->eps[io->ep];
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->disabling) {
dev_dbg(&dev->gadget->dev,
"fail, endpoint is already being disabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if (in != usb_endpoint_dir_in(ep->ep->desc)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EINVAL;
goto out_unlock;
}
ep->dev = dev;
ep->req->context = &done;
ep->req->complete = gadget_ep_complete;
ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
char *data;
struct usb_raw_ep_io io;
]
|
| 402/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 21429
output: 45
thoughts: 485
Thoughts: **Analyzing Checkpatch Warnings After Patch Application**
Okay, the patch is definitely in! Let's revisit those `checkpatch` warnings. It looks like we have two "Missing a blank line after declarations" warnings in `drivers/usb/gadget/legacy/raw_gadget.c` around lines 779 and 1186. My initial instinct is to just jump in and read those lines directly to assess the situation.
I'll start by reading the surrounding code for context. I'll use `read-file` to get lines 760-800 and 1160-1200. Hmm, wait a minute, the line numbers might have shifted due to the patch itself! I need to be more precise.
To get the *exact* context, I'll find the line numbers containing those exact declarations. I need to be sure I'm looking at the correct lines. Let's use `grepper` to search for the lines with `struct usb_ep \*ep0` and `struct usb_ep \*usb_ep`. Oh wait, I remember now! I had issues with regex before. With `grepper`, the asterisk needs to be escaped. Or maybe I should simplify the search. Let's try `grepper` again, but this time I'll use `ep0 = dev->gadget->ep0` instead. That should be unambiguous and get me the right lines quickly.
|
| 403/4 |
2026/07/29 09:38 |
tool |
grepper |
0m
Error:
Args: map[Expression:ep0 = dev->gadget->ep0 PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 404/4 |
2026/07/29 09:38 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 22283
output: 39
thoughts: 26
Thoughts: Here's my thought process, as if I were the one thinking it:
**Digging into that "no matches" result**
Okay, wait a minute, why did that search return nothing? That's not right. Let's try a more specific search. I should have been more granular in my initial query. Let's try searching for the `struct usb_ep` in the code, I know that's where I need to look for low-level USB endpoint information. Good thing I have `grepper` set up, it'll make this so much faster than manually sifting through the source. Let's see what that turns up. Hopefully, this will lead me to the right location.
|
| 405/4 |
2026/07/29 09:39 |
tool |
grepper |
0m
Args: map[Expression:struct usb_ep PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:drivers/usb/gadget/legacy/raw_gadget.c=139=struct raw_ep {
--
drivers/usb/gadget/legacy/raw_gadget.c-141- enum ep_state state;
drivers/usb/gadget/legacy/raw_gadget.c:142: struct usb_ep *ep;
drivers/usb/gadget/legacy/raw_gadget.c-143- u8 addr;
--
drivers/usb/gadget/legacy/raw_gadget.c=234=static int raw_queue_event(struct raw_dev *dev,
--
drivers/usb/gadget/legacy/raw_gadget.c-248-
drivers/usb/gadget/legacy/raw_gadget.c:249:static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
drivers/usb/gadget/legacy/raw_gadget.c-250-{
--
drivers/usb/gadget/legacy/raw_gadget.c=281=static int gadget_bind(struct usb_gadget *gadget,
--
drivers/usb/gadget/legacy/raw_gadget.c-286- struct usb_request *req;
drivers/usb/gadget/legacy/raw_gadget.c:287: struct usb_ep *ep;
drivers/usb/gadget/legacy/raw_gadget.c-288- unsigned long flags;
--
drivers/usb/gadget/legacy/raw_gadget.c=974=static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
--
drivers/usb/gadget/legacy/raw_gadget.c-1041-
drivers/usb/gadget/legacy/raw_gadget.c:1042:static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
drivers/usb/gadget/legacy/raw_gadget.c-1043-{
--
drivers/usb/gadget/legacy/raw_gadget.c=1207=static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
--
drivers/usb/gadget/legacy/raw_gadget.c-1229-
drivers/usb/gadget/legacy/raw_gadget.c:1230:static void fill_ep_caps(struct usb_ep_caps *caps,
drivers/usb/gadget/legacy/raw_gadget.c-1231- struct usb_raw_ep_caps *raw_caps)
--
drivers/usb/gadget/legacy/raw_gadget.c-1240-
drivers/usb/gadget/legacy/raw_gadget.c:1241:static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits)
drivers/usb/gadget/legacy/raw_gadget.c-1242-{
]
|
| 406/4 |
2026/07/29 09:39 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 23250
output: 43
thoughts: 545
Thoughts: **Debugging a `grepper` Mystery**
Okay, hold on a second. I ran `grepper` looking for instances of `struct usb_ep`, specifically to find `struct usb_ep *ep0` or `struct usb_ep *usb_ep`, and it came up empty! That's strange. I *know* I applied a patch that modifies that structure. Let's double-check the patch diff again.
Aha! Looking at the `patch-diff` output, I see the code modification around the `wait_for_completion_interruptible` call. Specifically, the patch does indeed modify how `ep0` is handled after an interrupt. Okay, so the patch *is* applied. Why isn't `grepper` finding it?
Wait a minute, I bet I know. Is `grepper` running `git grep` on the git index or the working directory? Let me check the documentation... "The tool executes git grep on the kernel sources and returns the output." Okay, that narrows it down. Normally, `git grep` searches the tracked files in the working directory if no commit is specified, so that *should* be right. Unless... wait, maybe the changes aren't staged or committed. Although, if no commit is specified, it should search the working directory. But maybe it's only searching the index, which wouldn't have the unstaged changes.
To confirm this, let's just go ahead and read the file directly. I'll use `read-file` to check the contents of `drivers/usb/gadget/legacy/raw_gadget.c` around line 740 and see if I can find the patch. I'll read from line 730 to 770 just to be sure. This should give me the definitive answer.
|
| 407/4 |
2026/07/29 09:39 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c FirstLine:730 LineCount:40]
Results: map[Contents: 730: dev_err(&dev->gadget->dev,
731: "fail, usb_ep_queue returned %d\n", ret);
732: spin_lock_irqsave(&dev->lock, flags);
733: goto out_queue_failed;
734: }
735:
736: ret = wait_for_completion_interruptible(&dev->ep0_done);
737: if (ret) {
738: dev_dbg(&dev->gadget->dev, "wait interrupted\n");
739: usb_ep_dequeue(dev->gadget->ep0, dev->req);
740: wait_for_completion(&dev->ep0_done);
741: spin_lock_irqsave(&dev->lock, flags);
742: if (dev->ep0_status == -ECONNRESET)
743: dev->ep0_status = -EINTR;
744: goto out_interrupted;
745: }
746:
747: spin_lock_irqsave(&dev->lock, flags);
748:
749: out_interrupted:
750: ret = dev->ep0_status;
751: out_queue_failed:
752: dev->ep0_urb_queued = false;
753: out_unlock:
754: spin_unlock_irqrestore(&dev->lock, flags);
755: return ret;
756: }
757:
758: static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
759: {
760: int ret = 0;
761: void *data;
762: struct usb_raw_ep_io io;
763:
764: data = raw_alloc_io_data(&io, (void __user *)value, true);
765: if (IS_ERR(data))
766: return PTR_ERR(data);
767: ret = raw_process_ep0_io(dev, &io, data, true);
768: kfree(data);
769: return ret;
]
|
| 408/4 |
2026/07/29 09:39 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 24860
output: 178
thoughts: 5496
Thoughts: **Code Review and Patch Application Process**
Okay, so the initial confusion stemmed from a fundamental misunderstanding. Tools like `read-file` and `codesearch` *only* show the original source code, not the working copy that has patches applied. `patch-diff`, however, is the key to seeing the *actual* state of the modified file after applying a patch, and the state of the modified file is where we need to look. To make edits, we must use `codeeditor`, which requires careful attention to detail. The `codeeditor` tool relies on the *current* state of the file β including any previously applied patches β as the `CurrentCode`.
Now, the plan is to clean up code according to coding standards, specifically around missing blank lines after declarations. After analyzing `patch-diff`, I identified two specific areas where `checkpatch` flagged warnings.
The first issue is in the interruptible wait in `raw_gadget.c` around line 779:
```c
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req);
```
I need to insert a blank line after the declaration `struct usb_ep *ep0 = dev->gadget->ep0;`, so the code would become:
```c
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req);
```
Then, the second area for fixing is in `raw_process_ep_io`:
```c
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(usb_ep, ep->req);
```
We need to add a blank line after the declaration `struct usb_ep *usb_ep = ep->ep;`.
So the code needs to be modified:
```c
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(usb_ep, ep->req);
```
Importantly, I will use the exact lines from the diff (including tab characters, as indentation matters!) as the `CurrentCode` and the modified code (also using the exact tab characters) as the `NewCode` when calling `codeeditor`, since we can't rely on being able to see our edits through standard code viewing tools. Let's do this meticulously!
|
| 409/4 |
2026/07/29 09:39 |
tool |
codeeditor |
0m
Args: map[CurrentCode: if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req); NewCode: if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(ep0, dev->req); SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 410/4 |
2026/07/29 09:39 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 31209
output: 174
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Moment of Triumph & Anticipation**
Well, that went precisely as planned! The initial execution yielded the desired result, confirming the efficacy of the approach. Now, with that foundation secured, it's time to transition seamlessly to the next phase. I'm already mentally charting the course for the second modification, anticipating the nuances and interactions it will introduce. The parameters are in place, so let's get started.
|
| 411/4 |
2026/07/29 09:39 |
tool |
codeeditor |
0m
Args: map[CurrentCode: if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(usb_ep, ep->req); NewCode: if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_dequeue(usb_ep, ep->req); SourceFile:drivers/usb/gadget/legacy/raw_gadget.c]
Results:
|
| 412/4 |
2026/07/29 09:39 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 31509
output: 30
thoughts: 22
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the Recent Code Modifications**
Right, that seems to have worked flawlessly. Now, I want to take a closer look at the actual code changes we just implemented. Let's fire up `patch-diff`. I'm especially interested in reviewing the specific modifications, ensuring they align perfectly with the original design and, of course, that there are no unexpected side effects. I anticipate a concise output, highlighting the relevant diffs. From there I'll be able to ensure that everything is exactly how it should be.
|
| 413/4 |
2026/07/29 09:39 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..bce4f8d3f 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -160,28 +160,30 @@ enum dev_state {
struct raw_dev {
struct kref count;
spinlock_t lock;
const char *udc_name;
struct usb_gadget_driver driver;
/* Reference to misc device: */
struct device *dev;
/* Make driver names unique */
int driver_id_number;
/* Protected by lock: */
enum dev_state state;
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
ssize_t ep0_status;
struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
int eps_num;
struct completion ep0_done;
struct raw_event_queue queue;
};
@@ -198,41 +200,27 @@ static struct raw_dev *dev_new(void)
spin_lock_init(&dev->lock);
init_completion(&dev->ep0_done);
raw_event_queue_init(&dev->queue);
dev->driver_id_number = -1;
return dev;
}
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
/*----------------------------------------------------------------------*/
static int raw_queue_event(struct raw_dev *dev,
enum usb_raw_event_type type, size_t length, const void *data)
{
int ret = 0;
unsigned long flags;
@@ -281,60 +269,106 @@ static u8 get_ep_addr(const char *name)
static int gadget_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int ret = 0, i = 0;
struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
struct usb_request *req;
struct usb_ep *ep;
unsigned long flags;
if (strcmp(gadget->name, dev->udc_name) != 0)
return -ENODEV;
set_gadget_data(gadget, dev);
req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!req) {
dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
set_gadget_data(gadget, NULL);
return -ENOMEM;
}
spin_lock_irqsave(&dev->lock, flags);
dev->req = req;
dev->req->context = dev;
dev->req->complete = gadget_ep0_complete;
dev->gadget = gadget;
gadget_for_each_ep(ep, dev->gadget) {
dev->eps[i].ep = ep;
dev->eps[i].addr = get_ep_addr(ep->name);
dev->eps[i].state = STATE_EP_DISABLED;
i++;
}
dev->eps_num = i;
spin_unlock_irqrestore(&dev->lock, flags);
dev_dbg(&gadget->dev, "gadget connected\n");
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
/* Matches kref_put() in gadget_unbind(). */
kref_get(&dev->count);
return ret;
}
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
static int gadget_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
int ret = 0;
@@ -615,44 +649,44 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
{
struct usb_raw_event arg;
unsigned long flags;
struct usb_raw_event *event;
uint32_t length;
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
return -EFAULT;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EINVAL;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
spin_unlock_irqrestore(&dev->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->lock, flags);
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
length = min(arg.length, event->length);
if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
kfree(event);
return -EFAULT;
}
kfree(event);
return 0;
}
@@ -683,80 +717,98 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->ep0_urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if ((in && !dev->ep0_in_pending) ||
(!in && !dev->ep0_out_pending)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EBUSY;
goto out_unlock;
}
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
goto out_unlock;
}
dev->req->buf = data;
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
void *data;
struct usb_raw_ep_io io;
@@ -920,54 +972,58 @@ static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
{
int ret = 0, i = value;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (i < 0 || i >= dev->eps_num) {
dev_dbg(dev->dev, "fail, invalid endpoint\n");
ret = -EBUSY;
goto out_unlock;
}
if (dev->eps[i].state == STATE_EP_DISABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].disabling) {
dev_dbg(&dev->gadget->dev,
"fail, disable already in progress\n");
ret = -EINVAL;
goto out_unlock;
}
if (dev->eps[i].urb_queued) {
dev_dbg(&dev->gadget->dev,
"fail, waiting for urb completion\n");
ret = -EINVAL;
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
dev->eps[i].disabling = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
@@ -1058,91 +1114,109 @@ static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
void *data, bool in)
{
int ret = 0;
unsigned long flags;
struct raw_ep *ep;
DECLARE_COMPLETION_ONSTACK(done);
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) {
dev_dbg(dev->dev, "fail, device is not running\n");
ret = -EINVAL;
goto out_unlock;
}
if (!dev->gadget) {
dev_dbg(dev->dev, "fail, gadget is not bound\n");
ret = -EBUSY;
goto out_unlock;
}
if (io->ep >= dev->eps_num) {
dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
ret = -EINVAL;
goto out_unlock;
}
ep = &dev->eps[io->ep];
if (ep->state != STATE_EP_ENABLED) {
dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->disabling) {
dev_dbg(&dev->gadget->dev,
"fail, endpoint is already being disabled\n");
ret = -EBUSY;
goto out_unlock;
}
if (ep->urb_queued) {
dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
ret = -EBUSY;
goto out_unlock;
}
if (in != usb_endpoint_dir_in(ep->ep->desc)) {
dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
ret = -EINVAL;
goto out_unlock;
}
ep->dev = dev;
ep->req->context = &done;
ep->req->complete = gadget_ep_complete;
ep->req->buf = data;
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
out_interrupted:
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
}
static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
{
int ret = 0;
char *data;
struct usb_raw_ep_io io;
]
|
| 414/4 |
2026/07/29 09:39 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 37801
output: 9
thoughts: 25
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Finalizing This Patchset**
Alright, that diff looks exceptionally clean. I'm satisfied with the changes themselves β the logic is sound, the implementation adheres to the project's style guidelines, and it addresses the original issue comprehensively. But, before I even *think* about submitting, I need to make absolutely sure everything is in order. It's a matter of professional pride, really.
Let's fire up `checkpatch` one more time. I'm confident it'll be clean, but I've learned that a final run can uncover some minor, easily-missed nitpick, or perhaps a lingering whitespace issue I hadn't spotted before. It's a quick, automated check, and well worth the time. Better to catch anything now, while I'm in the zone, than have it flagged during the review process. And, let's be honest, it would bug me if something slipped through. Time to get this patchset properly buttoned up.
|
| 415/4 |
2026/07/29 09:39 |
tool |
checkpatch |
0m
Results: map[Output:total: 0 errors, 0 warnings, 223 lines checked
Your patch has no obvious style problems and is ready for submission.
]
|
| 416/4 |
2026/07/29 09:39 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 38103
output: 71
thoughts: 257
Thoughts: **Patch Submission Confidence**
Alright, checkpatch is giving me the green light! "Your patch has no obvious style problems and is ready for submission." That's excellent news. Still, let's just make absolutely certain. No other files need attention, and the user's sole request was style formatting. Good, we're on track.
One last, thorough review of the diff. Gotta be absolutely certain there are no lingering syntax errors or logic breaks. Okay, let's quickly re-examine the changes:
1. Originally:
```c
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *ep0 = dev->gadget->ep0;
spin_unlock_irqrestore(&dev->lock, flags);
```
2. Now:
```c
if (dev->state != STATE_DEV_FAILED && dev->gadget) {
struct usb_ep *usb_ep = ep->ep;
spin_unlock_irqrestore(&dev->lock, flags);
```
Yep, clean, valid C. The logic remains *identical*, and those blank lines are precisely where they needed to be. Brilliant. It's been checked, and it's perfect. This should pass code review without a hiccup.
Fantastic. With that final double-check complete, I am now ready to present the formatted patch. Task completed.
|
| 417/3 |
2026/07/29 09:39 |
action |
test-patch-inplace |
12m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..bce4f8d3f 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,47 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +671,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +757,37 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -750,6 +799,9 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
@@ -956,11 +1008,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1165,37 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
@@ -1136,6 +1207,9 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
TestError:]
|
| 418/3 |
2026/07/29 09:52 |
action |
run-checkpatch |
0m
Results: map[CheckpatchOutput:total: 0 errors, 0 warnings, 223 lines checked
Your patch has no obvious style problems and is ready for submission.
NeedRefinement:false]
|
| 419/1 |
2026/07/29 09:52 |
agent |
fixes-finder |
1m
Model:
Results: map[FixesHash:f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
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 crash is:
=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
The explanation of the root cause is:
The root cause of this crash is a use-after-free (UAF) vulnerability in the `raw_gadget` driver. It occurs because the driver fails to properly clean up its internal state and synchronize concurrent operations when the underlying USB Device Controller (UDC) is unbound.
### Detailed Sequence of Events
1. **Gadget Initialization and Binding:**
The reproducer opens `/dev/raw-gadget` and initializes it. When the gadget binds to the `dummy_udc` controller, the UDC core invokes the `gadget_bind()` callback. This function saves the `struct usb_gadget` pointer into `dev->gadget` and eventually sets `dev->state = STATE_DEV_RUNNING`.
2. **UDC Unbinding:**
The reproducer then writes to `/sys/bus/platform/drivers/dummy_udc/unbind` via sysfs. This forces the UDC core to unbind the gadget driver, which invokes the `gadget_unbind()` callback in `raw_gadget.c`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
Notice that `gadget_unbind()` **fails to clear `dev->gadget`** and **fails to update `dev->state`**. It only clears the driver data. After this function returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device` (including the device name string allocated via `kvasprintf`).
3. **Dangling Pointer Dereference:**
The reproducer then issues a `USB_RAW_IOCTL_EP0_READ` (or `WRITE`) ioctl, which lands in `raw_process_ep0_io()`:
```c
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, void *data, bool in)
{
// ...
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { // Passes, state was never updated
// ...
}
if (!dev->gadget) { // Passes, dev->gadget is a dangling pointer
// ...
}
// ...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);
// ...
```
Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
4. **The Crash:**
Since the UDC is disconnected/freed, `usb_ep_queue()` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, ...)`. The `dev_err()` macro attempts to print the device name (`dev->gadget->dev.kobj.name`), which points to the freed 16-byte string, resulting in the KASAN `slab-use-after-free` crash observed in the trace.
### How to Fix
To fix this bug, `gadget_unbind()` must clear the `dev->gadget` pointer and update `dev->state` (e.g., to `STATE_DEV_FAILED`) while holding `dev->lock`.
However, simply clearing the pointer is not enough due to race conditions. If an ioctl thread has already passed the `!dev->gadget` check and released `dev->lock`, but hasn't yet called `usb_ep_queue()` (which is called lockless), `gadget_unbind()` could return and free the gadget right under the ioctl's feet.
To prevent this race, `raw_gadget` needs a synchronization mechanism to track ongoing UDC calls and wait for them to finish before returning from `gadget_unbind()`. A common pattern for this (as seen in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`) is to introduce a `udc_usage` counter:
- Increment `udc_usage` under `dev->lock` before releasing the lock to call `usb_ep_queue()`.
- Decrement `udc_usage` under `dev->lock` after `usb_ep_queue()` returns.
- In `gadget_unbind()`, wait for `udc_usage` to drop to `0` before returning.
The patch that fixes the bug is:
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..bce4f8d3f 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,47 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +671,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +757,37 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -750,6 +799,9 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
@@ -956,11 +1008,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1165,37 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
@@ -1136,6 +1207,9 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
Search for the commit(s) that introduced this bug.
|
| 420/2 |
2026/07/29 09:52 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 13196
output: 41
thoughts: 225
Thoughts: **Hunting Down a Use-After-Free in raw_gadget.c**
Alright, let's get down to brass tacks. I need to pinpoint the commit that introduced this use-after-free vulnerability within `raw_gadget.c`. The root of the problem seems to be the `gadget_unbind()` function within `drivers/usb/gadget/legacy/raw_gadget.c`. It's my understanding that it's neglecting to properly clean up `dev->gadget` and `dev->state`. Adding insult to injury, there's a distinct lack of synchronization for concurrent ioctls when the UDC is unbound, which is a recipe for disaster.
From what I've read, the proposed patch aims to rectify this by introducing `udc_usage` and `unbind_completion` primitives, clearing `dev->gadget` within `gadget_unbind()`, and correcting the dev error message to use the correct device pointer.
My focus now is the history of `drivers/usb/gadget/legacy/raw_gadget.c` and specifically `gadget_unbind` function and `raw_process_ep0_io`. I need to figure out when this `raw_gadget` driver was introduced or when `gadget_unbind` was added. Once I have the commit history, I can start to trace the introduction of the bug and the changes that made the vulnerability possible.
|
| 421/2 |
2026/07/29 09:52 |
tool |
git-log |
0m
Error:
Args: map[PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c SymbolName:gadget_unbind]
Results:
|
| 422/2 |
2026/07/29 09:52 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 13788
output: 41
thoughts: 16
Thoughts: Okay, here's the summary, assuming I'm thinking about the connection between a symbol name and its source path:
**Thinking About Symbol Names and Source Paths**
Right, so I'm processing this information, and it's hitting me: the relationship between this `SymbolName` and `SourcePath` β they're intrinsically linked. It's not a suggestion, it's a fundamental pairing. You can't really have one without the other in any meaningful context.
`SymbolName` gives you *what* you're dealing with β a variable, a function, a class, whatever. But it's the `SourcePath` that tells you *where* to find the definition, the implementation, the declaration. It's the grounding.
I can immediately think of several scenarios where this connection is crucial. Debugging? Absolutely. Refactoring? Essential. Code analysis? Forget about it without knowing both. Any kind of tooling that needs to understand the structure and location of code *must* link these pieces together. Without them, you're essentially blind.
|
| 423/2 |
2026/07/29 09:52 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/legacy/raw_gadget.c SymbolName:gadget_unbind]
Results: map[Output:f2c2e717642c usb: gadget: add raw-gadget interface
]
|
| 424/2 |
2026/07/29 09:52 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 14108
output: 27
thoughts: 20
Thoughts: Okay, here's my interpretation of what's going through my mind when I encounter that request, rendered in the first person, assuming a level of expertise:
**Diving into Commit f2c2e717642c**
Alright, *f2c2e717642c*β¦ let's pull that commit up. I need to get a sense of what's changed. Usually, I'll start by glancing at the commit message; that should give me the headline, the *why*. Is it a bug fix? Feature implementation? Refactor? Knowing the intention sets the context.
Next, I'm going to look at the diff. I'm not going to meticulously read *every* line immediately, that's inefficient unless I'm debugging something related to this. I'll likely use `git diff --stat` or similar to get a quick summary of file changes first. I'm focusing on the *types* of files affected. Are we talking core logic? UI? Dependencies? That hints at the potential scope of the change.
Then, depending on the initial assessment, I'll dive deeper. If it's a critical area, I might review the changes file by file. I'm paying close attention to the code around the modified lines. I want to see if this change introduced any unexpected side effects, or if it has knock-on implications I should be aware of. I'll look for potential edge cases that weren't considered, or opportunities for improvement. I'm also considering the code style and readability, does this change integrate cleanly with the existing codebase?
If it's a refactor, I'm expecting a more structural change, and I'll look for patterns. Have they simplified something? Introduced a new abstraction? Removed unnecessary code? I'll probably be thinking about testing too, looking for tests to confirm the expected behavior is maintained.
Ultimately, I want to form a mental model of *what* was changed, *why* it was changed, and *how* it impacts the broader system. I'll be assessing the quality of the change and looking for anything that might need further discussion or follow-up. Based on that assessment, I'll either archive it, make some comments, maybe ask for clarifications, or simply move on, if the change is already good.
|
| 425/2 |
2026/07/29 09:52 |
tool |
git-show |
0m
Args:
Results: map[Output:
Full output is too long, showing 1000 out of 1403 lines.
[commit f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10
Author: Andrey Konovalov <andreyknvl@gmail.com>
Date: Mon Feb 24 17:13:03 2020 +0100
usb: gadget: add raw-gadget interface
USB Raw Gadget is a kernel module that provides a userspace interface for
the USB Gadget subsystem. Essentially it allows to emulate USB devices
from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
currently a strictly debugging feature and shouldn't be used in
production.
Raw Gadget is similar to GadgetFS, but provides a more low-level and
direct access to the USB Gadget layer for the userspace. The key
differences are:
1. Every USB request is passed to the userspace to get a response, while
GadgetFS responds to some USB requests internally based on the provided
descriptors. However note, that the UDC driver might respond to some
requests on its own and never forward them to the Gadget layer.
2. GadgetFS performs some sanity checks on the provided USB descriptors,
while Raw Gadget allows you to provide arbitrary data as responses to
USB requests.
3. Raw Gadget provides a way to select a UDC device/driver to bind to,
while GadgetFS currently binds to the first available UDC.
4. Raw Gadget uses predictable endpoint names (handles) across different
UDCs (as long as UDCs have enough endpoints of each required transfer
type).
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
diff --git a/Documentation/usb/index.rst b/Documentation/usb/index.rst
index 36b6ebd9a9d9..b656c9be23ed 100644
--- a/Documentation/usb/index.rst
+++ b/Documentation/usb/index.rst
@@ -22,6 +22,7 @@ USB support
misc_usbsevseg
mtouchusb
ohci
+ raw-gadget
usbip_protocol
usbmon
usb-serial
diff --git a/Documentation/usb/raw-gadget.rst b/Documentation/usb/raw-gadget.rst
new file mode 100644
index 000000000000..9e78cb858f86
--- /dev/null
+++ b/Documentation/usb/raw-gadget.rst
@@ -0,0 +1,61 @@
+==============
+USB Raw Gadget
+==============
+
+USB Raw Gadget is a kernel module that provides a userspace interface for
+the USB Gadget subsystem. Essentially it allows to emulate USB devices
+from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
+currently a strictly debugging feature and shouldn't be used in
+production, use GadgetFS instead.
+
+Comparison to GadgetFS
+~~~~~~~~~~~~~~~~~~~~~~
+
+Raw Gadget is similar to GadgetFS, but provides a more low-level and
+direct access to the USB Gadget layer for the userspace. The key
+differences are:
+
+1. Every USB request is passed to the userspace to get a response, while
+ GadgetFS responds to some USB requests internally based on the provided
+ descriptors. However note, that the UDC driver might respond to some
+ requests on its own and never forward them to the Gadget layer.
+
+2. GadgetFS performs some sanity checks on the provided USB descriptors,
+ while Raw Gadget allows you to provide arbitrary data as responses to
+ USB requests.
+
+3. Raw Gadget provides a way to select a UDC device/driver to bind to,
+ while GadgetFS currently binds to the first available UDC.
+
+4. Raw Gadget uses predictable endpoint names (handles) across different
+ UDCs (as long as UDCs have enough endpoints of each required transfer
+ type).
+
+5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
+
+Userspace interface
+~~~~~~~~~~~~~~~~~~~
+
+To create a Raw Gadget instance open /dev/raw-gadget. Multiple raw-gadget
+instances (bound to different UDCs) can be used at the same time. The
+interaction with the opened file happens through the ioctl() calls, see
+comments in include/uapi/linux/usb/raw_gadget.h for details.
+
+The typical usage of Raw Gadget looks like:
+
+1. Open Raw Gadget instance via /dev/raw-gadget.
+2. Initialize the instance via USB_RAW_IOCTL_INIT.
+3. Launch the instance with USB_RAW_IOCTL_RUN.
+4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from
+ Raw Gadget and react to those depending on what kind of USB device
+ needs to be emulated.
+
+Potential future improvements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Implement ioctl's for setting/clearing halt status on endpoints.
+
+- Reporting more events (suspend, resume, etc.) through
+ USB_RAW_IOCTL_EVENT_FETCH.
+
+- Support O_NONBLOCK I/O.
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 6e7e1a9202e6..a7064e21d9f2 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -516,4 +516,15 @@ config USB_G_WEBCAM
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_webcam".
+config USB_RAW_GADGET
+ tristate "USB Raw Gadget"
+ help
+ USB Raw Gadget is a kernel module that provides a userspace interface
+ for the USB Gadget subsystem. Essentially it allows to emulate USB
+ devices from userspace. See Documentation/usb/raw-gadget.rst for
+ details.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "raw_gadget".
+
endchoice
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
index abd0c3e66a05..4d864bf82799 100644
--- a/drivers/usb/gadget/legacy/Makefile
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
+obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
new file mode 100644
index 000000000000..76406343fbe5
--- /dev/null
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -0,0 +1,1078 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Raw Gadget driver.
+ * See Documentation/usb/raw-gadget.rst for more details.
+ *
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ */
+
+#include <linux/compiler.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/kref.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/gadget.h>
+
+#include <uapi/linux/usb/raw_gadget.h>
+
+#define DRIVER_DESC "USB Raw Gadget"
+#define DRIVER_NAME "raw-gadget"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Andrey Konovalov");
+MODULE_LICENSE("GPL");
+
+/*----------------------------------------------------------------------*/
+
+#define RAW_EVENT_QUEUE_SIZE 16
+
+struct raw_event_queue {
+ /* See the comment in raw_event_queue_fetch() for locking details. */
+ spinlock_t lock;
+ struct semaphore sema;
+ struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE];
+ int size;
+};
+
+static void raw_event_queue_init(struct raw_event_queue *queue)
+{
+ spin_lock_init(&queue->lock);
+ sema_init(&queue->sema, 0);
+ queue->size = 0;
+}
+
+static int raw_event_queue_add(struct raw_event_queue *queue,
+ enum usb_raw_event_type type, size_t length, const void *data)
+{
+ unsigned long flags;
+ struct usb_raw_event *event;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return -ENOMEM;
+ }
+ event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
+ if (!event) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return -ENOMEM;
+ }
+ event->type = type;
+ event->length = length;
+ if (event->length)
+ memcpy(&event->data[0], data, length);
+ queue->events[queue->size] = event;
+ queue->size++;
+ up(&queue->sema);
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return 0;
+}
+
+static struct usb_raw_event *raw_event_queue_fetch(
+ struct raw_event_queue *queue)
+{
+ unsigned long flags;
+ struct usb_raw_event *event;
+
+ /*
+ * This function can be called concurrently. We first check that
+ * there's at least one event queued by decrementing the semaphore,
+ * and then take the lock to protect queue struct fields.
+ */
+ if (down_interruptible(&queue->sema))
+ return NULL;
+ spin_lock_irqsave(&queue->lock, flags);
+ if (WARN_ON(!queue->size))
+ return NULL;
+ event = queue->events[0];
+ queue->size--;
+ memmove(&queue->events[0], &queue->events[1],
+ queue->size * sizeof(queue->events[0]));
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return event;
+}
+
+static void raw_event_queue_destroy(struct raw_event_queue *queue)
+{
+ int i;
+
+ for (i = 0; i < queue->size; i++)
+ kfree(queue->events[i]);
+ queue->size = 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+struct raw_dev;
+
+#define USB_RAW_MAX_ENDPOINTS 32
+
+enum ep_state {
+ STATE_EP_DISABLED,
+ STATE_EP_ENABLED,
+};
+
+struct raw_ep {
+ struct raw_dev *dev;
+ enum ep_state state;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ bool urb_queued;
+ bool disabling;
+ ssize_t status;
+};
+
+enum dev_state {
+ STATE_DEV_INVALID = 0,
+ STATE_DEV_OPENED,
+ STATE_DEV_INITIALIZED,
+ STATE_DEV_RUNNING,
+ STATE_DEV_CLOSED,
+ STATE_DEV_FAILED
+};
+
+struct raw_dev {
+ struct kref count;
+ spinlock_t lock;
+
+ const char *udc_name;
+ struct usb_gadget_driver driver;
+
+ /* Reference to misc device: */
+ struct device *dev;
+
+ /* Protected by lock: */
+ enum dev_state state;
+ bool gadget_registered;
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ bool ep0_in_pending;
+ bool ep0_out_pending;
+ bool ep0_urb_queued;
+ ssize_t ep0_status;
+ struct raw_ep eps[USB_RAW_MAX_ENDPOINTS];
+
+ struct completion ep0_done;
+ struct raw_event_queue queue;
+};
+
+static struct raw_dev *dev_new(void)
+{
+ struct raw_dev *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+ /* Matches kref_put() in raw_release(). */
+ kref_init(&dev->count);
+ spin_lock_init(&dev->lock);
+ init_completion(&dev->ep0_done);
+ raw_event_queue_init(&dev->queue);
+ return dev;
+}
+
+static void dev_free(struct kref *kref)
+{
+ struct raw_dev *dev = container_of(kref, struct raw_dev, count);
+ int i;
+
+ kfree(dev->udc_name);
+ kfree(dev->driver.udc_name);
+ if (dev->req) {
+ if (dev->ep0_urb_queued)
+ usb_ep_dequeue(dev->gadget->ep0, dev->req);
+ usb_ep_free_request(dev->gadget->ep0, dev->req);
+ }
+ raw_event_queue_destroy(&dev->queue);
+ for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
+ if (dev->eps[i].state != STATE_EP_ENABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ kfree(dev);
+}
+
+/*----------------------------------------------------------------------*/
+
+static int raw_queue_event(struct raw_dev *dev,
+ enum usb_raw_event_type type, size_t length, const void *data)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ ret = raw_event_queue_add(&dev->queue, type, length, data);
+ if (ret < 0) {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ return ret;
+}
+
+static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct raw_dev *dev = req->context;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (req->status)
+ dev->ep0_status = req->status;
+ else
+ dev->ep0_status = req->actual;
+ if (dev->ep0_in_pending)
+ dev->ep0_in_pending = false;
+ else
+ dev->ep0_out_pending = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ complete(&dev->ep0_done);
+}
+
+static int gadget_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ int ret = 0;
+ struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
+ struct usb_request *req;
+ unsigned long flags;
+
+ if (strcmp(gadget->name, dev->udc_name) != 0)
+ return -ENODEV;
+
+ set_gadget_data(gadget, dev);
+ req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!req) {
+ dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
+ set_gadget_data(gadget, NULL);
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->req = req;
+ dev->req->context = dev;
+ dev->req->complete = gadget_ep0_complete;
+ dev->gadget = gadget;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* Matches kref_put() in gadget_unbind(). */
+ kref_get(&dev->count);
+
+ ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
+ if (ret < 0)
+ dev_err(&gadget->dev, "failed to queue event\n");
+
+ return ret;
+}
+
+static void gadget_unbind(struct usb_gadget *gadget)
+{
+ struct raw_dev *dev = get_gadget_data(gadget);
+
+ set_gadget_data(gadget, NULL);
+ /* Matches kref_get() in gadget_bind(). */
+ kref_put(&dev->count, dev_free);
+}
+
+static int gadget_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int ret = 0;
+ struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_err(&gadget->dev, "ignoring, device is not running\n");
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+ if (dev->ep0_in_pending || dev->ep0_out_pending) {
+ dev_dbg(&gadget->dev, "stalling, request already pending\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
+ dev->ep0_in_pending = true;
+ else
+ dev->ep0_out_pending = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
+ if (ret < 0)
+ dev_err(&gadget->dev, "failed to queue event\n");
+ goto out;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+out:
+ return ret;
+}
+
+/* These are currently unused but present in case UDC driver requires them. */
+static void gadget_disconnect(struct usb_gadget *gadget) { }
+static void gadget_suspend(struct usb_gadget *gadget) { }
+static void gadget_resume(struct usb_gadget *gadget) { }
+static void gadget_reset(struct usb_gadget *gadget) { }
+
+/*----------------------------------------------------------------------*/
+
+static struct miscdevice raw_misc_device;
+
+static int raw_open(struct inode *inode, struct file *fd)
+{
+ struct raw_dev *dev;
+
+ /* Nonblocking I/O is not supported yet. */
+ if (fd->f_flags & O_NONBLOCK)
+ return -EINVAL;
+
+ dev = dev_new();
+ if (!dev)
+ return -ENOMEM;
+ fd->private_data = dev;
+ dev->state = STATE_DEV_OPENED;
+ dev->dev = raw_misc_device.this_device;
+ return 0;
+}
+
+static int raw_release(struct inode *inode, struct file *fd)
+{
+ int ret = 0;
+ struct raw_dev *dev = fd->private_data;
+ unsigned long flags;
+ bool unregister = false;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_CLOSED;
+ if (!dev->gadget) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto out_put;
+ }
+ if (dev->gadget_registered)
+ unregister = true;
+ dev->gadget_registered = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (unregister) {
+ ret = usb_gadget_unregister_driver(&dev->driver);
+ if (ret != 0)
+ dev_err(dev->dev,
+ "usb_gadget_unregister_driver() failed with %d\n",
+ ret);
+ /* Matches kref_get() in raw_ioctl_run(). */
+ kref_put(&dev->count, dev_free);
+ }
+
+out_put:
+ /* Matches dev_new() in raw_open(). */
+ kref_put(&dev->count, dev_free);
+ return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ struct usb_raw_init arg;
+ char *udc_driver_name;
+ char *udc_device_name;
+ unsigned long flags;
+
+ ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
+ if (ret)
+ return ret;
+
+ switch (arg.speed) {
+ case USB_SPEED_UNKNOWN:
+ arg.speed = USB_SPEED_HIGH;
+ break;
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
+ if (!udc_driver_name)
+ return -ENOMEM;
+ ret = strscpy(udc_driver_name, &arg.driver_name[0],
+ UDC_NAME_LENGTH_MAX);
+ if (ret < 0) {
+ kfree(udc_driver_name);
+ return ret;
+ }
+ ret = 0;
+
+ udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
+ if (!udc_device_name) {
+ kfree(udc_driver_name);
+ return -ENOMEM;
+ }
+ ret = strscpy(udc_device_name, &arg.device_name[0],
+ UDC_NAME_LENGTH_MAX);
+ if (ret < 0) {
+ kfree(udc_driver_name);
+ kfree(udc_device_name);
+ return ret;
+ }
+ ret = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_OPENED) {
+ dev_dbg(dev->dev, "fail, device is not opened\n");
+ kfree(udc_driver_name);
+ kfree(udc_device_name);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ dev->udc_name = udc_driver_name;
+
+ dev->driver.function = DRIVER_DESC;
+ dev->driver.max_speed = arg.speed;
+ dev->driver.setup = gadget_setup;
+ dev->driver.disconnect = gadget_disconnect;
+ dev->driver.bind = gadget_bind;
+ dev->driver.unbind = gadget_unbind;
+ dev->driver.suspend = gadget_suspend;
+ dev->driver.resume = gadget_resume;
+ dev->driver.reset = gadget_reset;
+ dev->driver.driver.name = DRIVER_NAME;
+ dev->driver.udc_name = udc_device_name;
+ dev->driver.match_existing_only = 1;
+
+ dev->state = STATE_DEV_INITIALIZED;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (value)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_INITIALIZED) {
+ dev_dbg(dev->dev, "fail, device is not initialized\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = usb_gadget_probe_driver(&dev->driver);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (ret) {
+ dev_err(dev->dev,
+ "fail, usb_gadget_probe_driver returned %d\n", ret);
+ dev->state = STATE_DEV_FAILED;
+ goto out_unlock;
+ }
+ dev->gadget_registered = true;
+ dev->state = STATE_DEV_RUNNING;
+ /* Matches kref_put() in raw_release(). */
+ kref_get(&dev->count);
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ struct usb_raw_event arg;
+ unsigned long flags;
+ struct usb_raw_event *event;
+ uint32_t length;
+
+ ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EINVAL;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ event = raw_event_queue_fetch(&dev->queue);
+ if (!event) {
+ dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ return -EINTR;
+ }
+ length = min(arg.length, event->length);
+ ret = copy_to_user((void __user *)value, event,
+ sizeof(*event) + length);
+ return ret;
+}
+
+static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
+ bool get_from_user)
+{
+ int ret;
+ void *data;
+
+ ret = copy_from_user(io, ptr, sizeof(*io));
+ if (ret)
+ return ERR_PTR(ret);
+ if (io->ep >= USB_RAW_MAX_ENDPOINTS)
+ return ERR_PTR(-EINVAL);
+ if (!usb_raw_io_flags_valid(io->flags))
+ return ERR_PTR(-EINVAL);
+ if (io->length > PAGE_SIZE)
+ return ERR_PTR(-EINVAL);
+ if (get_from_user)
+ data = memdup_user(ptr + sizeof(*io), io->length);
+ else {
+ data = kmalloc(io->length, GFP_KERNEL);
+ if (!data)
+ data = ERR_PTR(-ENOMEM);
+ }
+ return data;
+}
+
+static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
+ void *data, bool in)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (dev->ep0_urb_queued) {
+ dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if ((in && !dev->ep0_in_pending) ||
+ (!in && !dev->ep0_out_pending)) {
+ dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (WARN_ON(in && dev->ep0_out_pending)) {
+ ret = -ENODEV;
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+ if (WARN_ON(!in && dev->ep0_in_pending)) {
+ ret = -ENODEV;
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+
+ dev->req->buf = data;
+ dev->req->length = io->length;
+ dev->req->zero = usb_raw_io_flags_zero(io->flags);
+ dev->ep0_urb_queued = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
+ if (ret) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_queue returned %d\n", ret);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+
+ ret = wait_for_completion_interruptible(&dev->ep0_done);
+ if (ret) {
+ dev_dbg(&dev->gadget->dev, "wait interrupted\n");
+ usb_ep_dequeue(dev->gadget->ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ goto out_done;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ ret = dev->ep0_status;
+
+out_done:
+ dev->ep0_urb_queued = false;
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ void *data;
+ struct usb_raw_ep_io io;
+
+ data = raw_alloc_io_data(&io, (void __user *)value, true);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ret = raw_process_ep0_io(dev, &io, data, true);
+ kfree(data);
+ return ret;
+}
+
+static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ void *data;
+ struct usb_raw_ep_io io;
+ unsigned int length;
+
+ data = raw_alloc_io_data(&io, (void __user *)value, false);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ret = raw_process_ep0_io(dev, &io, data, false);
+ if (ret < 0) {
+ kfree(data);
+ return ret;
+ }
+ length = min(io.length, (unsigned int)ret);
+ ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
+ kfree(data);
+ return ret;
+}
+
+static bool check_ep_caps(struct usb_ep *ep,
+ struct usb_endpoint_descriptor *desc)
+{
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!ep->caps.type_iso)
+ return false;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if (!ep->caps.type_bulk)
+ return false;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (!ep->caps.type_int)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
+ return false;
+ if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
+ return false;
+
+ return true;
+}
+
+static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0, i;
+ unsigned long flags;
+ struct usb_endpoint_descriptor *desc;
+ struct usb_ep *ep = NULL;
+
+ desc = memdup_user((void __user *)value, sizeof(*desc));
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ /*
+ * Endpoints with a maxpacket length of 0 can cause crashes in UDC
+ * drivers.
+ */
+ if (usb_endpoint_maxp(desc) == 0) {
+ dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
+ kfree(desc);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_free;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_free;
+ }
+
+ for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED)
+ continue;
+ break;
+ }
+ if (i == USB_RAW_MAX_ENDPOINTS) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, no device endpoints available\n");
+ ret = -EBUSY;
+ goto out_free;
+ }
+
+ gadget_for_each_ep(ep, dev->gadget) {
+ if (ep->enabled)
+ continue;
+ if (!check_ep_caps(ep, desc))
+ continue;
+ ep->desc = desc;
+ ret = usb_ep_enable(ep);
+ if (ret < 0) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_enable returned %d\n", ret);
+ goto out_free;
+ }
+ dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!dev->eps[i].req) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_alloc_request failed\n");
+ usb_ep_disable(ep);
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ dev->eps[i].ep = ep;
+ dev->eps[i].state = STATE_EP_ENABLED;
+ ep->driver_data = &dev->eps[i];
+ ret = i;
+ goto out_unlock;
+ }
+
+ dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
+ ret = -EBUSY;
+
+out_free:
+ kfree(desc);
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0, i = value;
+ unsigned long flags;
+ const void *desc;
+
+ if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (dev->eps[i].state != STATE_EP_ENABLED) {
+ dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (dev->eps[i].disabling) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, disable already in progress\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (dev->eps[i].urb_queued) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, waiting for urb completion\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ dev->eps[i].disabling = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_disable(dev->eps[i].ep);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ desc = dev->eps[i].ep->desc;
+ dev->eps[i].ep = NULL;
+ dev->eps[i].state = STATE_EP_DISABLED;
+ kfree(desc);
+ dev->eps[i].disabling = false;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
+ struct raw_dev *dev = r_ep->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (req->status)
+ r_ep->status = req->status;
+ else
+ r_ep->status = req->actual;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
]
]
|
| 426/2 |
2026/07/29 09:52 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 25483
output: 31
thoughts: 83
Thoughts: Here's my analysis of the situation:
**Investigating a `gadget_unbind` Modification History**
Okay, so commit `f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10` introduced the `raw_gadget` driver. That's a key piece of information. Now, I need to understand the context of any changes to the `gadget_unbind` function within the `raw_gadget.c` file *before* this commit. I need to see if any previous modifications to that function might have introduced the bug this commit is fixing or provide clues as to how this new driver interacts with that function in an unexpected way. Understanding the history around `gadget_unbind` is critical here; itβs likely where the issue lies, or at the very least, where the symptom is manifesting. Knowing the evolution of that part of the code will paint a fuller picture.
|
| 427/2 |
2026/07/29 09:53 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/usb/gadget/legacy/raw_gadget.c]
Results: map[Output:bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
a5160af78be7 usb: raw-gadget: cap raw_io transfer length to KMALLOC_MAX_SIZE
37b9dd0d114a usb: raw-gadget: do not limit transfer length
b7d49096d595 drivers/usb/gadget: refactor min with min_t
cb787f4ac0c2 [tree-wide] finally take no_llseek out
c3a383d8d382 usb: raw-gadget: report suspend, resume, reset, and disconnect events
1f97e3f429cf usb: raw-gadget: don't disable device if usb_ep_queue fails
e8033bde451e usb: raw-gadget: properly handle interrupted requests
cf9f7a6ee7b1 usb: raw-gadget: return USB_GADGET_DELAYED_STATUS from setup()
]
|
| 428/2 |
2026/07/29 09:53 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 26099
output: 41
thoughts: 33
Thoughts: Okay, here's my interpretation of that thought process, as if it were my own:
**Investigating the History of `gadget_unbind` in `raw_gadget.c`**
Alright, time to dive into this. I need to understand the evolution of the `gadget_unbind` function within the `raw_gadget.c` source file. To do that efficiently, I'll leverage `git-log`. The beauty of `git-log` is its ability to trace the history of code, see who touched it, and understand why changes were made. I'm going to specify the function, `gadget_unbind`, with `SymbolName` within `git-log` to get a precise history of that particular symbol. This should give me a clean, focused view of every commit that modified, added, or removed that specific function from the codebase. That's the best way to get a solid grasp of how `gadget_unbind` has changed over time.
|
| 429/2 |
2026/07/29 09:53 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/legacy/raw_gadget.c SymbolName:gadget_unbind]
Results: map[Output:f2c2e717642c usb: gadget: add raw-gadget interface
]
|
| 430/2 |
2026/07/29 09:53 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 26419
output: 45
thoughts: 362
Thoughts: **Analyzing the Root Cause of the UAF Vulnerability**
Okay, so I'm looking at this UAF vulnerability in the `raw_gadget` driver. From what I can see, `gadget_unbind` was introduced way back in commit `f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10` and hasn't had any changes since then, which is interesting. Let me go over the bug description one more time to make sure I understand it fully.
The root cause seems to be that the driver isn't cleaning up its internal state correctly and that there is a lack of synchronisation during operations. The bug triggers when the USB Device Controller (UDC) is unbound. Specifically, `gadget_unbind()` is the problem, since it **fails to clear `dev->gadget` and update `dev->state`**. It clears the driver data. After it returns, the UDC core frees the `struct usb_gadget` and its associated `struct device` which creates the use-after-free issue. Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
So the problem is in both `gadget_unbind()` and `raw_process_ep0_io()`. I need to confirm that `raw_process_ep0_io` was also introduced in `f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10`.
|
| 431/2 |
2026/07/29 09:53 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/legacy/raw_gadget.c SymbolName:raw_process_ep0_io]
Results: map[Output:1f97e3f429cf usb: raw-gadget: don't disable device if usb_ep_queue fails
e8033bde451e usb: raw-gadget: properly handle interrupted requests
f2c2e717642c usb: gadget: add raw-gadget interface
]
|
| 432/2 |
2026/07/29 09:53 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 27267
output: 26
thoughts: 120
Thoughts: Okay, let's dive into this.
**Debugging USB Raw Gadget Issues**
Alright, so we've got a bug to track down, and my gut feeling is it might be linked to some recent commits in the USB raw gadget code. Let's start by isolating the potential suspects. I'm going to start by reviewing commit `1f97e3f429cf`, which is titled "usb: raw-gadget: don't disable device if usb_ep_queue fails", and then reviewing commit `e8033bde451e`, which has the title "usb: raw-gadget: properly handle interrupted requests". My initial hypothesis leans towards `e8033bde451e`, since that sounds like it might have introduced a regression related to request handling. Let's focus our attention on `e8033bde451e` first.
|
| 433/2 |
2026/07/29 09:53 |
tool |
git-show |
0m
Args:
Results: map[Output:commit e8033bde451eddfb9b1bbd6e2d848c1b5c277222
Author: Andrey Konovalov <andreyknvl@gmail.com>
Date: Thu Oct 26 22:01:12 2023 +0200
usb: raw-gadget: properly handle interrupted requests
Currently, if a USB request that was queued by Raw Gadget is interrupted
(via a signal), wait_for_completion_interruptible returns -ERESTARTSYS.
Raw Gadget then attempts to propagate this value to userspace as a return
value from its ioctls. However, when -ERESTARTSYS is returned by a syscall
handler, the kernel internally restarts the syscall.
This doesn't allow userspace applications to interrupt requests queued by
Raw Gadget (which is required when the emulated device is asked to switch
altsettings). It also violates the implied interface of Raw Gadget that a
single ioctl must only queue a single USB request.
Instead, make Raw Gadget do what GadgetFS does: check whether the request
was interrupted (dequeued with status == -ECONNRESET) and report -EINTR to
userspace.
Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
Cc: stable <stable@kernel.org>
Signed-off-by: Andrey Konovalov <andreyknvl@gmail.com>
Link: https://lore.kernel.org/r/0db45b1d7cc466e3d4d1ab353f61d63c977fbbc5.1698350424.git.andreyknvl@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index b9ecc55a2ce2..ce9e87f84911 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -674,12 +674,12 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
if (WARN_ON(in && dev->ep0_out_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
- goto out_done;
+ goto out_unlock;
}
if (WARN_ON(!in && dev->ep0_in_pending)) {
ret = -ENODEV;
dev->state = STATE_DEV_FAILED;
- goto out_done;
+ goto out_unlock;
}
dev->req->buf = data;
@@ -694,7 +694,7 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
- goto out_done;
+ goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
@@ -703,13 +703,16 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
usb_ep_dequeue(dev->gadget->ep0, dev->req);
wait_for_completion(&dev->ep0_done);
spin_lock_irqsave(&dev->lock, flags);
- goto out_done;
+ if (dev->ep0_status == -ECONNRESET)
+ dev->ep0_status = -EINTR;
+ goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
- ret = dev->ep0_status;
-out_done:
+out_interrupted:
+ ret = dev->ep0_status;
+out_queue_failed:
dev->ep0_urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
@@ -1078,7 +1081,7 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
- goto out_done;
+ goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
@@ -1087,13 +1090,16 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
usb_ep_dequeue(ep->ep, ep->req);
wait_for_completion(&done);
spin_lock_irqsave(&dev->lock, flags);
- goto out_done;
+ if (ep->status == -ECONNRESET)
+ ep->status = -EINTR;
+ goto out_interrupted;
}
spin_lock_irqsave(&dev->lock, flags);
- ret = ep->status;
-out_done:
+out_interrupted:
+ ret = ep->status;
+out_queue_failed:
ep->urb_queued = false;
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
]
|
| 434/2 |
2026/07/29 09:53 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 29133
output: 26
thoughts: 17
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Checking a Hash: Initial Assessment**
Alright, let's take a look at `1f97e3f429cf`. Hmm, just seeing the hash itself doesn't tell me much, of course. My immediate instinct is to start mentally cataloging what this *could* be. Considering its length and format, it looks like a standard hexadecimal hash. Depending on the context I would assume it to be something like MD5, SHA-1, SHA-256 or similar, or potentially a unique identifier within a database, a cryptographic signature, or some form of checksum. The context surrounding this hash is going to be incredibly important for understanding its significance. Am I dealing with a file integrity check, a password hash, a version control commit ID, or something else entirely? Without that information, this is just a string of characters. I need more information to begin to evaluate. I'll need to know where I encountered it, what system is in use, and what the overall goal is to understand it.
|
| 435/2 |
2026/07/29 09:53 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 1f97e3f429cff941b5f7adc9fcbb1b5138cd6125
Author: Andrey Konovalov <andreyknvl@gmail.com>
Date: Thu Oct 26 22:01:13 2023 +0200
usb: raw-gadget: don't disable device if usb_ep_queue fails
During device operation, the host might decide to reset a device emulated
via Raw Gadget. In this case, if the device emulation code has endpoint
requests queued, usb_ep_queue will fail with -ESHUTDOWN. Currently, this
disables the Raw Gadget device and makes the emulation code unable to
proceed.
Do not disable the Raw Gadget device if usb_ep_queue fails.
Signed-off-by: Andrey Konovalov <andreyknvl@gmail.com>
Link: https://lore.kernel.org/r/3c5df3dddb67623b4aeb11c5546370363e65d8e2.1698350424.git.andreyknvl@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index ce9e87f84911..daac1f078516 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -693,7 +693,6 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
- dev->state = STATE_DEV_FAILED;
goto out_queue_failed;
}
@@ -1080,7 +1079,6 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev_err(&dev->gadget->dev,
"fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
- dev->state = STATE_DEV_FAILED;
goto out_queue_failed;
}
]
|
| 436/2 |
2026/07/29 09:53 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 30106
output: 54
thoughts: 851
Thoughts: **Bug Analysis: Unbinding the Gadget**
Alright, let's break this down. My analysis points directly to a crucial oversight in the `gadget_unbind()` function. The problem boils down to a failure to properly clean up the `raw_dev` structure when a USB gadget is unbound. Specifically, `gadget_unbind()` doesn't nullify `dev->gadget` and fails to update `dev->state`. This leaves the system vulnerable to a use-after-free scenario.
Looking at the code, it's immediately apparent. The original implementation, introduced way back in `f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10`, shows this exact behavior. It sets the gadget data to NULL, but it skips the necessary cleanup of the `raw_dev` structure's members, specifically `dev->gadget` and `dev->state`. The vulnerability report hits the nail on the head: the UDC core later frees the `struct usb_gadget` and associated `struct device`, including the device name string. This is precisely what's happening.
I've double-checked that `f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10` is indeed the culprit. I went through the commit history, and it's clear `gadget_unbind` was introduced in that commit and remained unchanged until the fix. There are no other commits that could have introduced the problem.
The fix patch confirms my initial suspicion. It correctly updates the state to `STATE_DEV_FAILED` and adds logic to handle queued URBs, which was also missing. It includes clearing the `dev->gadget` member, which would prevent the UAF issue.
So, in summary, I'm confident that commit `f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10` introduced this bug due to incomplete cleanup during the gadget unbinding process. The fix patch provides a valid solution by addressing the missing state update and member clearing, preventing the use-after-free.
|
| 437/2 |
2026/07/29 09:54 |
tool |
set-results |
0m
Args: map[FixesHash:f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10]
Results: map[FixesHash:f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10]
|
| 438/1 |
2026/07/29 09:54 |
action |
format-fixes |
0m
Results: map[Fixes:map[AuthorEmail:andreyknvl@google.com AuthorName:Andrey Konovalov Hash:f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10 Title:usb: gadget: add raw-gadget interface]]
|
| 439/1 |
2026/07/29 09:54 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:andreyknvl@gmail.com Name:Andrey Konovalov To:false] map[Email:gregkh@linuxfoundation.org Name:Greg Kroah-Hartman To:true] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:krishnagopi487@gmail.com Name:Gopi Krishna Menon To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:linux-usb@vger.kernel.org Name: To:true] map[Email:andreyknvl@google.com Name:Andrey Konovalov To:true]]]
|
| 440/1 |
2026/07/29 09:54 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
usb: raw-gadget: cap raw_io transfer length to KMALLOC_MAX_SIZE
usb: raw-gadget: do not limit transfer length
drivers/usb/gadget: refactor min with min_t
[tree-wide] finally take no_llseek out
usb: raw-gadget: report suspend, resume, reset, and disconnect events
usb: raw-gadget: don't disable device if usb_ep_queue fails
usb: raw-gadget: properly handle interrupted requests
usb: raw-gadget: return USB_GADGET_DELAYED_STATUS from setup()
USB: gadget: Fix the memory leak in raw_gadget driver
USB: gadget: Fix double-free bug in raw_gadget driver
usb: gadget: Fix non-unique driver names in raw-gadget driver
usb: gadget: fix race when gadget driver register via ioctl
USB: gadget: Rename usb_gadget_probe_driver()
usb: raw-gadget: return -EINVAL if no proper ep address available
usb: raw-gadget: use kzalloc
usb: raw-gadget: fix handling of dual-direction-capable endpoints
usb: raw-gadget: add copyright
usb: raw-gadget: fix memory leak in gadget_setup
]
|
| 441/1 |
2026/07/29 09:54 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
The one-line summary should describe the change being made, rather than mention the tool that
detected the bug.
The description must not contain lines starting with '#' because they will dropped by git as comments.
The description must not contain lines starting with '--' or '---' (including inline code diffs)
because they may confuse git/patch utilities.
Your final reply should contain only the text of the commit description.
The one-line summary must be not longer than 72 characters.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Generally try to phrase the description without mentioning syzkaller
(avoid phrases like "the bug was triggered by syzkaller" or "the bug was triggered by fuzzer", etc).
How the bug was triggered is generally an irrelevant detail.
Any bug triggered by a fuzzer can also be triggered by a malicious user, or a buggy program.
If the crash is reported by a sanitizer (e.g., KASAN, KMSAN, lockdep), include the relevant
parts of the sanitizer output to illustrate the problem. Exclude less relevant sections,
as the stack trace can be very long. Describe the execution path that leads to the manifestation
of the kernel bug.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
=======[ 103.606781][ T6395] ==================================================================
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
CPU: 1 UID: 0 PID: 6395 Comm: syz.0.40 Not tainted syzkaller #1 PREEMPT_{RT,(full)}
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
print_address_description+0x55/0x1e0 mm/kasan/report.c:378
print_report+0x58/0x70 mm/kasan/report.c:482
kasan_report+0x117/0x150 mm/kasan/report.c:595
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff39d4ddb2b
Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <89> c2 3d 00 f0 ff ff 77 1c 48 8b 44 24 18 64 48 2b 04 25 28 00 00
RSP: 002b:00007ff39cb3bf50 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007ff39d4ddb2b
RDX: 00007ff39cb3bfd0 RSI: 00000000c0085504 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 00007ff39cb3bfd8
R13: 0000000000000000 R14: 00007ff39d765fa0 R15: 00007ffff182d2e8
</TASK>
Allocated by task 6385:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
__kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415
kasan_kmalloc include/linux/kasan.h:263 [inline]
__do_kmalloc_node mm/slub.c:5334 [inline]
__kmalloc_node_track_caller_noprof+0x57f/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kobject_set_name_vargs+0x61/0x110 lib/kobject.c:274
dev_set_name+0xe2/0x140 drivers/base/core.c:3560
usb_add_gadget+0x475/0x700 drivers/usb/gadget/udc/core.c:1459
usb_add_gadget_udc_release+0x1a4/0x1f0 drivers/usb/gadget/udc/core.c:1517
dummy_udc_probe+0x6e1/0x7e0 drivers/usb/gadget/udc/dummy_hcd.c:1121
platform_probe+0xf9/0x190 drivers/base/platform.c:1439
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
device_driver_attach+0xe0/0x1d0 drivers/base/dd.c:1203
bind_store+0x1d3/0x220 drivers/base/bus.c:267
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 6393:
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2677 [inline]
slab_free mm/slub.c:6377 [inline]
kfree+0x1c5/0x6c0 mm/slub.c:6692
kobject_cleanup lib/kobject.c:695 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x267/0x550 lib/kobject.c:737
device_remove drivers/base/dd.c:616 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x48b/0x880 drivers/base/dd.c:1372
unbind_store+0x1a4/0x1d0 drivers/base/bus.c:244
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff888195535600
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 0 bytes inside of
freed 16-byte region [ffff888195535600, ffff888195535610)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888195535dc0 pfn:0x195535
flags: 0x500000000000200(workingset|node=1|zone=2)
page_type: f5(slab)
raw: 0500000000000200 ffff888100041640 ffff888180400588 ffffea000617cc10
raw: ffff888195535dc0 000000080080006e 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2cc0(GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5915, tgid 5915 (udevd), ts 101560327999, free_ts 101558925626
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1f9/0x250 mm/page_alloc.c:1859
prep_new_page mm/page_alloc.c:1867 [inline]
get_page_from_freelist+0x2639/0x26b0 mm/page_alloc.c:3946
__alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5304
alloc_slab_page mm/slub.c:3266 [inline]
allocate_slab+0x79/0x5e0 mm/slub.c:3380
new_slab mm/slub.c:3426 [inline]
refill_objects+0x2d8/0x350 mm/slub.c:7310
refill_sheaf mm/slub.c:2804 [inline]
__pcs_replace_empty_main+0x330/0x690 mm/slub.c:4675
alloc_from_pcs mm/slub.c:4773 [inline]
slab_alloc_node mm/slub.c:4905 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x62d/0x7c0 mm/slub.c:5471
kvasprintf+0xeb/0x1a0 lib/kasprintf.c:25
kasprintf+0xe2/0x140 lib/kasprintf.c:59
device_get_devnode+0xb6/0x2a0 drivers/base/core.c:4056
dev_uevent+0x16c/0x870 drivers/base/core.c:2738
uevent_show+0x1a0/0x310 drivers/base/core.c:2826
dev_attr_show+0x58/0xc0 drivers/base/core.c:2487
sysfs_kf_seq_show+0x331/0x490 fs/sysfs/file.c:65
seq_read_iter+0x464/0xcb0 fs/seq_file.c:231
new_sync_read fs/read_write.c:493 [inline]
vfs_read+0x59e/0xa90 fs/read_write.c:574
page last free pid 5009 tgid 5009 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
__free_pages_prepare mm/page_alloc.c:1406 [inline]
__free_frozen_pages+0x10de/0x11c0 mm/page_alloc.c:2950
__slab_free+0x252/0x2a0 mm/slub.c:5741
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4584 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
kmem_cache_alloc_lru_noprof+0x353/0x650 mm/slub.c:4950
shmem_alloc_inode+0x28/0x40 mm/shmem.c:5090
alloc_inode+0x6a/0x1b0 fs/inode.c:338
new_inode+0x1f/0x170 fs/inode.c:1174
__shmem_get_inode mm/shmem.c:3024 [inline]
shmem_get_inode+0x3b1/0xf80 mm/shmem.c:3096
shmem_mknod+0x17d/0x340 mm/shmem.c:3806
lookup_open fs/namei.c:4508 [inline]
open_last_lookups fs/namei.c:4608 [inline]
path_openat+0x1344/0x3850 fs/namei.c:4860
do_file_open+0x23e/0x4a0 fs/namei.c:4892
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
Memory state around the buggy address:
ffff888195535500: 00 00 fc fc fa fb fc fc fa fb fc fc fa fb fc fc
ffff888195535580: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc
>ffff888195535600: fa fb fc fc 00 04 fc fc fc fc fc fc fc fc fc fc
^
ffff888195535680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888195535700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
The explanation of the root cause of the bug is:
The root cause of this crash is a use-after-free (UAF) vulnerability in the `raw_gadget` driver. It occurs because the driver fails to properly clean up its internal state and synchronize concurrent operations when the underlying USB Device Controller (UDC) is unbound.
### Detailed Sequence of Events
1. **Gadget Initialization and Binding:**
The reproducer opens `/dev/raw-gadget` and initializes it. When the gadget binds to the `dummy_udc` controller, the UDC core invokes the `gadget_bind()` callback. This function saves the `struct usb_gadget` pointer into `dev->gadget` and eventually sets `dev->state = STATE_DEV_RUNNING`.
2. **UDC Unbinding:**
The reproducer then writes to `/sys/bus/platform/drivers/dummy_udc/unbind` via sysfs. This forces the UDC core to unbind the gadget driver, which invokes the `gadget_unbind()` callback in `raw_gadget.c`:
```c
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
kref_put(&dev->count, dev_free);
}
```
Notice that `gadget_unbind()` **fails to clear `dev->gadget`** and **fails to update `dev->state`**. It only clears the driver data. After this function returns, the UDC core proceeds to free the `struct usb_gadget` and its associated `struct device` (including the device name string allocated via `kvasprintf`).
3. **Dangling Pointer Dereference:**
The reproducer then issues a `USB_RAW_IOCTL_EP0_READ` (or `WRITE`) ioctl, which lands in `raw_process_ep0_io()`:
```c
static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, void *data, bool in)
{
// ...
spin_lock_irqsave(&dev->lock, flags);
if (dev->state != STATE_DEV_RUNNING) { // Passes, state was never updated
// ...
}
if (!dev->gadget) { // Passes, dev->gadget is a dangling pointer
// ...
}
// ...
dev->ep0_urb_queued = true;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
dev_err(&dev->gadget->dev, "fail, usb_ep_queue returned %d\n", ret);
// ...
```
Because the state checks pass, the ioctl proceeds to call `usb_ep_queue()` using the freed gadget pointer.
4. **The Crash:**
Since the UDC is disconnected/freed, `usb_ep_queue()` returns an error (e.g., `-ESHUTDOWN`). The error path then calls `dev_err(&dev->gadget->dev, ...)`. The `dev_err()` macro attempts to print the device name (`dev->gadget->dev.kobj.name`), which points to the freed 16-byte string, resulting in the KASAN `slab-use-after-free` crash observed in the trace.
### How to Fix
To fix this bug, `gadget_unbind()` must clear the `dev->gadget` pointer and update `dev->state` (e.g., to `STATE_DEV_FAILED`) while holding `dev->lock`.
However, simply clearing the pointer is not enough due to race conditions. If an ioctl thread has already passed the `!dev->gadget` check and released `dev->lock`, but hasn't yet called `usb_ep_queue()` (which is called lockless), `gadget_unbind()` could return and free the gadget right under the ioctl's feet.
To prevent this race, `raw_gadget` needs a synchronization mechanism to track ongoing UDC calls and wait for them to finish before returning from `gadget_unbind()`. A common pattern for this (as seen in `drivers/usb/gadget/legacy/inode.c` for `gadgetfs`) is to introduce a `udc_usage` counter:
- Increment `udc_usage` under `dev->lock` before releasing the lock to call `usb_ep_queue()`.
- Decrement `udc_usage` under `dev->lock` after `usb_ep_queue()` returns.
- In `gadget_unbind()`, wait for `udc_usage` to drop to `0` before returning.
The diff of the bug fix is:
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 4febf8dac..bce4f8d3f 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -175,6 +175,8 @@ struct raw_dev {
bool gadget_registered;
struct usb_gadget *gadget;
struct usb_request *req;
+ int udc_usage;
+ struct completion *unbind_completion;
bool ep0_in_pending;
bool ep0_out_pending;
bool ep0_urb_queued;
@@ -205,27 +207,13 @@ static struct raw_dev *dev_new(void)
static void dev_free(struct kref *kref)
{
struct raw_dev *dev = container_of(kref, struct raw_dev, count);
- int i;
kfree(dev->udc_name);
kfree(dev->driver.udc_name);
kfree(dev->driver.driver.name);
if (dev->driver_id_number >= 0)
ida_free(&driver_id_numbers, dev->driver_id_number);
- if (dev->req) {
- if (dev->ep0_urb_queued)
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- usb_ep_free_request(dev->gadget->ep0, dev->req);
- }
raw_event_queue_destroy(&dev->queue);
- for (i = 0; i < dev->eps_num; i++) {
- if (dev->eps[i].state == STATE_EP_DISABLED)
- continue;
- usb_ep_disable(dev->eps[i].ep);
- usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
- kfree(dev->eps[i].ep->desc);
- dev->eps[i].state = STATE_EP_DISABLED;
- }
kfree(dev);
}
@@ -316,6 +304,11 @@ static int gadget_bind(struct usb_gadget *gadget,
ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
if (ret < 0) {
dev_err(&gadget->dev, "failed to queue connect event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->gadget = NULL;
+ dev->req = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_free_request(gadget->ep0, req);
set_gadget_data(gadget, NULL);
return ret;
}
@@ -328,6 +321,47 @@ static int gadget_bind(struct usb_gadget *gadget,
static void gadget_unbind(struct usb_gadget *gadget)
{
struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+ int i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+
+ if (dev->ep0_urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(gadget->ep0, dev->req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_ENABLED && dev->eps[i].urb_queued) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ usb_ep_dequeue(dev->eps[i].ep, dev->eps[i].req);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+
+ if (dev->udc_usage > 0) {
+ dev->unbind_completion = &done;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ dev->gadget = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ if (dev->req) {
+ usb_ep_free_request(gadget->ep0, dev->req);
+ dev->req = NULL;
+ }
set_gadget_data(gadget, NULL);
/* Matches kref_get() in gadget_bind(). */
@@ -637,11 +671,11 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
event = raw_event_queue_fetch(&dev->queue);
if (PTR_ERR(event) == -EINTR) {
- dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ dev_dbg(dev->dev, "event fetching interrupted\n");
return -EINTR;
}
if (IS_ERR(event)) {
- dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ dev_err(dev->dev, "failed to fetch event\n");
spin_lock_irqsave(&dev->lock, flags);
dev->state = STATE_DEV_FAILED;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -723,22 +757,37 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
dev->req->length = io->length;
dev->req->zero = usb_raw_io_flags_zero(io->flags);
dev->ep0_urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&dev->ep0_done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(dev->gadget->ep0, dev->req);
- wait_for_completion(&dev->ep0_done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *ep0 = dev->gadget->ep0;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (dev->ep0_status == -ECONNRESET)
dev->ep0_status = -EINTR;
goto out_interrupted;
@@ -750,6 +799,9 @@ static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = dev->ep0_status;
out_queue_failed:
dev->ep0_urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
@@ -956,11 +1008,15 @@ static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
goto out_unlock;
}
dev->eps[i].disabling = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
usb_ep_disable(dev->eps[i].ep);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
kfree(dev->eps[i].ep->desc);
dev->eps[i].state = STATE_EP_DISABLED;
@@ -1109,22 +1165,37 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ep->req->length = io->length;
ep->req->zero = usb_raw_io_flags_zero(io->flags);
ep->urb_queued = true;
+ dev->udc_usage++;
spin_unlock_irqrestore(&dev->lock, flags);
ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
if (ret) {
- dev_err(&dev->gadget->dev,
- "fail, usb_ep_queue returned %d\n", ret);
+ dev_err(dev->dev, "fail, usb_ep_queue returned %d\n", ret);
spin_lock_irqsave(&dev->lock, flags);
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
goto out_queue_failed;
}
ret = wait_for_completion_interruptible(&done);
if (ret) {
- dev_dbg(&dev->gadget->dev, "wait interrupted\n");
- usb_ep_dequeue(ep->ep, ep->req);
- wait_for_completion(&done);
+ dev_dbg(dev->dev, "wait interrupted\n");
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_FAILED && dev->gadget) {
+ struct usb_ep *usb_ep = ep->ep;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_dequeue(usb_ep, ep->req);
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ }
if (ep->status == -ECONNRESET)
ep->status = -EINTR;
goto out_interrupted;
@@ -1136,6 +1207,9 @@ static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
ret = ep->status;
out_queue_failed:
ep->urb_queued = false;
+ dev->udc_usage--;
+ if (!dev->udc_usage && dev->unbind_completion)
+ complete(dev->unbind_completion);
out_unlock:
spin_unlock_irqrestore(&dev->lock, flags);
return ret;
Additional description of the patch:
The `refcount_t: addition on 0; use-after-free` crash in `usb_add_gadget()` is a secondary effect of a use-after-free on the endpoint requests (`dev->req` and `dev->eps[i].req`) caused by the previous patch.
### Root Cause of the Crash
In the previous patch, `gadget_unbind()` was modified to wait for `udc_usage` to drop to 0 before freeing `dev->req` and `dev->eps[i].req`. However, to avoid a deadlock with the UDC core, the patch dropped `udc_usage` while the ioctls were waiting for the I/O completion (`wait_for_completion_interruptible()`).
Because `udc_usage` was 0 while the requests were still queued in the UDC, `gadget_unbind()` would proceed and free the requests *while they were still in use by the UDC core*. When the UDC core later stopped the controller (which happens after `gadget_unbind()` returns), it accessed the freed requests to complete them. This use-after-free corrupted memory, which in this case happened to overwrite the `refcount` of the `struct device` embedded in the gadget, leading to the `refcount_t: addition on 0` crash when the gadget was re-registered.
### The Fix
To fix this properly without deadlocking, we must ensure that `gadget_unbind()` explicitly dequeues all pending requests before waiting for `udc_usage` to drop to 0.
1. **Hold `udc_usage` for the entire I/O duration:**
Instead of dropping `udc_usage` while waiting for completion, we hold it. This ensures `gadget_unbind()` will wait for the ioctls to fully finish.
2. **Explicitly dequeue requests in `gadget_unbind()`:**
To prevent the deadlock, `gadget_unbind()` must explicitly call `usb_ep_dequeue()` on all queued requests before waiting for `udc_usage`. This forces the UDC core to complete the requests immediately. The completion handlers will run, wake up the waiting ioctls, and the ioctls will decrement `udc_usage` and signal the completion.
3. **Handle interruptions correctly:**
If an ioctl is interrupted by a signal, it checks if `dev->state == STATE_DEV_FAILED` (which is set by `gadget_unbind()`). If so, it knows `gadget_unbind()` is handling the dequeue, so it just waits for the completion handler to finish without calling `usb_ep_dequeue()` itself.
4. **Proper cleanup in `gadget_bind()`:**
If `gadget_bind()` fails (e.g., event queue is full), it must clear `dev->gadget` and `dev->req` before freeing the request, as `gadget_unbind()` will not be called in this case.
I have applied these fixes to the patch.
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
usb: raw-gadget: cap raw_io transfer length to KMALLOC_MAX_SIZE
usb: raw-gadget: do not limit transfer length
drivers/usb/gadget: refactor min with min_t
[tree-wide] finally take no_llseek out
usb: raw-gadget: report suspend, resume, reset, and disconnect events
usb: raw-gadget: don't disable device if usb_ep_queue fails
usb: raw-gadget: properly handle interrupted requests
usb: raw-gadget: return USB_GADGET_DELAYED_STATUS from setup()
USB: gadget: Fix the memory leak in raw_gadget driver
USB: gadget: Fix double-free bug in raw_gadget driver
usb: gadget: Fix non-unique driver names in raw-gadget driver
usb: gadget: fix race when gadget driver register via ioctl
USB: gadget: Rename usb_gadget_probe_driver()
usb: raw-gadget: return -EINVAL if no proper ep address available
usb: raw-gadget: use kzalloc
usb: raw-gadget: fix handling of dual-direction-capable endpoints
usb: raw-gadget: add copyright
usb: raw-gadget: fix memory leak in gadget_setup
Reply: usb: raw-gadget: fix use-after-free during gadget unbind
A use-after-free vulnerability occurs in the raw-gadget driver when the
underlying USB Device Controller (UDC) is unbound while concurrent ioctls
are in progress.
When the UDC is unbound, gadget_unbind() is called. However, this function
fails to clear the dev->gadget pointer and update dev->state. It only
clears the driver data. After gadget_unbind() returns, the UDC core frees
the struct usb_gadget and its associated device name string.
If a concurrent ioctl (e.g., USB_RAW_IOCTL_EP0_READ) is executing, it may
pass the state checks because dev->state was not updated, and proceed to
use the dangling dev->gadget pointer. If usb_ep_queue() fails (e.g., due to
the UDC being disconnected), the error path attempts to print an error
message using dev_err(&dev->gadget->dev, ...), which dereferences the freed
device name string, resulting in a KASAN slab-use-after-free crash:
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648
[inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
...
Call Trace:
<TASK>
...
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
...
To fix this, gadget_unbind() must clear dev->gadget and update dev->state
to STATE_DEV_FAILED under dev->lock.
To prevent race conditions where an ioctl has already passed the checks but
hasn't yet called usb_ep_queue(), a synchronization mechanism is
introduced. A udc_usage counter tracks ongoing UDC calls. gadget_unbind()
waits for this counter to drop to zero before returning.
To avoid deadlocks where ioctls are waiting for I/O completion,
gadget_unbind() explicitly dequeues all pending requests before waiting for
udc_usage to drop. This forces the UDC core to complete the requests
immediately, waking up the waiting ioctls so they can decrement udc_usage
and signal the completion.
Additionally, if an ioctl is interrupted by a signal, it checks if
dev->state == STATE_DEV_FAILED. If so, it knows gadget_unbind() is handling
the dequeue and simply waits for the completion handler to finish. Finally,
proper cleanup is added in gadget_bind() if the connect event fails to
queue, and dev_err() calls are updated to use dev->dev instead of
dev->gadget->dev to avoid dereferencing the gadget pointer in error paths.
|
| 442/2 |
2026/07/29 09:54 |
llm |
description-generator |
0m
Model:
Tokens:
input: 14172
output: 932
thoughts: 3923
Thoughts: **Fix for Use-After-Free in raw-gadget Driver During Unbind**
Okay, so I've been digging into this `raw-gadget` driver and I've found a nasty use-after-free bug. It happens when the underlying USB Device Controller (UDC) is unbound while there are concurrent ioctls running. Basically, when `gadget_unbind()` is called, it doesn't correctly clear the `dev->gadget` pointer or update `dev->state`. It only cleans up the driver's data. After `gadget_unbind()` returns, the UDC core goes ahead and frees the `struct usb_gadget` and its device name string. If an ioctl, like `USB_RAW_IOCTL_EP0_READ`, is running concurrently, it might slip past the state checks because `dev->state` hasn't been updated, and then tries to use that dangling `dev->gadget` pointer. If `usb_ep_queue()` fails, maybe because the UDC got disconnected, the error path uses `dev_err(&dev->gadget->dev, ...)` to print an error message, but that now dereferences the freed name string and we get a KASAN slab-use-after-free crash:
```
BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:648 [inline]
BUG: KASAN: slab-use-after-free in string+0x216/0x2d0 lib/vsprintf.c:730
Read of size 1 at addr ffff888195535600 by task syz.0.40/6395
...
Call Trace:
<TASK>
...
string_nocheck lib/vsprintf.c:648 [inline]
string+0x216/0x2d0 lib/vsprintf.c:730
vsnprintf+0x74a/0xef0 lib/vsprintf.c:2945
snprintf+0xe8/0x140 lib/vsprintf.c:3043
set_dev_info drivers/base/core.c:4984 [inline]
dev_vprintk_emit+0x30f/0x400 drivers/base/core.c:4994
dev_printk_emit+0xee/0x140 drivers/base/core.c:5007
_dev_err+0x11e/0x180 drivers/base/core.c:5062
raw_process_ep0_io+0x531/0x990 drivers/usb/gadget/legacy/raw_gadget.c:730
raw_ioctl_ep0_read drivers/usb/gadget/legacy/raw_gadget.c:782 [inline]
raw_ioctl+0x2721/0x40d0 drivers/usb/gadget/legacy/raw_gadget.c:1316
...
```
To fix this mess, `gadget_unbind()` needs to first set `dev->gadget` to NULL and update `dev->state` to `STATE_DEV_FAILED`, all under `dev->lock`, of course. However, there's a race: an ioctl could have passed the checks but hasn't called `usb_ep_queue()` yet. So, I introduced a `udc_usage` counter to track ongoing UDC calls. Now, `gadget_unbind()` waits for this counter to reach zero before it returns, that gives existing calls time to finish. To prevent a deadlock where an ioctl is waiting for I/O completion, I made `gadget_unbind()` explicitly dequeue all pending requests with `usb_ep_dequeue()` before waiting. This forces the UDC core to complete those requests ASAP, waking up any waiting ioctls so they can decrement `udc_usage`. Also, if an ioctl gets interrupted by a signal, it checks `dev->state`. If it's `STATE_DEV_FAILED`, the ioctl knows that `gadget_unbind()` is handling the dequeue and it just waits for the completion handler to finish. Finally, I added some proper cleanup in `gadget_bind()` if the connect event fails to queue, and crucially I updated `dev_err()` calls to use `dev->dev` instead of `dev->gadget->dev` which avoids that whole dangling pointer issue in error paths and stops the UAF.
|