2022-11-04 13:24:56

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 0/2] use-after-free issues in configfs

This series addresses a few problems with the users of the gether code.
The problem arises when a UDC is disconnected from a gadget created with
configfs doing a "echo '' > UDC". It seems the existing code is tested
up to the point where the gadget from configfs is up, tearing it down
still seems to make problems. I for myself am also not interested in tearing
it down, but I see use-after-free issues when doing a reboot -f.

The underlying problem is that the eth_dev returned by the gether code is used
for multiple bind/unbind cycles, but only initialized properly once.

The usb_gadget * is only valid between bind and unbind, so it is not a suitable
parent for the net_device whose lifetime spans multiple bind/unbind cycles.

I solved the issues for the f_ecm driver, similar problems exist in the other users
like f_eem or f_ncm as well. I can prepare patches for these once it's clear
that this is really the way to go.

Sascha Hauer (2):
usb: gadget: u_ether: Do not make UDC parent of the net device
usb: gadget: f_ecm: Always set current gadget in ecm_bind()

drivers/usb/gadget/function/f_ecm.c | 22 +++++++++-------------
drivers/usb/gadget/function/u_ether.c | 4 ----
2 files changed, 9 insertions(+), 17 deletions(-)

--
2.30.2



2022-11-04 13:25:30

by Sascha Hauer

[permalink] [raw]
Subject: [PATCH 2/2] usb: gadget: f_ecm: Always set current gadget in ecm_bind()

The gadget may change over bind/unbind cycles, so set it each time during
bind, not only the first time. Without it we get a use-after-free with
the following example:

cd /sys/kernel/config/usb_gadget/; mkdir -p mygadget; cd mygadget
mkdir -p configs/c.1/strings/0x409
echo "C1:Composite Device" > configs/c.1/strings/0x409/configuration
mkdir -p functions/ecm.usb0
ln -s functions/ecm.usb0 configs/c.1/
rmmod dummy_hcd
modprobe dummy_hcd

KASAN will complain shortly after the 'modprobe':

usb 2-1: New USB device found, idVendor=0000, idProduct=0000, bcdDevice= 6.01
usb 2-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
==================================================================
BUG: KASAN: use-after-free in gether_connect+0xb8/0x30c
Read of size 4 at addr cbef170c by task swapper/3/0

CPU: 3 PID: 0 Comm: swapper/3 Not tainted 6.1.0-rc3-00014-g41ff012f50cb-dirty #322
Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
unwind_backtrace from show_stack+0x10/0x14
show_stack from dump_stack_lvl+0x58/0x70
dump_stack_lvl from print_report+0x134/0x4d4
print_report from kasan_report+0x78/0x10c
kasan_report from gether_connect+0xb8/0x30c
gether_connect from ecm_set_alt+0x124/0x254
ecm_set_alt from composite_setup+0xb98/0x2b18
composite_setup from configfs_composite_setup+0x80/0x98
configfs_composite_setup from dummy_timer+0x8f0/0x14a0 [dummy_hcd]
...

Signed-off-by: Sascha Hauer <[email protected]>
---
drivers/usb/gadget/function/f_ecm.c | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index ffe2486fce71c..a7ab30e603e20 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -685,7 +685,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
struct f_ecm *ecm = func_to_ecm(f);
struct usb_string *us;
- int status;
+ int status = 0;
struct usb_ep *ep;

struct f_ecm_opts *ecm_opts;
@@ -695,23 +695,19 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)

ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);

- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to ecm_opts->bound access
- */
+ mutex_lock(&ecm_opts->lock);
+
+ gether_set_gadget(ecm_opts->net, cdev->gadget);
+
if (!ecm_opts->bound) {
- mutex_lock(&ecm_opts->lock);
- gether_set_gadget(ecm_opts->net, cdev->gadget);
status = gether_register_netdev(ecm_opts->net);
- mutex_unlock(&ecm_opts->lock);
- if (status)
- return status;
ecm_opts->bound = true;
}

+ mutex_unlock(&ecm_opts->lock);
+ if (status)
+ return status;
+
ecm_string_defs[1].s = ecm->ethaddr;

us = usb_gstrings_attach(cdev, ecm_strings,
--
2.30.2