From dba4258068f822b7dafc78c28fe9c99c551eca7e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 May 2012 00:13:36 -0700 Subject: Input: evdev - properly access RCU-protected 'grab' data We should use rcu_dereference_protected() when checking if given client is the one that grabbed the device. This fixes warnings produced by sparse. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 4b2e10d5d641..9226b4d9118f 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -180,7 +180,10 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client) static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client) { - if (evdev->grab != 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); @@ -259,8 +262,7 @@ static int evdev_release(struct inode *inode, struct file *file) struct evdev *evdev = client->evdev; mutex_lock(&evdev->mutex); - if (evdev->grab == client) - evdev_ungrab(evdev, client); + evdev_ungrab(evdev, client); mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); -- cgit v1.3-8-gc7d7 From 2872a9b521ac936c7a8525a8c2bdfb9b4ccf5cfc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 May 2012 00:13:37 -0700 Subject: Input: evdev - properly handle read/write with count 0 According to the standard count 0 is special - no IO should happen but we can check error conditions (device gone away, etc), and return 0 if there are no errors. We used to return -EINVAL instead and we also could return 0 if an event was "stolen" by another thread. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 61 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 23 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 9226b4d9118f..6c58bfff01a3 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -345,7 +345,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, struct input_event event; int retval = 0; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; retval = mutex_lock_interruptible(&evdev->mutex); @@ -357,7 +357,8 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, goto out; } - do { + while (retval + input_event_size() <= count) { + if (input_event_from_user(buffer + retval, &event)) { retval = -EFAULT; goto out; @@ -366,7 +367,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, input_inject_event(&evdev->handle, event.type, event.code, event.value); - } while (retval + input_event_size() <= count); + } out: mutex_unlock(&evdev->mutex); @@ -397,35 +398,49 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; - int retval = 0; + size_t read = 0; + int error; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; - if (!(file->f_flags & O_NONBLOCK)) { - retval = wait_event_interruptible(evdev->wait, - client->packet_head != client->tail || - !evdev->exist); - if (retval) - return retval; - } + for (;;) { + if (!evdev->exist) + return -ENODEV; - if (!evdev->exist) - 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 (retval + input_event_size() <= count && - evdev_fetch_next_event(client, &event)) { + while (read + input_event_size() <= count && + evdev_fetch_next_event(client, &event)) { - if (input_event_to_user(buffer + retval, &event)) - return -EFAULT; + if (input_event_to_user(buffer + read, &event)) + return -EFAULT; - retval += input_event_size(); - } + read += input_event_size(); + } - if (retval == 0 && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; + if (read) + break; - return retval; + if (!(file->f_flags & O_NONBLOCK)) { + error = wait_event_interruptible(evdev->wait, + client->packet_head != client->tail || + !evdev->exist); + if (error) + return error; + } + } + + return read; } /* No kernel lock - fine */ -- cgit v1.3-8-gc7d7