mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
usb: gadget: f_ncm: Refactor bind path to use __free()
After an bind/unbind cycle, the ncm->notify_req is left stale. If a
subsequent bind fails, the unified error label attempts to free this
stale request, leading to a NULL pointer dereference when accessing
ep->ops->free_request.
Refactor the error handling in the bind path to use the __free()
automatic cleanup mechanism.
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020
Call trace:
usb_ep_free_request+0x2c/0xec
ncm_bind+0x39c/0x3dc
usb_add_function+0xcc/0x1f0
configfs_composite_bind+0x468/0x588
gadget_bind_driver+0x104/0x270
really_probe+0x190/0x374
__driver_probe_device+0xa0/0x12c
driver_probe_device+0x3c/0x218
__device_attach_driver+0x14c/0x188
bus_for_each_drv+0x10c/0x168
__device_attach+0xfc/0x198
device_initial_probe+0x14/0x24
bus_probe_device+0x94/0x11c
device_add+0x268/0x48c
usb_add_gadget+0x198/0x28c
dwc3_gadget_init+0x700/0x858
__dwc3_set_mode+0x3cc/0x664
process_scheduled_works+0x1d8/0x488
worker_thread+0x244/0x334
kthread+0x114/0x1bc
ret_from_fork+0x10/0x20
Fixes: 9f6ce4240a ("usb: gadget: f_ncm.c added")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250916-ready-v1-3-4997bf277548@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250916-ready-v1-3-4997bf277548@google.com
This commit is contained in:
parent
201c53c687
commit
75a5b8d4dd
1 changed files with 33 additions and 45 deletions
|
|
@ -11,6 +11,7 @@
|
|||
* Copyright (C) 2008 Nokia Corporation
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
#include <linux/string_choices.h>
|
||||
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_ether_configfs.h"
|
||||
|
|
@ -1436,18 +1438,18 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
struct usb_ep *ep;
|
||||
struct f_ncm_opts *ncm_opts;
|
||||
|
||||
struct usb_os_desc_table *os_desc_table __free(kfree) = 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) {
|
||||
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
|
||||
GFP_KERNEL);
|
||||
if (!f->os_desc_table)
|
||||
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
|
||||
if (!os_desc_table)
|
||||
return -ENOMEM;
|
||||
f->os_desc_n = 1;
|
||||
f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
|
||||
}
|
||||
|
||||
mutex_lock(&ncm_opts->lock);
|
||||
|
|
@ -1459,16 +1461,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
mutex_unlock(&ncm_opts->lock);
|
||||
|
||||
if (status)
|
||||
goto fail;
|
||||
return status;
|
||||
|
||||
ncm_opts->bound = true;
|
||||
|
||||
us = usb_gstrings_attach(cdev, ncm_strings,
|
||||
ARRAY_SIZE(ncm_string_defs));
|
||||
if (IS_ERR(us)) {
|
||||
status = PTR_ERR(us);
|
||||
goto fail;
|
||||
}
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
|
||||
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
|
||||
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
|
||||
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
|
||||
|
|
@ -1478,20 +1479,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
return status;
|
||||
ncm->ctrl_id = status;
|
||||
ncm_iad_desc.bFirstInterface = status;
|
||||
|
||||
ncm_control_intf.bInterfaceNumber = status;
|
||||
ncm_union_desc.bMasterInterface0 = status;
|
||||
|
||||
if (cdev->use_os_string)
|
||||
f->os_desc_table[0].if_id =
|
||||
ncm_iad_desc.bFirstInterface;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
return status;
|
||||
ncm->data_id = status;
|
||||
|
||||
ncm_data_nop_intf.bInterfaceNumber = status;
|
||||
|
|
@ -1500,35 +1497,31 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
/* allocate instance-specific endpoints */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
return -ENODEV;
|
||||
ncm->port.in_ep = ep;
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
return -ENODEV;
|
||||
ncm->port.out_ep = ep;
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
return -ENODEV;
|
||||
ncm->notify = ep;
|
||||
|
||||
status = -ENOMEM;
|
||||
|
||||
/* allocate notification request and buffer */
|
||||
ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
|
||||
if (!ncm->notify_req)
|
||||
goto fail;
|
||||
ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
|
||||
if (!ncm->notify_req->buf)
|
||||
goto fail;
|
||||
ncm->notify_req->context = ncm;
|
||||
ncm->notify_req->complete = ncm_notify_complete;
|
||||
request = usb_ep_alloc_request(ep, GFP_KERNEL);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
request->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
|
||||
if (!request->buf)
|
||||
return -ENOMEM;
|
||||
request->context = ncm;
|
||||
request->complete = ncm_notify_complete;
|
||||
|
||||
/*
|
||||
* support all relevant hardware speeds... we expect that when
|
||||
|
|
@ -1548,7 +1541,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
|
||||
ncm_ss_function, ncm_ss_function);
|
||||
if (status)
|
||||
goto fail;
|
||||
return status;
|
||||
|
||||
/*
|
||||
* NOTE: all that is done without knowing or caring about
|
||||
|
|
@ -1561,23 +1554,18 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
hrtimer_setup(&ncm->task_timer, ncm_tx_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
if (cdev->use_os_string) {
|
||||
os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
|
||||
os_desc_table[0].if_id = ncm_iad_desc.bFirstInterface;
|
||||
f->os_desc_table = no_free_ptr(os_desc_table);
|
||||
f->os_desc_n = 1;
|
||||
}
|
||||
ncm->notify_req = no_free_ptr(request);
|
||||
|
||||
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
|
||||
ncm->port.in_ep->name, ncm->port.out_ep->name,
|
||||
ncm->notify->name);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(f->os_desc_table);
|
||||
f->os_desc_n = 0;
|
||||
|
||||
if (ncm->notify_req) {
|
||||
kfree(ncm->notify_req->buf);
|
||||
usb_ep_free_request(ncm->notify, ncm->notify_req);
|
||||
}
|
||||
|
||||
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue