usb: gadget: f_ecm: Always set current gadget in ecm_bind()
authorSascha Hauer <s.hauer@pengutronix.de>
Fri, 4 Nov 2022 13:10:31 +0000 (14:10 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 9 Nov 2022 11:39:33 +0000 (12:39 +0100)
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 <s.hauer@pengutronix.de>
Link: https://lore.kernel.org/r/20221104131031.850850-3-s.hauer@pengutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/f_ecm.c

index ffe2486fce71c95956a21b2c921cf84a247ada02..a7ab30e603e20c2b72a54f550838ed6332ef6da7 100644 (file)
@@ -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,