Commit 1572ca2a authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: fix locking issue in /proc/bus/input/ handlers

input_devices_seq_start() uses mutex_lock_interruptible() to acquire
the input_mutex, but doesn't properly handle the situation when the
call fails (for example due to interrupt). Instead of returning NULL
(which indicates that there is no more data) we should return
ERR_PTR()-encoded error.

We also need explicit flag indicating whether input_mutex was acquired
since input_devices_seq_stop() is called whether input_devices_seq_start()
was successful or not.

The same applies to input_handlers_seq_start().
Reported-by: default avatariceberg <strakh@ispras.ru>
Reviewed-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 94dfb0d6
...@@ -781,10 +781,29 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) ...@@ -781,10 +781,29 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
return 0; return 0;
} }
union input_seq_state {
struct {
unsigned short pos;
bool mutex_acquired;
};
void *p;
};
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
{ {
if (mutex_lock_interruptible(&input_mutex)) union input_seq_state *state = (union input_seq_state *)&seq->private;
return NULL; int error;
/* We need to fit into seq->private pointer */
BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
error = mutex_lock_interruptible(&input_mutex);
if (error) {
state->mutex_acquired = false;
return ERR_PTR(error);
}
state->mutex_acquired = true;
return seq_list_start(&input_dev_list, *pos); return seq_list_start(&input_dev_list, *pos);
} }
...@@ -794,8 +813,11 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) ...@@ -794,8 +813,11 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
return seq_list_next(v, &input_dev_list, pos); return seq_list_next(v, &input_dev_list, pos);
} }
static void input_devices_seq_stop(struct seq_file *seq, void *v) static void input_seq_stop(struct seq_file *seq, void *v)
{ {
union input_seq_state *state = (union input_seq_state *)&seq->private;
if (state->mutex_acquired)
mutex_unlock(&input_mutex); mutex_unlock(&input_mutex);
} }
...@@ -860,7 +882,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v) ...@@ -860,7 +882,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
static const struct seq_operations input_devices_seq_ops = { static const struct seq_operations input_devices_seq_ops = {
.start = input_devices_seq_start, .start = input_devices_seq_start,
.next = input_devices_seq_next, .next = input_devices_seq_next,
.stop = input_devices_seq_stop, .stop = input_seq_stop,
.show = input_devices_seq_show, .show = input_devices_seq_show,
}; };
...@@ -880,40 +902,49 @@ static const struct file_operations input_devices_fileops = { ...@@ -880,40 +902,49 @@ static const struct file_operations input_devices_fileops = {
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
{ {
if (mutex_lock_interruptible(&input_mutex)) union input_seq_state *state = (union input_seq_state *)&seq->private;
return NULL; int error;
/* We need to fit into seq->private pointer */
BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
error = mutex_lock_interruptible(&input_mutex);
if (error) {
state->mutex_acquired = false;
return ERR_PTR(error);
}
state->mutex_acquired = true;
state->pos = *pos;
seq->private = (void *)(unsigned long)*pos;
return seq_list_start(&input_handler_list, *pos); return seq_list_start(&input_handler_list, *pos);
} }
static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
seq->private = (void *)(unsigned long)(*pos + 1); union input_seq_state *state = (union input_seq_state *)&seq->private;
return seq_list_next(v, &input_handler_list, pos);
}
static void input_handlers_seq_stop(struct seq_file *seq, void *v) state->pos = *pos + 1;
{ return seq_list_next(v, &input_handler_list, pos);
mutex_unlock(&input_mutex);
} }
static int input_handlers_seq_show(struct seq_file *seq, void *v) static int input_handlers_seq_show(struct seq_file *seq, void *v)
{ {
struct input_handler *handler = container_of(v, struct input_handler, node); struct input_handler *handler = container_of(v, struct input_handler, node);
union input_seq_state *state = (union input_seq_state *)&seq->private;
seq_printf(seq, "N: Number=%ld Name=%s", seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);
(unsigned long)seq->private, handler->name);
if (handler->fops) if (handler->fops)
seq_printf(seq, " Minor=%d", handler->minor); seq_printf(seq, " Minor=%d", handler->minor);
seq_putc(seq, '\n'); seq_putc(seq, '\n');
return 0; return 0;
} }
static const struct seq_operations input_handlers_seq_ops = { static const struct seq_operations input_handlers_seq_ops = {
.start = input_handlers_seq_start, .start = input_handlers_seq_start,
.next = input_handlers_seq_next, .next = input_handlers_seq_next,
.stop = input_handlers_seq_stop, .stop = input_seq_stop,
.show = input_handlers_seq_show, .show = input_handlers_seq_show,
}; };
......
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