syzbot is reporting a NULL pointer dereference [1] because usbip modules do not use serialization when attaching/detaching. As a preparation for serializing attach/detach operations, introduce a global mutex, export it via usbip_event_lock_killable()/usbip_event_unlock(), and use it in event_handler(). We don't need to use per a device mutex because event_handler() is processed by a singlethreaded workqueue and ud->tcp_{tx,rx} must not wait for event_handler() to flush. [1] https://syzkaller.appspot.com/bug?extid=95ce4b142579611ef0a9 Reported-by: syzbot <syzbot+95ce4b142579611ef0a9@xxxxxxxxxxxxxxxxxxxxxxxxx> Tested-by: syzbot <syzbot+a93fba6d384346a761e3@xxxxxxxxxxxxxxxxxxxxxxxxx> Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx> --- drivers/usb/usbip/usbip_common.h | 2 ++ drivers/usb/usbip/usbip_event.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index a7dd6c66aee5..f8e16c75b154 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -327,6 +327,8 @@ void usbip_stop_eh(struct usbip_device *ud); void usbip_event_add(struct usbip_device *ud, unsigned long event); int usbip_event_happened(struct usbip_device *ud); int usbip_in_eh(struct task_struct *task); +int usbip_event_lock_killable(void); +void usbip_event_unlock(void); static inline int interface_to_busnum(struct usb_interface *interface) { diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c index 5d88917c9631..e05b858f346d 100644 --- a/drivers/usb/usbip/usbip_event.c +++ b/drivers/usb/usbip/usbip_event.c @@ -58,6 +58,19 @@ static struct usbip_device *get_event(void) } static struct task_struct *worker_context; +static DEFINE_MUTEX(usbip_event_mutex); + +int usbip_event_lock_killable(void) +{ + return mutex_lock_killable(&usbip_event_mutex); +} +EXPORT_SYMBOL_GPL(usbip_event_lock_killable); + +void usbip_event_unlock(void) +{ + mutex_unlock(&usbip_event_mutex); +} +EXPORT_SYMBOL_GPL(usbip_event_unlock); static void event_handler(struct work_struct *work) { @@ -68,6 +81,7 @@ static void event_handler(struct work_struct *work) } while ((ud = get_event()) != NULL) { + mutex_lock(&usbip_event_mutex); usbip_dbg_eh("pending event %lx\n", ud->event); /* @@ -91,6 +105,7 @@ static void event_handler(struct work_struct *work) unset_event(ud, USBIP_EH_UNUSABLE); } + mutex_unlock(&usbip_event_mutex); wake_up(&ud->eh_waitq); } } -- 2.18.4