| From ec50bde09b8ded5313e3f785fb772dba0a0326e8 Mon Sep 17 00:00:00 2001 |
| From: Ingo Rohloff <ingo.rohloff@lauterbach.com> |
| Date: Fri, 11 Oct 2019 13:55:18 +0200 |
| Subject: [PATCH] usb: usbfs: Suppress problematic bind and unbind uevents. |
| |
| commit abb0b3d96a1f9407dd66831ae33985a386d4200d upstream. |
| |
| commit 1455cf8dbfd0 ("driver core: emit uevents when device is bound |
| to a driver") added bind and unbind uevents when a driver is bound or |
| unbound to a physical device. |
| |
| For USB devices which are handled via the generic usbfs layer (via |
| libusb for example), this is problematic: |
| Each time a user space program calls |
| ioctl(usb_fd, USBDEVFS_CLAIMINTERFACE, &usb_intf_nr); |
| and then later |
| ioctl(usb_fd, USBDEVFS_RELEASEINTERFACE, &usb_intf_nr); |
| The kernel will now produce a bind or unbind event, which does not |
| really contain any useful information. |
| |
| This allows a user space program to run a DoS attack against programs |
| which listen to uevents (in particular systemd/eudev/upowerd): |
| A malicious user space program just has to call in a tight loop |
| |
| ioctl(usb_fd, USBDEVFS_CLAIMINTERFACE, &usb_intf_nr); |
| ioctl(usb_fd, USBDEVFS_RELEASEINTERFACE, &usb_intf_nr); |
| |
| With this loop the malicious user space program floods the kernel and |
| all programs listening to uevents with tons of bind and unbind |
| events. |
| |
| This patch suppresses uevents for ioctls USBDEVFS_CLAIMINTERFACE and |
| USBDEVFS_RELEASEINTERFACE. |
| |
| Signed-off-by: Ingo Rohloff <ingo.rohloff@lauterbach.com> |
| Link: https://lore.kernel.org/r/20191011115518.2801-1-ingo.rohloff@lauterbach.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c |
| index 86130e8d35f9..19940c2aaaa9 100644 |
| --- a/drivers/usb/core/devio.c |
| +++ b/drivers/usb/core/devio.c |
| @@ -737,8 +737,15 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum) |
| intf = usb_ifnum_to_if(dev, ifnum); |
| if (!intf) |
| err = -ENOENT; |
| - else |
| + else { |
| + unsigned int old_suppress; |
| + |
| + /* suppress uevents while claiming interface */ |
| + old_suppress = dev_get_uevent_suppress(&intf->dev); |
| + dev_set_uevent_suppress(&intf->dev, 1); |
| err = usb_driver_claim_interface(&usbfs_driver, intf, ps); |
| + dev_set_uevent_suppress(&intf->dev, old_suppress); |
| + } |
| if (err == 0) |
| set_bit(ifnum, &ps->ifclaimed); |
| return err; |
| @@ -758,7 +765,13 @@ static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum) |
| if (!intf) |
| err = -ENOENT; |
| else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { |
| + unsigned int old_suppress; |
| + |
| + /* suppress uevents while releasing interface */ |
| + old_suppress = dev_get_uevent_suppress(&intf->dev); |
| + dev_set_uevent_suppress(&intf->dev, 1); |
| usb_driver_release_interface(&usbfs_driver, intf); |
| + dev_set_uevent_suppress(&intf->dev, old_suppress); |
| err = 0; |
| } |
| return err; |
| -- |
| 2.7.4 |
| |