4a2a8a2cce
The usbfs code doesn't provide sufficient mutual exclusion among open, release, and remove. Release vs. remove is okay because they both acquire the device lock, but open is not exclusive with either one. All three routines modify the udev->filelist linked list, so they must not run concurrently. Apparently someone gave this a minimum amount of thought in the past by explicitly acquiring the BKL at the start of the usbdev_open routine. Oddly enough, there's a comment pointing out that locking is unnecessary because chrdev_open already has acquired the BKL. But this ignores the point that the files in /proc/bus/usb/* are not char device files; they are regular files and so they don't get any special locking. Furthermore it's necessary to acquire the same lock in the release and remove routines, which the code does not do. Yet another problem arises because the same file_operations structure is accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file nodes. Even when one of them has been removed, it's still possible for userspace to open the other. So simple locking around the individual remove routines is insufficient; we need to lock the entire usb_notify_remove_device notifier chain. Rather than rely on the BKL, this patch (as723) introduces a new private mutex for the purpose. Holding the BKL while invoking a notifier chain doesn't seem like a good idea. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
68 lines
1.7 KiB
C
68 lines
1.7 KiB
C
/*
|
|
* All the USB notify logic
|
|
*
|
|
* (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
|
|
*
|
|
* notifier functions originally based on those in kernel/sys.c
|
|
* but fixed up to not be so broken.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/mutex.h>
|
|
#include "usb.h"
|
|
|
|
static BLOCKING_NOTIFIER_HEAD(usb_notifier_list);
|
|
|
|
/**
|
|
* usb_register_notify - register a notifier callback whenever a usb change happens
|
|
* @nb: pointer to the notifier block for the callback events.
|
|
*
|
|
* These changes are either USB devices or busses being added or removed.
|
|
*/
|
|
void usb_register_notify(struct notifier_block *nb)
|
|
{
|
|
blocking_notifier_chain_register(&usb_notifier_list, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_register_notify);
|
|
|
|
/**
|
|
* usb_unregister_notify - unregister a notifier callback
|
|
* @nb: pointer to the notifier block for the callback events.
|
|
*
|
|
* usb_register_notifier() must have been previously called for this function
|
|
* to work properly.
|
|
*/
|
|
void usb_unregister_notify(struct notifier_block *nb)
|
|
{
|
|
blocking_notifier_chain_unregister(&usb_notifier_list, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_unregister_notify);
|
|
|
|
|
|
void usb_notify_add_device(struct usb_device *udev)
|
|
{
|
|
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
|
|
}
|
|
|
|
void usb_notify_remove_device(struct usb_device *udev)
|
|
{
|
|
/* Protect against simultaneous usbfs open */
|
|
mutex_lock(&usbfs_mutex);
|
|
blocking_notifier_call_chain(&usb_notifier_list,
|
|
USB_DEVICE_REMOVE, udev);
|
|
mutex_unlock(&usbfs_mutex);
|
|
}
|
|
|
|
void usb_notify_add_bus(struct usb_bus *ubus)
|
|
{
|
|
blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus);
|
|
}
|
|
|
|
void usb_notify_remove_bus(struct usb_bus *ubus)
|
|
{
|
|
blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus);
|
|
}
|