Commit 66d2a595 authored by Dmitry Torokhov's avatar Dmitry Torokhov

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: default avatarJim Paradis <jparadis@redhat.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 6ee88d71
...@@ -46,8 +46,6 @@ ...@@ -46,8 +46,6 @@
extern void ctrl_alt_del(void); extern void ctrl_alt_del(void);
#define to_handle_h(n) container_of(n, struct input_handle, h_node)
/* /*
* Exported functions/variables * Exported functions/variables
*/ */
...@@ -191,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier); ...@@ -191,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
* etc.). So this means that scancodes for the extra function keys won't * etc.). So this means that scancodes for the extra function keys won't
* be valid for the first event device, but will be for the second. * be valid for the first event device, but will be for the second.
*/ */
struct getset_keycode_data {
unsigned int scancode;
unsigned int keycode;
int error;
};
static int getkeycode_helper(struct input_handle *handle, void *data)
{
struct getset_keycode_data *d = data;
d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
return d->error == 0; /* stop as soon as we successfully get one */
}
int getkeycode(unsigned int scancode) int getkeycode(unsigned int scancode)
{ {
struct input_handle *handle; struct getset_keycode_data d = { scancode, 0, -ENODEV };
int keycode;
int error = -ENODEV;
list_for_each_entry(handle, &kbd_handler.h_list, h_node) { input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
error = input_get_keycode(handle->dev, scancode, &keycode);
if (!error)
return keycode;
}
return error; return d.error ?: d.keycode;
}
static int setkeycode_helper(struct input_handle *handle, void *data)
{
struct getset_keycode_data *d = data;
d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
return d->error == 0; /* stop as soon as we successfully set one */
} }
int setkeycode(unsigned int scancode, unsigned int keycode) int setkeycode(unsigned int scancode, unsigned int keycode)
{ {
struct input_handle *handle; struct getset_keycode_data d = { scancode, keycode, -ENODEV };
int error = -ENODEV;
list_for_each_entry(handle, &kbd_handler.h_list, h_node) { input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
error = input_set_keycode(handle->dev, scancode, keycode);
if (!error)
break;
}
return error; return d.error;
} }
/* /*
* Making beeps and bells. * Making beeps and bells.
*/ */
static void kd_nosound(unsigned long ignored)
static int kd_sound_helper(struct input_handle *handle, void *data)
{ {
struct input_handle *handle; unsigned int *hz = data;
struct input_dev *dev = handle->dev;
list_for_each_entry(handle, &kbd_handler.h_list, h_node) { if (test_bit(EV_SND, dev->evbit)) {
if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, dev->sndbit))
if (test_bit(SND_TONE, handle->dev->sndbit)) input_inject_event(handle, EV_SND, SND_TONE, *hz);
input_inject_event(handle, EV_SND, SND_TONE, 0); if (test_bit(SND_BELL, handle->dev->sndbit))
if (test_bit(SND_BELL, handle->dev->sndbit)) input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0);
input_inject_event(handle, EV_SND, SND_BELL, 0);
}
} }
return 0;
}
static void kd_nosound(unsigned long ignored)
{
static unsigned int zero;
input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
} }
static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0); static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);
void kd_mksound(unsigned int hz, unsigned int ticks) void kd_mksound(unsigned int hz, unsigned int ticks)
{ {
struct list_head *node; del_timer_sync(&kd_mksound_timer);
del_timer(&kd_mksound_timer); input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
if (hz) { if (hz && ticks)
list_for_each_prev(node, &kbd_handler.h_list) { mod_timer(&kd_mksound_timer, jiffies + ticks);
struct input_handle *handle = to_handle_h(node);
if (test_bit(EV_SND, handle->dev->evbit)) {
if (test_bit(SND_TONE, handle->dev->sndbit)) {
input_inject_event(handle, EV_SND, SND_TONE, hz);
break;
}
if (test_bit(SND_BELL, handle->dev->sndbit)) {
input_inject_event(handle, EV_SND, SND_BELL, 1);
break;
}
}
}
if (ticks)
mod_timer(&kd_mksound_timer, jiffies + ticks);
} else
kd_nosound(0);
} }
EXPORT_SYMBOL(kd_mksound); EXPORT_SYMBOL(kd_mksound);
...@@ -270,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound); ...@@ -270,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound);
* Setting the keyboard rate. * Setting the keyboard rate.
*/ */
int kbd_rate(struct kbd_repeat *rep) static int kbd_rate_helper(struct input_handle *handle, void *data)
{ {
struct list_head *node; struct input_dev *dev = handle->dev;
unsigned int d = 0; struct kbd_repeat *rep = data;
unsigned int p = 0;
if (test_bit(EV_REP, dev->evbit)) {
list_for_each(node, &kbd_handler.h_list) {
struct input_handle *handle = to_handle_h(node); if (rep[0].delay > 0)
struct input_dev *dev = handle->dev; input_inject_event(handle,
EV_REP, REP_DELAY, rep[0].delay);
if (test_bit(EV_REP, dev->evbit)) { if (rep[0].period > 0)
if (rep->delay > 0) input_inject_event(handle,
input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); EV_REP, REP_PERIOD, rep[0].period);
if (rep->period > 0)
input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); rep[1].delay = dev->rep[REP_DELAY];
d = dev->rep[REP_DELAY]; rep[1].period = dev->rep[REP_PERIOD];
p = dev->rep[REP_PERIOD];
}
} }
rep->delay = d;
rep->period = p; return 0;
}
int kbd_rate(struct kbd_repeat *rep)
{
struct kbd_repeat data[2] = { *rep };
input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
*rep = data[1]; /* Copy currently used settings */
return 0; return 0;
} }
...@@ -998,36 +1010,36 @@ static inline unsigned char getleds(void) ...@@ -998,36 +1010,36 @@ static inline unsigned char getleds(void)
return leds; return leds;
} }
static int kbd_update_leds_helper(struct input_handle *handle, void *data)
{
unsigned char leds = *(unsigned char *)data;
if (test_bit(EV_LED, handle->dev->evbit)) {
input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
}
return 0;
}
/* /*
* This routine is the bottom half of the keyboard interrupt * This is the tasklet that updates LED state on all keyboards
* routine, and runs with all interrupts enabled. It does * attached to the box. The reason we use tasklet is that we
* console changing, led setting and copy_to_cooked, which can * need to handle the scenario when keyboard handler is not
* take a reasonably long time. * registered yet but we already getting updates form VT to
* * update led state.
* Aside from timing (which isn't really that important for
* keyboard interrupts as they happen often), using the software
* interrupt routines for this thing allows us to easily mask
* this when we don't want any of the above to happen.
* This allows for easy and efficient race-condition prevention
* for kbd_start => input_inject_event(dev, EV_LED, ...) => ...
*/ */
static void kbd_bh(unsigned long dummy) static void kbd_bh(unsigned long dummy)
{ {
struct list_head *node;
unsigned char leds = getleds(); unsigned char leds = getleds();
if (leds != ledstate) { if (leds != ledstate) {
list_for_each(node, &kbd_handler.h_list) { input_handler_for_each_handle(&kbd_handler, &leds,
struct input_handle *handle = to_handle_h(node); kbd_update_leds_helper);
input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); ledstate = leds;
input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
}
} }
ledstate = leds;
} }
DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
...@@ -1370,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle) ...@@ -1370,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle)
*/ */
static void kbd_start(struct input_handle *handle) static void kbd_start(struct input_handle *handle)
{ {
unsigned char leds = ledstate;
tasklet_disable(&keyboard_tasklet); tasklet_disable(&keyboard_tasklet);
if (leds != 0xff) {
input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); if (ledstate != 0xff)
input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); kbd_update_leds_helper(handle, &ledstate);
input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
}
tasklet_enable(&keyboard_tasklet); tasklet_enable(&keyboard_tasklet);
} }
......
...@@ -1650,6 +1650,38 @@ void input_unregister_handler(struct input_handler *handler) ...@@ -1650,6 +1650,38 @@ void input_unregister_handler(struct input_handler *handler)
} }
EXPORT_SYMBOL(input_unregister_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 * input_register_handle - register a new input handle
* @handle: handle to register * @handle: handle to register
...@@ -1683,7 +1715,7 @@ int input_register_handle(struct input_handle *handle) ...@@ -1683,7 +1715,7 @@ int input_register_handle(struct input_handle *handle)
* we can't be racing with input_unregister_handle() * we can't be racing with input_unregister_handle()
* and so separate lock is not needed here. * 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) if (handler->start)
handler->start(handle); handler->start(handle);
...@@ -1706,7 +1738,7 @@ void input_unregister_handle(struct input_handle *handle) ...@@ -1706,7 +1738,7 @@ void input_unregister_handle(struct input_handle *handle)
{ {
struct input_dev *dev = handle->dev; 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(). * Take dev->mutex to prevent race with input_release_device().
...@@ -1714,6 +1746,7 @@ void input_unregister_handle(struct input_handle *handle) ...@@ -1714,6 +1746,7 @@ void input_unregister_handle(struct input_handle *handle)
mutex_lock(&dev->mutex); mutex_lock(&dev->mutex);
list_del_rcu(&handle->d_node); list_del_rcu(&handle->d_node);
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
synchronize_rcu(); synchronize_rcu();
} }
EXPORT_SYMBOL(input_unregister_handle); EXPORT_SYMBOL(input_unregister_handle);
......
...@@ -1021,9 +1021,12 @@ struct ff_effect { ...@@ -1021,9 +1021,12 @@ struct ff_effect {
* @keycodesize: size of elements in keycode table * @keycodesize: size of elements in keycode table
* @keycode: map of scancodes to keycodes for this device * @keycode: map of scancodes to keycodes for this device
* @setkeycode: optional method to alter current keymap, used to implement * @setkeycode: optional method to alter current keymap, used to implement
* sparse keymaps. If not supplied default mechanism will be used * sparse keymaps. If not supplied default mechanism will be used.
* The method is being called while holding event_lock and thus must
* not sleep
* @getkeycode: optional method to retrieve current keymap. If not supplied * @getkeycode: optional method to retrieve current keymap. If not supplied
* default mechanism will be used * default mechanism will be used. The method is being called while
* holding event_lock and thus must not sleep
* @ff: force feedback structure associated with the device if device * @ff: force feedback structure associated with the device if device
* supports force feedback effects * supports force feedback effects
* @repeat_key: stores key code of the last key pressed; used to implement * @repeat_key: stores key code of the last key pressed; used to implement
...@@ -1295,6 +1298,9 @@ void input_unregister_device(struct input_dev *); ...@@ -1295,6 +1298,9 @@ void input_unregister_device(struct input_dev *);
int __must_check input_register_handler(struct input_handler *); int __must_check input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *); void input_unregister_handler(struct input_handler *);
int input_handler_for_each_handle(struct input_handler *, void *data,
int (*fn)(struct input_handle *, void *));
int input_register_handle(struct input_handle *); int input_register_handle(struct input_handle *);
void input_unregister_handle(struct input_handle *); void input_unregister_handle(struct input_handle *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment