Input: keyboard - fix lack of locking when traversing handler->h_list
Keyboard handler should not attempt to traverse handler->h_list on its own, without any locking, otherwise it races with registering and unregistering of input handles which leads to crashes. Introduce input_handler_for_each_handle() helper that allows safely iterate over all handles attached to a particular handler and switch keyboard handler to use it. Reported-by: Jim Paradis <jparadis@redhat.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
@@ -1650,6 +1650,38 @@ void input_unregister_handler(struct input_handler *handler)
|
||||
}
|
||||
EXPORT_SYMBOL(input_unregister_handler);
|
||||
|
||||
/**
|
||||
* input_handler_for_each_handle - handle iterator
|
||||
* @handler: input handler to iterate
|
||||
* @data: data for the callback
|
||||
* @fn: function to be called for each handle
|
||||
*
|
||||
* Iterate over @bus's list of devices, and call @fn for each, passing
|
||||
* it @data and stop when @fn returns a non-zero value. The function is
|
||||
* using RCU to traverse the list and therefore may be usind in atonic
|
||||
* contexts. The @fn callback is invoked from RCU critical section and
|
||||
* thus must not sleep.
|
||||
*/
|
||||
int input_handler_for_each_handle(struct input_handler *handler, void *data,
|
||||
int (*fn)(struct input_handle *, void *))
|
||||
{
|
||||
struct input_handle *handle;
|
||||
int retval = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(handle, &handler->h_list, h_node) {
|
||||
retval = fn(handle, data);
|
||||
if (retval)
|
||||
break;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(input_handler_for_each_handle);
|
||||
|
||||
/**
|
||||
* input_register_handle - register a new input handle
|
||||
* @handle: handle to register
|
||||
@@ -1683,7 +1715,7 @@ int input_register_handle(struct input_handle *handle)
|
||||
* we can't be racing with input_unregister_handle()
|
||||
* and so separate lock is not needed here.
|
||||
*/
|
||||
list_add_tail(&handle->h_node, &handler->h_list);
|
||||
list_add_tail_rcu(&handle->h_node, &handler->h_list);
|
||||
|
||||
if (handler->start)
|
||||
handler->start(handle);
|
||||
@@ -1706,7 +1738,7 @@ void input_unregister_handle(struct input_handle *handle)
|
||||
{
|
||||
struct input_dev *dev = handle->dev;
|
||||
|
||||
list_del_init(&handle->h_node);
|
||||
list_del_rcu(&handle->h_node);
|
||||
|
||||
/*
|
||||
* Take dev->mutex to prevent race with input_release_device().
|
||||
@@ -1714,6 +1746,7 @@ void input_unregister_handle(struct input_handle *handle)
|
||||
mutex_lock(&dev->mutex);
|
||||
list_del_rcu(&handle->d_node);
|
||||
mutex_unlock(&dev->mutex);
|
||||
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL(input_unregister_handle);
|
||||
|
Reference in New Issue
Block a user