blob: 23f5e4e9855ac6132e7455ab44a6d15f0945426e [file] [log] [blame]
From b1222b90b3aa77cf9574200a12f27e3e76696741 Mon Sep 17 00:00:00 2001
Message-Id: <b1222b90b3aa77cf9574200a12f27e3e76696741.1513090390.git.andreyknvl@google.com>
From: Andrey Konovalov <andreyknvl@google.com>
Date: Wed, 27 Sep 2017 17:07:47 +0200
Subject: [PATCH 1/2] usb-fuzzer: add hub hijacking ioctl
---
drivers/usb/core/hub.c | 112 +++++++++++++++++++++++++++++++++++++-
include/uapi/linux/usbdevice_fs.h | 3 +
2 files changed, 114 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index cf7bbcb9a63c..2dfdd74b1b76 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -28,6 +28,7 @@
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
+#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>
@@ -1795,6 +1796,65 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
return -ENODEV;
}
+DECLARE_WAIT_QUEUE_HEAD(uf_hub_wq);
+DEFINE_SPINLOCK(uf_hub_lock);
+LIST_HEAD(uf_hub_list);
+int uf_hub_switch = 0;
+
+struct uf_hub_entry {
+ struct list_head list;
+ struct work_struct *work;
+ struct completion done;
+};
+
+void hub_event_impl(struct work_struct *work);
+
+void uf_hub_events_handle(int wait)
+{
+ unsigned long flags;
+ struct list_head *curr, *next;
+ struct list_head local;
+ unsigned long rv;
+
+ INIT_LIST_HEAD(&local);
+
+ pr_err("! uf: handling queued events: start\n");
+
+ if (!wait && list_empty(&uf_hub_list))
+ goto exit;
+
+retry:
+ rv = wait_event_interruptible(uf_hub_wq, !list_empty(&uf_hub_list));
+ if (rv != 0)
+ goto exit;
+ spin_lock_irqsave(&uf_hub_lock, flags);
+ if (list_empty(&uf_hub_list)) {
+ spin_unlock_irqrestore(&uf_hub_lock, flags);
+ if (!wait)
+ goto exit;
+ goto retry;
+ }
+ list_for_each_safe(curr, next, &uf_hub_list) {
+ list_del(curr);
+ list_add(curr, &local);
+ }
+ spin_unlock_irqrestore(&uf_hub_lock, flags);
+
+ pr_err("! uf: handling queued events: got events\n");
+
+ list_for_each_safe(curr, next, &local) {
+ struct uf_hub_entry *entry =
+ list_entry(curr, struct uf_hub_entry, list);
+
+ list_del(curr);
+ hub_event_impl(entry->work);
+ complete(&entry->done);
+ }
+
+exit:
+ pr_err("! uf: handling queued events: done\n");
+}
+
static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
@@ -1824,6 +1884,24 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
return info->nports + 1;
}
+ case USBDEVFS_HUB_HIGHJACK:
+ WRITE_ONCE(uf_hub_switch, 1);
+ pr_err("! uf: hub highjacked\n");
+ return 0;
+ case USBDEVFS_HUB_RELEASE:
+ spin_lock(&uf_hub_lock);
+ WRITE_ONCE(uf_hub_switch, 0);
+ spin_unlock(&uf_hub_lock);
+ pr_err("! uf: hub released\n");
+ return 0;
+ case USBDEVFS_HUB_PROCESS: {
+ u32 *value = user_data;
+ usb_unlock_device(hdev);
+ uf_hub_events_handle(!*value);
+ usb_lock_device(hdev);
+ pr_err("! uf: hub processed\n");
+ return 0;
+ }
default:
return -ENOSYS;
@@ -5140,7 +5218,39 @@ static void port_event(struct usb_hub *hub, int port1)
hub_port_connect_change(hub, port1, portstatus, portchange);
}
-static void hub_event(struct work_struct *work)
+static void hub_event(struct work_struct *work) {
+ struct usb_device *hdev;
+ struct usb_hub *hub;
+
+ struct uf_hub_entry entry;
+
+ if (READ_ONCE(uf_hub_switch) == 0) {
+ hub_event_impl(work);
+ return;
+ }
+
+ hub = container_of(work, struct usb_hub, events);
+ hdev = hub->hdev;
+
+ pr_err("! hub event: queueing %p (device: %p)\n", work, hdev);
+
+ entry.work = work;
+ init_completion(&entry.done);
+
+ spin_lock(&uf_hub_lock);
+ if (READ_ONCE(uf_hub_switch) == 0) {
+ spin_unlock(&uf_hub_lock);
+ hub_event_impl(work);
+ return;
+ }
+ list_add_tail(&entry.list, &uf_hub_list);
+ spin_unlock(&uf_hub_lock);
+ wake_up_interruptible(&uf_hub_wq);
+
+ wait_for_completion_interruptible(&entry.done);
+}
+
+void hub_event_impl(struct work_struct *work)
{
struct usb_device *hdev;
struct usb_interface *intf;
diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h
index 70ed5338d447..9e6db931fd7b 100644
--- a/include/uapi/linux/usbdevice_fs.h
+++ b/include/uapi/linux/usbdevice_fs.h
@@ -197,5 +197,8 @@ struct usbdevfs_streams {
#define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams)
#define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32)
#define USBDEVFS_GET_SPEED _IO('U', 31)
+#define USBDEVFS_HUB_HIGHJACK _IO('U', 32)
+#define USBDEVFS_HUB_RELEASE _IO('U', 33)
+#define USBDEVFS_HUB_PROCESS _IOW('U', 34, __u32)
#endif /* _UAPI_LINUX_USBDEVICE_FS_H */
--
2.15.1.424.g9478a66081-goog