5.10-stable review patch. If anyone has any objections, please let me know. ------------------ From: Johan Hovold [ Upstream commit 646e5b28b4fdbc1b828a2398d6ced2c1dd308a46 ] USB serial driver modules register one driver for the USB bus and one or more drivers for the ports on the USB serial bus. When unloading a driver module, the USB driver must be deregistered before the USB serial bus drivers so that I/O is stopped before unbinding the ports to avoid use-after-free in completion handlers accessing port data. Note that the "new_id" attributes must first be removed to prevent new ids from being added and triggering a probe of the USB driver after it has been deregistered. Fixes: 765e0ba62613 ("usb-serial: new API for driver registration") Cc: stable@vger.kernel.org # 3.4 Cc: Alan Stern Signed-off-by: Johan Hovold Signed-off-by: Sasha Levin --- drivers/usb/serial/bus.c | 4 ++++ drivers/usb/serial/usb-serial.c | 21 ++++++++++++++++++--- include/linux/usb/serial.h | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index bbb981041c10e..de6d9f9ecd3a5 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -188,3 +188,7 @@ void usb_serial_bus_deregister(struct usb_serial_driver *driver) free_dynids(driver); } +void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver) +{ + driver_remove_file(&driver->driver, &driver_attr_new_id); +} diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 338fd0d5e65e1..bc3ec34f9f913 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1432,7 +1432,7 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[] { int rc; struct usb_driver *udriver; - struct usb_serial_driver * const *sd; + struct usb_serial_driver * const *sd, * const *s; /* * udriver must be registered before any of the serial drivers, @@ -1484,9 +1484,11 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[] return 0; err_deregister_drivers: + for (s = serial_drivers; s < sd; ++s) + usb_serial_bus_remove_new_id(*s); + usb_deregister(udriver); while (sd-- > serial_drivers) usb_serial_deregister(*sd); - usb_deregister(udriver); err_free_driver: kfree(udriver); return rc; @@ -1504,10 +1506,23 @@ EXPORT_SYMBOL_GPL(usb_serial_register_drivers); void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]) { struct usb_driver *udriver = (*serial_drivers)->usb_driver; + struct usb_serial_driver * const *sd; + + /* + * udriver must be deregistered before the serial drivers so that + * I/O is stopped before unbinding the ports. + * + * Remove the new_id attributes to prevent ids from being added and + * triggering a probe of udriver after it has been deregistered. + */ + for (sd = serial_drivers; *sd; ++sd) + usb_serial_bus_remove_new_id(*sd); + + usb_deregister(udriver); for (; *serial_drivers; ++serial_drivers) usb_serial_deregister(*serial_drivers); - usb_deregister(udriver); + kfree(udriver); } EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8e67eff9039fa..d98521605f8d6 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -378,6 +378,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, int usb_serial_bus_register(struct usb_serial_driver *device); void usb_serial_bus_deregister(struct usb_serial_driver *device); +void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver); extern struct bus_type usb_serial_bus_type; extern struct tty_driver *usb_serial_tty_driver; -- 2.53.0