Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 9f6ce4240a2b ("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

authored by

Kuen-Han Tsai and committed by
Greg Kroah-Hartman
75a5b8d4 201c53c6

+33 -45
+33 -45
drivers/usb/gadget/function/f_ncm.c
··· 11 11 * Copyright (C) 2008 Nokia Corporation 12 12 */ 13 13 14 + #include <linux/cleanup.h> 14 15 #include <linux/kernel.h> 15 16 #include <linux/interrupt.h> 16 17 #include <linux/module.h> ··· 21 20 #include <linux/string_choices.h> 22 21 23 22 #include <linux/usb/cdc.h> 23 + #include <linux/usb/gadget.h> 24 24 25 25 #include "u_ether.h" 26 26 #include "u_ether_configfs.h" ··· 1438 1436 struct usb_ep *ep; 1439 1437 struct f_ncm_opts *ncm_opts; 1440 1438 1439 + struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; 1440 + struct usb_request *request __free(free_usb_request) = NULL; 1441 + 1441 1442 if (!can_support_ecm(cdev->gadget)) 1442 1443 return -EINVAL; 1443 1444 1444 1445 ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); 1445 1446 1446 1447 if (cdev->use_os_string) { 1447 - f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), 1448 - GFP_KERNEL); 1449 - if (!f->os_desc_table) 1448 + os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); 1449 + if (!os_desc_table) 1450 1450 return -ENOMEM; 1451 - f->os_desc_n = 1; 1452 - f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; 1453 1451 } 1454 1452 1455 1453 mutex_lock(&ncm_opts->lock); ··· 1461 1459 mutex_unlock(&ncm_opts->lock); 1462 1460 1463 1461 if (status) 1464 - goto fail; 1462 + return status; 1465 1463 1466 1464 ncm_opts->bound = true; 1467 1465 1468 1466 us = usb_gstrings_attach(cdev, ncm_strings, 1469 1467 ARRAY_SIZE(ncm_string_defs)); 1470 - if (IS_ERR(us)) { 1471 - status = PTR_ERR(us); 1472 - goto fail; 1473 - } 1468 + if (IS_ERR(us)) 1469 + return PTR_ERR(us); 1470 + 1474 1471 ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; 1475 1472 ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; 1476 1473 ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; ··· 1479 1478 /* allocate instance-specific interface IDs */ 1480 1479 status = usb_interface_id(c, f); 1481 1480 if (status < 0) 1482 - goto fail; 1481 + return status; 1483 1482 ncm->ctrl_id = status; 1484 1483 ncm_iad_desc.bFirstInterface = status; 1485 1484 1486 1485 ncm_control_intf.bInterfaceNumber = status; 1487 1486 ncm_union_desc.bMasterInterface0 = status; 1488 1487 1489 - if (cdev->use_os_string) 1490 - f->os_desc_table[0].if_id = 1491 - ncm_iad_desc.bFirstInterface; 1492 - 1493 1488 status = usb_interface_id(c, f); 1494 1489 if (status < 0) 1495 - goto fail; 1490 + return status; 1496 1491 ncm->data_id = status; 1497 1492 1498 1493 ncm_data_nop_intf.bInterfaceNumber = status; ··· 1497 1500 1498 1501 ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size); 1499 1502 1500 - status = -ENODEV; 1501 - 1502 1503 /* allocate instance-specific endpoints */ 1503 1504 ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc); 1504 1505 if (!ep) 1505 - goto fail; 1506 + return -ENODEV; 1506 1507 ncm->port.in_ep = ep; 1507 1508 1508 1509 ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc); 1509 1510 if (!ep) 1510 - goto fail; 1511 + return -ENODEV; 1511 1512 ncm->port.out_ep = ep; 1512 1513 1513 1514 ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc); 1514 1515 if (!ep) 1515 - goto fail; 1516 + return -ENODEV; 1516 1517 ncm->notify = ep; 1517 1518 1518 - status = -ENOMEM; 1519 - 1520 1519 /* allocate notification request and buffer */ 1521 - ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); 1522 - if (!ncm->notify_req) 1523 - goto fail; 1524 - ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); 1525 - if (!ncm->notify_req->buf) 1526 - goto fail; 1527 - ncm->notify_req->context = ncm; 1528 - ncm->notify_req->complete = ncm_notify_complete; 1520 + request = usb_ep_alloc_request(ep, GFP_KERNEL); 1521 + if (!request) 1522 + return -ENOMEM; 1523 + request->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); 1524 + if (!request->buf) 1525 + return -ENOMEM; 1526 + request->context = ncm; 1527 + request->complete = ncm_notify_complete; 1529 1528 1530 1529 /* 1531 1530 * support all relevant hardware speeds... we expect that when ··· 1541 1548 status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, 1542 1549 ncm_ss_function, ncm_ss_function); 1543 1550 if (status) 1544 - goto fail; 1551 + return status; 1545 1552 1546 1553 /* 1547 1554 * NOTE: all that is done without knowing or caring about ··· 1554 1561 1555 1562 hrtimer_setup(&ncm->task_timer, ncm_tx_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); 1556 1563 1564 + if (cdev->use_os_string) { 1565 + os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; 1566 + os_desc_table[0].if_id = ncm_iad_desc.bFirstInterface; 1567 + f->os_desc_table = no_free_ptr(os_desc_table); 1568 + f->os_desc_n = 1; 1569 + } 1570 + ncm->notify_req = no_free_ptr(request); 1571 + 1557 1572 DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", 1558 1573 ncm->port.in_ep->name, ncm->port.out_ep->name, 1559 1574 ncm->notify->name); 1560 1575 return 0; 1561 - 1562 - fail: 1563 - kfree(f->os_desc_table); 1564 - f->os_desc_n = 0; 1565 - 1566 - if (ncm->notify_req) { 1567 - kfree(ncm->notify_req->buf); 1568 - usb_ep_free_request(ncm->notify, ncm->notify_req); 1569 - } 1570 - 1571 - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 1572 - 1573 - return status; 1574 1576 } 1575 1577 1576 1578 static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)