diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-13 12:27:00 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-13 12:27:00 -0700 |
| commit | 22098763a10d9c1340827fcf6edab66f153b27f0 (patch) | |
| tree | 3962f95a34df034652ff93686cdd156c7197f2d9 /drivers/input/evdev.c | |
| download | linux-stable-22098763a10d9c1340827fcf6edab66f153b27f0.tar.gz linux-stable-22098763a10d9c1340827fcf6edab66f153b27f0.zip | |
Merge tag 'trace-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt:
- Don't destroy user event fields when removal fails
User event fields are destroyed before the event is removed from
visibility. But that can fail leaving the still visible event with no
fields. Move the destroying of the fields to after the event is
successfully removed from visibility.
- Initialize function graph state is fork before calling
copy_exec_state()
For non-CLONE_VM forks, copy_exec_state() allocates a new
task_exec_state. If that allocation fails, ftrace_graph_exit_task()
will free the tasks ret_stack pointer. Since that pointer is still
using the parent's ret_stack, it mistakenly frees the parent's
pointer too.
Call ftrace_graph_init() on the task first which will NULL out the
new tasks's ret_stack and if the copy fails, it will not free
anything.
- Remove FGRAPH_MAX_INDEX
The macro FGRAPH_MAX_INDEX was added but never used. Remove it.
- Save ent_size in function graph printing of nested functions
The function graph tracer needs to look at the next event to see if
the next event is the return of the current function entry. If it is,
it prints a single line:
ktime_get();
Otherwise it prints it like a nested function:
tick_nohz_irq_exit() {
ktime_get();
kcpustat_irq_exit();
}
In order to look at the next event, it must save the current event so
that it has the information to print from it. It saves the event in
the iterator descriptor called "ent". What it doesn't save is the
ent_size of the event which is now used to know if the function graph
arguments are to be printed. The peek doesn't save the size so the
size used happens to be that of the size of the last event that was
seen.
Save the entry event size in the iterator descriptor so that the
correct size is used.
- Fix several errors with freeing data in the histogram code
The histogram code had a lot of leaked or or incorrect accounting
when failures happen. Correct them.
- Fix histogram regression of .percent and .graph modifiers
Up until 6.3 histogram values could have "percent" or "graph"
modifiers that changed how they were printed. But a change that added
restricting histograms values from being strings, stack traces and
other modifiers inadvertently prevented them from using the percent
and graph modifiers, which were legal use cases for values.
Put back the percent and graph modifiers.
- Fix various typos in the comments
- Set the trace_clock before initializing a histogram with clock
argument
The histogram API allows the user to specific which trace clock to
use via a "clock=" string. The histogram is set up first before the
clock is checked. If the passed in clock is not valid, it exits
without fully fixing up the histogram leaving it on the list and a
use-after-free can trigger.
Update the clock argument first and if it fails then exit gracefully
before the histogram trigger is placed on any lists.
- Restore :mod: trailer after parsing in ftrace_set_clr_event
The function ftrace_set_clr_event() modifies the parse string and
needs to put it back to what was passed in. It searches for ":mod:"
via a strsep() but fails to put back the first ':' in the string.
Add back the ':' in the passed in string.
- Take trace_array reference when opening a tracer options file
The options files are dynamically created and some tracers add their
own options. When a tracer adds their own list of options, the
trace_array holding them has an array to hold the list of options for
each tracer. This array increases in size via a krealloc(), and the
new entry gets a newly allocated array to hold the options of the new
tracer being added.
The element in each entry of the tracer's option array holds a
pointer back to the trace_array, a pointer to the tracer it is
associated to, a pointer to the flags of the option.
The issue is that these arrays are freed when the trace_array is
freed when its instance it represents is removed from the instances
directory. There's a race that an open of one of these options files
can happen when the instance is being removed.
Add a new helper function to be called by the open function of the
options file to iterate all existing trace_arrays under a lock and
find the one that has the given option element in one of it's tracer
arrays. If found, then update the associated trace_array's reference
counter to keep it from being freed. If not found, have the open call
return -ENODEV.
- Disable interrupts when acquiring the lock in rb_wake_up_waiters()
The function rb_wake_up_waiters() assumes it will be called in
interrupt context and does not disable irqs when taking
cpu_buffer->reader_lock, which can be called in hard interrupt
context. The issue is in PREEMPT_RT, this function is called in
thread context leaving this lock open to a deadlock.
Take the lock with interrupts disabled.
- Use rcu_assign_pointer() for tmp_ops filter hash
The tmp_ops used in update_ftrace_direct_mod() assigns its
filter_hash field directly, but that field is annotated as __rcu and
sparse complains. Assign it with rcu_assign_pointer()
- Fix use-after-free in enable_trigger_private_data_free()
The trace_event_call is accessed through the event_trigger_data's
trace_event_file pointer to put the trace_event_call on freeing. The
issue is that the trace_event_file data may have been freed already
causing a use-after-free. Add a field to the event_trigger_data that
points directly to the trace_event_call so that it can decrement its
reference directly without needing to go through the
trace_event_file.
- Fix accounting of buffer data remote headers
trace_buffer_desc_size() and trace_remote_alloc_buffer() undercount
the number of pages is needed for the asked for size as it doesn't
take into account the meta data on each page. Add a helper function
to do the calculation properly and use that in these functions.
- Catch nr_page_va overflow in ring_buffer_desc sizing
The number of pages per remote ring buffer is capped by
ring_buffer_desc::nr_page_va (32 bits). A buffer_size large enough to
overflow that field would silently allocate a descriptor smaller than
what was asked for.
- Do not resize the subbuf order if any per_cpu buffer is disabled
The mmapping of ring buffers disables resizing the subbuffers, but it
is done per-cpu whereas the subbuf size change is done for all the
per_cpu buffers under the buffer->mutex. It could change the size of
some while the mapping is happening on others. Have the resize of the
subbuf order check all the per_cpu buffers under the lock to see if
any of them is disabled before starting and causing an inconsistency
between buffers that are being mapped.
* tag 'trace-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (25 commits)
ring-buffer: Check resize_disabled before publishing the new subbuf order
tracing/remotes: Catch nr_page_va overflow in ring_buffer_desc sizing
tracing/remotes: Account for ring buffer page header in size calculation
tracing: Don't dereference trace_event_file in deferred trigger free
ftrace: Use rcu_assign_pointer() for tmp_ops filter hash
ring-buffer: Acquire the lock with irqsave in rb_wake_up_waiters()
tracing: Take trace_array reference when opening a tracer options file
tracing: Fix ring_buffer_read_page_size() kernel-doc
tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event()
tracing: Fix memory corruption from a "STACKTRACE" histogram key
tracing: Fix memory corruption from the histogram stacktrace modifier
tracing: Undo the registration when enabling the histogram trigger fails
tracing: Take the reference before publishing the named histogram trigger
tracing: Set the trace clock before registering the histogram trigger
tracing: Fix typo "preceeded" in comment
tracing: Fix typo "availabe" in comment
tracing: Let histogram values keep the percent and graph modifiers
tracing: Keep the entry count when the histogram stats allocation fails
tracing: Free histogram the field rejected for a bad modifier
tracing: Free histogram the var ref when its initialization fails
...
Diffstat (limited to 'drivers/input/evdev.c')
| -rw-r--r-- | drivers/input/evdev.c | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c new file mode 100644 index 000000000..3a718d600 --- /dev/null +++ b/drivers/input/evdev.c @@ -0,0 +1,1451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Event char devices, giving access to raw input device events. + * + * Copyright (c) 1999-2002 Vojtech Pavlik + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define EVDEV_MINOR_BASE 64 +#define EVDEV_MINORS 32 +#define EVDEV_MIN_BUFFER_SIZE 64U +#define EVDEV_BUF_PACKETS 8 + +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input/mt.h> +#include <linux/major.h> +#include <linux/nospec.h> +#include <linux/device.h> +#include <linux/cdev.h> +#include "input-compat.h" + +struct evdev { + int open; + struct input_handle handle; + struct evdev_client __rcu *grab; + struct list_head client_list; + spinlock_t client_lock; /* protects client_list */ + struct mutex mutex; + struct device dev; + struct cdev cdev; + bool exist; +}; + +struct evdev_client { + unsigned int head; + unsigned int tail; + unsigned int packet_head; /* [future] position of the first element of next packet */ + spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + wait_queue_head_t wait; + struct fasync_struct *fasync; + struct evdev *evdev; + struct list_head node; + enum input_clock_type clk_type; + bool revoked; + unsigned long *evmasks[EV_CNT]; + unsigned int bufsize; + struct input_event buffer[] __counted_by(bufsize); +}; + +static size_t evdev_get_mask_cnt(unsigned int type) +{ + static const size_t counts[EV_CNT] = { + /* EV_SYN==0 is EV_CNT, _not_ SYN_CNT, see EVIOCGBIT */ + [EV_SYN] = EV_CNT, + [EV_KEY] = KEY_CNT, + [EV_REL] = REL_CNT, + [EV_ABS] = ABS_CNT, + [EV_MSC] = MSC_CNT, + [EV_SW] = SW_CNT, + [EV_LED] = LED_CNT, + [EV_SND] = SND_CNT, + [EV_FF] = FF_CNT, + }; + unsigned long mask = array_index_mask_nospec(type, EV_CNT); + + /* Returns 0 for out-of-bounds types, including speculatively */ + return counts[type & mask] & mask; +} + +/* requires the buffer lock to be held */ +static bool __evdev_is_filtered(struct evdev_client *client, + unsigned int type, + unsigned int code) +{ + unsigned long *mask; + size_t cnt; + + /* EV_SYN and unknown codes are never filtered */ + if (type == EV_SYN || type >= EV_CNT) + return false; + + /* first test whether the type is filtered */ + mask = client->evmasks[0]; + if (mask && !test_bit(type, mask)) + return true; + + /* unknown values are never filtered */ + cnt = evdev_get_mask_cnt(type); + if (!cnt || code >= cnt) + return false; + + mask = client->evmasks[type]; + return mask && !test_bit(code, mask); +} + +/* flush queued events of type @type, caller must hold client->buffer_lock */ +static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) +{ + unsigned int i, head, num; + unsigned int mask = client->bufsize - 1; + bool is_report; + struct input_event *ev; + + BUG_ON(type == EV_SYN); + + head = client->tail; + client->packet_head = client->tail; + + /* init to 1 so a leading SYN_REPORT will not be dropped */ + num = 1; + + for (i = client->tail; i != client->head; i = (i + 1) & mask) { + ev = &client->buffer[i]; + is_report = ev->type == EV_SYN && ev->code == SYN_REPORT; + + if (ev->type == type) { + /* drop matched entry */ + continue; + } else if (is_report && !num) { + /* drop empty SYN_REPORT groups */ + continue; + } else if (head != i) { + /* move entry to fill the gap */ + client->buffer[head] = *ev; + } + + num++; + head = (head + 1) & mask; + + if (is_report) { + num = 0; + client->packet_head = head; + } + } + + client->head = head; +} + +static void __evdev_queue_syn_dropped(struct evdev_client *client) +{ + ktime_t *ev_time = input_get_timestamp(client->evdev->handle.dev); + struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]); + struct input_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.input_event_sec = ts.tv_sec; + ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + + client->buffer[client->head++] = ev; + client->head &= client->bufsize - 1; + + if (unlikely(client->head == client->tail)) { + /* drop queue but keep our SYN_DROPPED event */ + client->tail = (client->head - 1) & (client->bufsize - 1); + client->packet_head = client->tail; + } +} + +static void evdev_queue_syn_dropped(struct evdev_client *client) +{ + unsigned long flags; + + spin_lock_irqsave(&client->buffer_lock, flags); + __evdev_queue_syn_dropped(client); + spin_unlock_irqrestore(&client->buffer_lock, flags); +} + +static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid) +{ + unsigned long flags; + enum input_clock_type clk_type; + + switch (clkid) { + + case CLOCK_REALTIME: + clk_type = INPUT_CLK_REAL; + break; + case CLOCK_MONOTONIC: + clk_type = INPUT_CLK_MONO; + break; + case CLOCK_BOOTTIME: + clk_type = INPUT_CLK_BOOT; + break; + default: + return -EINVAL; + } + + if (client->clk_type != clk_type) { + client->clk_type = clk_type; + + /* + * Flush pending events and queue SYN_DROPPED event, + * but only if the queue is not empty. + */ + spin_lock_irqsave(&client->buffer_lock, flags); + + if (client->head != client->tail) { + client->packet_head = client->head = client->tail; + __evdev_queue_syn_dropped(client); + } + + spin_unlock_irqrestore(&client->buffer_lock, flags); + } + + return 0; +} + +static void __pass_event(struct evdev_client *client, + const struct input_event *event) +{ + client->buffer[client->head++] = *event; + client->head &= client->bufsize - 1; + + if (unlikely(client->head == client->tail)) { + struct input_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.input_event_sec = event->input_event_sec; + ev.input_event_usec = event->input_event_usec; + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + + /* + * This effectively "drops" all unconsumed events, leaving + * EV_SYN/SYN_DROPPED plus the newest event in the queue. + */ + client->tail = (client->head - 2) & (client->bufsize - 1); + client->buffer[client->tail] = ev; + client->packet_head = client->tail; + } + + if (event->type == EV_SYN && event->code == SYN_REPORT) { + client->packet_head = client->head; + kill_fasync(&client->fasync, SIGIO, POLL_IN); + } +} + +static void evdev_pass_values(struct evdev_client *client, + const struct input_value *vals, unsigned int count, + ktime_t *ev_time) +{ + const struct input_value *v; + struct input_event event; + struct timespec64 ts; + bool wakeup = false; + + if (client->revoked) + return; + + memset(&event, 0, sizeof(event)); + + ts = ktime_to_timespec64(ev_time[client->clk_type]); + event.input_event_sec = ts.tv_sec; + event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; + + /* Interrupts are disabled, just acquire the lock. */ + spin_lock(&client->buffer_lock); + + for (v = vals; v != vals + count; v++) { + if (__evdev_is_filtered(client, v->type, v->code)) + continue; + + if (v->type == EV_SYN && v->code == SYN_REPORT) { + /* drop empty SYN_REPORT */ + if (client->packet_head == client->head) + continue; + + wakeup = true; + } + + event.type = v->type; + event.code = v->code; + event.value = v->value; + __pass_event(client, &event); + } + + spin_unlock(&client->buffer_lock); + + if (wakeup) + wake_up_interruptible_poll(&client->wait, + EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM); +} + +/* + * Pass incoming events to all connected clients. + */ +static unsigned int evdev_events(struct input_handle *handle, + struct input_value *vals, unsigned int count) +{ + struct evdev *evdev = handle->private; + struct evdev_client *client; + ktime_t *ev_time = input_get_timestamp(handle->dev); + + rcu_read_lock(); + + client = rcu_dereference(evdev->grab); + + if (client) + evdev_pass_values(client, vals, count, ev_time); + else + list_for_each_entry_rcu(client, &evdev->client_list, node) + evdev_pass_values(client, vals, count, ev_time); + + rcu_read_unlock(); + + return count; +} + +static int evdev_fasync(int fd, struct file *file, int on) +{ + struct evdev_client *client = file->private_data; + + return fasync_helper(fd, file, on, &client->fasync); +} + +static void evdev_free(struct device *dev) +{ + struct evdev *evdev = container_of(dev, struct evdev, dev); + + input_put_device(evdev->handle.dev); + kfree(evdev); +} + +/* + * Grabs an event device (along with underlying input device). + * This function is called with evdev->mutex taken. + */ +static int evdev_grab(struct evdev *evdev, struct evdev_client *client) +{ + int error; + + if (evdev->grab) + return -EBUSY; + + error = input_grab_device(&evdev->handle); + if (error) + return error; + + rcu_assign_pointer(evdev->grab, client); + + return 0; +} + +static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client) +{ + struct evdev_client *grab = rcu_dereference_protected(evdev->grab, + lockdep_is_held(&evdev->mutex)); + + if (grab != client) + return -EINVAL; + + rcu_assign_pointer(evdev->grab, NULL); + synchronize_rcu(); + input_release_device(&evdev->handle); + + return 0; +} + +static void evdev_attach_client(struct evdev *evdev, + struct evdev_client *client) +{ + spin_lock(&evdev->client_lock); + list_add_tail_rcu(&client->node, &evdev->client_list); + spin_unlock(&evdev->client_lock); +} + +static void evdev_detach_client(struct evdev *evdev, + struct evdev_client *client) +{ + spin_lock(&evdev->client_lock); + list_del_rcu(&client->node); + spin_unlock(&evdev->client_lock); + synchronize_rcu(); +} + +static int evdev_open_device(struct evdev *evdev) +{ + int retval; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist) + retval = -ENODEV; + else if (!evdev->open++) { + retval = input_open_device(&evdev->handle); + if (retval) + evdev->open--; + } + + mutex_unlock(&evdev->mutex); + return retval; +} + +static void evdev_close_device(struct evdev *evdev) +{ + mutex_lock(&evdev->mutex); + + if (evdev->exist && !--evdev->open) + input_close_device(&evdev->handle); + + mutex_unlock(&evdev->mutex); +} + +/* + * Wake up users waiting for IO so they can disconnect from + * dead device. + */ +static void evdev_hangup(struct evdev *evdev) +{ + struct evdev_client *client; + + spin_lock(&evdev->client_lock); + list_for_each_entry(client, &evdev->client_list, node) { + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR); + } + spin_unlock(&evdev->client_lock); +} + +static int evdev_release(struct inode *inode, struct file *file) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + unsigned int i; + + mutex_lock(&evdev->mutex); + + if (evdev->exist && !client->revoked) + input_flush_device(&evdev->handle, file); + + evdev_ungrab(evdev, client); + mutex_unlock(&evdev->mutex); + + evdev_detach_client(evdev, client); + + for (i = 0; i < EV_CNT; ++i) + bitmap_free(client->evmasks[i]); + + kvfree(client); + + evdev_close_device(evdev); + + return 0; +} + +static unsigned int evdev_compute_buffer_size(struct input_dev *dev) +{ + unsigned int n_events = + max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS, + EVDEV_MIN_BUFFER_SIZE); + + return roundup_pow_of_two(n_events); +} + +static int evdev_open(struct inode *inode, struct file *file) +{ + struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); + unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); + struct evdev_client *client; + int error; + + client = kvzalloc_flex(*client, buffer, bufsize); + if (!client) + return -ENOMEM; + + init_waitqueue_head(&client->wait); + client->bufsize = bufsize; + spin_lock_init(&client->buffer_lock); + client->evdev = evdev; + evdev_attach_client(evdev, client); + + error = evdev_open_device(evdev); + if (error) + goto err_free_client; + + file->private_data = client; + stream_open(inode, file); + + return 0; + + err_free_client: + evdev_detach_client(evdev, client); + kvfree(client); + return error; +} + +static ssize_t evdev_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_event event; + int retval = 0; + + /* + * Limit amount of data we inject into the input subsystem so that + * we do not hold evdev->mutex for too long. 4096 bytes corresponds + * to 170 input events. + */ + count = min(count, 4096); + + if (count != 0 && count < input_event_size()) + return -EINVAL; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist || client->revoked) { + retval = -ENODEV; + goto out; + } + + while (retval + input_event_size() <= count) { + + if (input_event_from_user(buffer + retval, &event)) { + retval = -EFAULT; + goto out; + } + retval += input_event_size(); + + input_inject_event(&evdev->handle, + event.type, event.code, event.value); + cond_resched(); + } + + out: + mutex_unlock(&evdev->mutex); + return retval; +} + +static int evdev_fetch_next_event(struct evdev_client *client, + struct input_event *event) +{ + int have_event; + + spin_lock_irq(&client->buffer_lock); + + have_event = client->packet_head != client->tail; + if (have_event) { + *event = client->buffer[client->tail++]; + client->tail &= client->bufsize - 1; + } + + spin_unlock_irq(&client->buffer_lock); + + return have_event; +} + +static ssize_t evdev_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_event event; + size_t read = 0; + int error; + + if (count != 0 && count < input_event_size()) + return -EINVAL; + + for (;;) { + if (!evdev->exist || client->revoked) + return -ENODEV; + + if (client->packet_head == client->tail && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + /* + * count == 0 is special - no IO is done but we check + * for error conditions (see above). + */ + if (count == 0) + break; + + while (read + input_event_size() <= count && + evdev_fetch_next_event(client, &event)) { + + if (input_event_to_user(buffer + read, &event)) + return -EFAULT; + + read += input_event_size(); + } + + if (read) + break; + + if (!(file->f_flags & O_NONBLOCK)) { + error = wait_event_interruptible(client->wait, + client->packet_head != client->tail || + !evdev->exist || client->revoked); + if (error) + return error; + } + } + + return read; +} + +/* No kernel lock - fine */ +static __poll_t evdev_poll(struct file *file, poll_table *wait) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + __poll_t mask; + + poll_wait(file, &client->wait, wait); + + if (evdev->exist && !client->revoked) + mask = EPOLLOUT | EPOLLWRNORM; + else + mask = EPOLLHUP | EPOLLERR; + + if (client->packet_head != client->tail) + mask |= EPOLLIN | EPOLLRDNORM; + + return mask; +} + +#ifdef CONFIG_COMPAT + +#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8) +#define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1) + +#ifdef __BIG_ENDIAN +static int bits_to_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, int compat) +{ + int len, i; + + if (compat) { + len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t); + if (len > maxlen) + len = maxlen; + + for (i = 0; i < len / sizeof(compat_long_t); i++) + if (copy_to_user((compat_long_t __user *) p + i, + (compat_long_t *) bits + + i + 1 - ((i % 2) << 1), + sizeof(compat_long_t))) + return -EFAULT; + } else { + len = BITS_TO_LONGS(maxbit) * sizeof(long); + if (len > maxlen) + len = maxlen; + + if (copy_to_user(p, bits, len)) + return -EFAULT; + } + + return len; +} + +static int bits_from_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, const void __user *p, int compat) +{ + int len, i; + + if (compat) { + if (maxlen % sizeof(compat_long_t)) + return -EINVAL; + + len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t); + if (len > maxlen) + len = maxlen; + + for (i = 0; i < len / sizeof(compat_long_t); i++) + if (copy_from_user((compat_long_t *) bits + + i + 1 - ((i % 2) << 1), + (compat_long_t __user *) p + i, + sizeof(compat_long_t))) + return -EFAULT; + if (i % 2) + *((compat_long_t *) bits + i - 1) = 0; + + } else { + if (maxlen % sizeof(long)) + return -EINVAL; + + len = BITS_TO_LONGS(maxbit) * sizeof(long); + if (len > maxlen) + len = maxlen; + + if (copy_from_user(bits, p, len)) + return -EFAULT; + } + + return len; +} + +#else + +static int bits_to_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, int compat) +{ + int len = compat ? + BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) : + BITS_TO_LONGS(maxbit) * sizeof(long); + + if (len > maxlen) + len = maxlen; + + return copy_to_user(p, bits, len) ? -EFAULT : len; +} + +static int bits_from_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, const void __user *p, int compat) +{ + size_t chunk_size = compat ? sizeof(compat_long_t) : sizeof(long); + int len; + + if (maxlen % chunk_size) + return -EINVAL; + + len = compat ? BITS_TO_LONGS_COMPAT(maxbit) : BITS_TO_LONGS(maxbit); + len *= chunk_size; + if (len > maxlen) + len = maxlen; + + return copy_from_user(bits, p, len) ? -EFAULT : len; +} + +#endif /* __BIG_ENDIAN */ + +#else + +static int bits_to_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, int compat) +{ + int len = BITS_TO_LONGS(maxbit) * sizeof(long); + + if (len > maxlen) + len = maxlen; + + return copy_to_user(p, bits, len) ? -EFAULT : len; +} + +static int bits_from_user(unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, const void __user *p, int compat) +{ + int len; + + if (maxlen % sizeof(long)) + return -EINVAL; + + len = BITS_TO_LONGS(maxbit) * sizeof(long); + if (len > maxlen) + len = maxlen; + + return copy_from_user(bits, p, len) ? -EFAULT : len; +} + +#endif /* CONFIG_COMPAT */ + +static int str_to_user(const char *str, unsigned int maxlen, void __user *p) +{ + int len; + + if (!str) + return -ENOENT; + + len = strlen(str) + 1; + if (len > maxlen) + len = maxlen; + + return copy_to_user(p, str, len) ? -EFAULT : len; +} + +static int handle_eviocgbit(struct input_dev *dev, + unsigned int type, unsigned int size, + void __user *p, int compat_mode) +{ + unsigned long *bits; + int len; + + switch (type) { + + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + case EV_FF: bits = dev->ffbit; len = FF_MAX; break; + case EV_SW: bits = dev->swbit; len = SW_MAX; break; + default: return -EINVAL; + } + + return bits_to_user(bits, len, size, p, compat_mode); +} + +static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke = { + .len = sizeof(unsigned int), + .flags = 0, + }; + int __user *ip = (int __user *)p; + int error; + + /* legacy case */ + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (put_user(ke.keycode, ip + 1)) + return -EFAULT; + + return 0; +} + +static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke; + int error; + + if (copy_from_user(&ke, p, sizeof(ke))) + return -EFAULT; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (copy_to_user(p, &ke, sizeof(ke))) + return -EFAULT; + + return 0; +} + +static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke = { + .len = sizeof(unsigned int), + .flags = 0, + }; + int __user *ip = (int __user *)p; + + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + if (get_user(ke.keycode, ip + 1)) + return -EFAULT; + + return input_set_keycode(dev, &ke); +} + +static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke; + + if (copy_from_user(&ke, p, sizeof(ke))) + return -EFAULT; + + if (ke.len > sizeof(ke.scancode)) + return -EINVAL; + + return input_set_keycode(dev, &ke); +} + +/* + * If we transfer state to the user, we should flush all pending events + * of the same type from the client's queue. Otherwise, they might end up + * with duplicate events, which can screw up client's state tracking. + * If bits_to_user fails after flushing the queue, we queue a SYN_DROPPED + * event so user-space will notice missing events. + * + * LOCKING: + * We need to take event_lock before buffer_lock to avoid dead-locks. But we + * need the even_lock only to guarantee consistent state. We can safely release + * it while flushing the queue. This allows input-core to handle filters while + * we flush the queue. + */ +static int evdev_handle_get_val(struct evdev_client *client, + struct input_dev *dev, unsigned int type, + unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, + int compat) +{ + int ret; + unsigned long *mem; + + mem = bitmap_alloc(maxbit, GFP_KERNEL); + if (!mem) + return -ENOMEM; + + spin_lock_irq(&dev->event_lock); + spin_lock(&client->buffer_lock); + + bitmap_copy(mem, bits, maxbit); + + spin_unlock(&dev->event_lock); + + __evdev_flush_queue(client, type); + + spin_unlock_irq(&client->buffer_lock); + + ret = bits_to_user(mem, maxbit, maxlen, p, compat); + if (ret < 0) + evdev_queue_syn_dropped(client); + + bitmap_free(mem); + + return ret; +} + +static int evdev_handle_mt_request(struct input_dev *dev, + unsigned int size, + int __user *ip) +{ + const struct input_mt *mt = dev->mt; + unsigned int code; + int max_slots; + int i; + + if (get_user(code, &ip[0])) + return -EFAULT; + if (!mt || !input_is_mt_value(code)) + return -EINVAL; + + max_slots = (size - sizeof(__u32)) / sizeof(__s32); + for (i = 0; i < mt->num_slots && i < max_slots; i++) { + int value = input_mt_get_value(&mt->slots[i], code); + if (put_user(value, &ip[1 + i])) + return -EFAULT; + } + + return 0; +} + +static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, + struct file *file) +{ + client->revoked = true; + evdev_ungrab(evdev, client); + input_flush_device(&evdev->handle, file); + wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR); + + return 0; +} + +/* must be called with evdev-mutex held */ +static int evdev_set_mask(struct evdev_client *client, + unsigned int type, + const void __user *codes, + u32 codes_size, + int compat) +{ + unsigned long flags, *mask, *oldmask; + size_t cnt; + int error; + + /* we allow unknown types and 'codes_size > size' for forward-compat */ + cnt = evdev_get_mask_cnt(type); + if (!cnt) + return 0; + + mask = bitmap_zalloc(cnt, GFP_KERNEL); + if (!mask) + return -ENOMEM; + + error = bits_from_user(mask, cnt - 1, codes_size, codes, compat); + if (error < 0) { + bitmap_free(mask); + return error; + } + + spin_lock_irqsave(&client->buffer_lock, flags); + oldmask = client->evmasks[type]; + client->evmasks[type] = mask; + spin_unlock_irqrestore(&client->buffer_lock, flags); + + bitmap_free(oldmask); + + return 0; +} + +/* must be called with evdev-mutex held */ +static int evdev_get_mask(struct evdev_client *client, + unsigned int type, + void __user *codes, + u32 codes_size, + int compat) +{ + unsigned long *mask; + size_t cnt, size, xfer_size; + int i; + int error; + + /* we allow unknown types and 'codes_size > size' for forward-compat */ + cnt = evdev_get_mask_cnt(type); + size = sizeof(unsigned long) * BITS_TO_LONGS(cnt); + xfer_size = min_t(size_t, codes_size, size); + + if (cnt > 0) { + mask = client->evmasks[type]; + if (mask) { + error = bits_to_user(mask, cnt - 1, + xfer_size, codes, compat); + if (error < 0) + return error; + } else { + /* fake mask with all bits set */ + for (i = 0; i < xfer_size; i++) + if (put_user(0xffU, (u8 __user *)codes + i)) + return -EFAULT; + } + } + + if (xfer_size < codes_size) + if (clear_user(codes + xfer_size, codes_size - xfer_size)) + return -EFAULT; + + return 0; +} + +static long evdev_do_ioctl(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_dev *dev = evdev->handle.dev; + struct input_absinfo abs; + struct input_mask mask; + struct ff_effect effect; + int __user *ip = (int __user *)p; + unsigned int i, t, u, v; + unsigned int size; + int error; + + /* First we check for fixed-length commands */ + switch (cmd) { + + case EVIOCGVERSION: + return put_user(EV_VERSION, ip); + + case EVIOCGID: + if (copy_to_user(p, &dev->id, sizeof(struct input_id))) + return -EFAULT; + return 0; + + case EVIOCGREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (put_user(dev->rep[REP_DELAY], ip)) + return -EFAULT; + if (put_user(dev->rep[REP_PERIOD], ip + 1)) + return -EFAULT; + return 0; + + case EVIOCSREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (get_user(u, ip)) + return -EFAULT; + if (get_user(v, ip + 1)) + return -EFAULT; + + input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); + input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); + + return 0; + + case EVIOCRMFF: + return input_ff_erase(dev, (int)(unsigned long) p, file); + + case EVIOCGEFFECTS: + i = test_bit(EV_FF, dev->evbit) ? + dev->ff->max_effects : 0; + if (put_user(i, ip)) + return -EFAULT; + return 0; + + case EVIOCGRAB: + if (p) + return evdev_grab(evdev, client); + else + return evdev_ungrab(evdev, client); + + case EVIOCREVOKE: + if (p) + return -EINVAL; + else + return evdev_revoke(evdev, client, file); + + case EVIOCGMASK: { + void __user *codes_ptr; + + if (copy_from_user(&mask, p, sizeof(mask))) + return -EFAULT; + + codes_ptr = (void __user *)(unsigned long)mask.codes_ptr; + return evdev_get_mask(client, + mask.type, codes_ptr, mask.codes_size, + compat_mode); + } + + case EVIOCSMASK: { + const void __user *codes_ptr; + + if (copy_from_user(&mask, p, sizeof(mask))) + return -EFAULT; + + codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr; + return evdev_set_mask(client, + mask.type, codes_ptr, mask.codes_size, + compat_mode); + } + + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + + return evdev_set_clk_type(client, i); + + case EVIOCGKEYCODE: + return evdev_handle_get_keycode(dev, p); + + case EVIOCSKEYCODE: + return evdev_handle_set_keycode(dev, p); + + case EVIOCGKEYCODE_V2: + return evdev_handle_get_keycode_v2(dev, p); + + case EVIOCSKEYCODE_V2: + return evdev_handle_set_keycode_v2(dev, p); + } + + size = _IOC_SIZE(cmd); + + /* Now check variable-length commands */ +#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) + switch (EVIOC_MASK_SIZE(cmd)) { + + case EVIOCGPROP(0): + return bits_to_user(dev->propbit, INPUT_PROP_MAX, + size, p, compat_mode); + + case EVIOCGMTSLOTS(0): + return evdev_handle_mt_request(dev, size, ip); + + case EVIOCGKEY(0): + return evdev_handle_get_val(client, dev, EV_KEY, dev->key, + KEY_MAX, size, p, compat_mode); + + case EVIOCGLED(0): + return evdev_handle_get_val(client, dev, EV_LED, dev->led, + LED_MAX, size, p, compat_mode); + + case EVIOCGSND(0): + return evdev_handle_get_val(client, dev, EV_SND, dev->snd, + SND_MAX, size, p, compat_mode); + + case EVIOCGSW(0): + return evdev_handle_get_val(client, dev, EV_SW, dev->sw, + SW_MAX, size, p, compat_mode); + + case EVIOCGNAME(0): + return str_to_user(dev->name, size, p); + + case EVIOCGPHYS(0): + return str_to_user(dev->phys, size, p); + + case EVIOCGUNIQ(0): + return str_to_user(dev->uniq, size, p); + + case EVIOC_MASK_SIZE(EVIOCSFF): + if (input_ff_effect_from_user(p, size, &effect)) + return -EFAULT; + + error = input_ff_upload(dev, &effect, file); + if (error) + return error; + + if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) + return -EFAULT; + + return 0; + } + + /* Multi-number variable-length handlers */ + if (_IOC_TYPE(cmd) != 'E') + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) + return handle_eviocgbit(dev, + _IOC_NR(cmd) & EV_MAX, size, + p, compat_mode); + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + if (!dev->absinfo) + return -EINVAL; + + t = _IOC_NR(cmd) & ABS_MAX; + abs = dev->absinfo[t]; + + if (copy_to_user(p, &abs, min_t(size_t, + size, sizeof(struct input_absinfo)))) + return -EFAULT; + + return 0; + } + } + + if (_IOC_DIR(cmd) == _IOC_WRITE) { + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + + if (!dev->absinfo) + return -EINVAL; + + t = _IOC_NR(cmd) & ABS_MAX; + + if (copy_from_user(&abs, p, min_t(size_t, + size, sizeof(struct input_absinfo)))) + return -EFAULT; + + if (size < sizeof(struct input_absinfo)) + abs.resolution = 0; + + /* We can't change number of reserved MT slots */ + if (t == ABS_MT_SLOT) + return -EINVAL; + + /* + * Take event lock to ensure that we are not + * changing device parameters in the middle + * of event. + */ + spin_lock_irq(&dev->event_lock); + dev->absinfo[t] = abs; + spin_unlock_irq(&dev->event_lock); + + return 0; + } + } + + return -EINVAL; +} + +static long evdev_ioctl_handler(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +{ + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + int retval; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist || client->revoked) { + retval = -ENODEV; + goto out; + } + + retval = evdev_do_ioctl(file, cmd, p, compat_mode); + + out: + mutex_unlock(&evdev->mutex); + return retval; +} + +static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0); +} + +#ifdef CONFIG_COMPAT +static long evdev_ioctl_compat(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1); +} +#endif + +static const struct file_operations evdev_fops = { + .owner = THIS_MODULE, + .read = evdev_read, + .write = evdev_write, + .poll = evdev_poll, + .open = evdev_open, + .release = evdev_release, + .unlocked_ioctl = evdev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = evdev_ioctl_compat, +#endif + .fasync = evdev_fasync, +}; + +/* + * Mark device non-existent. This disables writes, ioctls and + * prevents new users from opening the device. Already posted + * blocking reads will stay, however new ones will fail. + */ +static void evdev_mark_dead(struct evdev *evdev) +{ + mutex_lock(&evdev->mutex); + evdev->exist = false; + mutex_unlock(&evdev->mutex); +} + +static void evdev_cleanup(struct evdev *evdev) +{ + struct input_handle *handle = &evdev->handle; + + evdev_mark_dead(evdev); + evdev_hangup(evdev); + + /* evdev is marked dead so no one else accesses evdev->open */ + if (evdev->open) { + input_flush_device(handle, NULL); + input_close_device(handle); + } +} + +/* + * Create new evdev device. Note that input core serializes calls + * to connect and disconnect. + */ +static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct evdev *evdev; + int minor; + int dev_no; + int error; + + minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); + if (minor < 0) { + error = minor; + pr_err("failed to reserve new minor: %d\n", error); + return error; + } + + evdev = kzalloc_obj(struct evdev); + if (!evdev) { + error = -ENOMEM; + goto err_free_minor; + } + + INIT_LIST_HEAD(&evdev->client_list); + spin_lock_init(&evdev->client_lock); + mutex_init(&evdev->mutex); + evdev->exist = true; + + dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS) + dev_no -= EVDEV_MINOR_BASE; + dev_set_name(&evdev->dev, "event%d", dev_no); + + evdev->handle.dev = input_get_device(dev); + evdev->handle.name = dev_name(&evdev->dev); + evdev->handle.handler = handler; + evdev->handle.private = evdev; + + evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); + evdev->dev.class = &input_class; + evdev->dev.parent = &dev->dev; + evdev->dev.release = evdev_free; + device_initialize(&evdev->dev); + + error = input_register_handle(&evdev->handle); + if (error) + goto err_free_evdev; + + cdev_init(&evdev->cdev, &evdev_fops); + + error = cdev_device_add(&evdev->cdev, &evdev->dev); + if (error) + goto err_cleanup_evdev; + + return 0; + + err_cleanup_evdev: + evdev_cleanup(evdev); + input_unregister_handle(&evdev->handle); + err_free_evdev: + put_device(&evdev->dev); + err_free_minor: + input_free_minor(minor); + return error; +} + +static void evdev_disconnect(struct input_handle *handle) +{ + struct evdev *evdev = handle->private; + + cdev_device_del(&evdev->cdev, &evdev->dev); + evdev_cleanup(evdev); + input_free_minor(MINOR(evdev->dev.devt)); + input_unregister_handle(handle); + put_device(&evdev->dev); +} + +static const struct input_device_id evdev_ids[] = { + { + /* Matches all devices */ + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_SYN) }, + }, + { } /* Terminating zero entry */ +}; + +MODULE_DEVICE_TABLE(input, evdev_ids); + +static struct input_handler evdev_handler = { + .events = evdev_events, + .connect = evdev_connect, + .disconnect = evdev_disconnect, + .legacy_minors = true, + .minor = EVDEV_MINOR_BASE, + .name = "evdev", + .id_table = evdev_ids, +}; + +static int __init evdev_init(void) +{ + return input_register_handler(&evdev_handler); +} + +static void __exit evdev_exit(void) +{ + input_unregister_handler(&evdev_handler); +} + +module_init(evdev_init); +module_exit(evdev_exit); + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_DESCRIPTION("Input driver event char devices"); +MODULE_LICENSE("GPL"); |
