[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20240827-hidraw-revoke-v4-1-88c6795bf867@kernel.org>
Date: Tue, 27 Aug 2024 00:47:51 +0900
From: bentiss@...nel.org
To: Jiri Kosina <jikos@...nel.org>, Shuah Khan <shuah@...nel.org>,
Peter Hutterer <peter.hutterer@...-t.net>
Cc: linux-input@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-kselftest@...r.kernel.org, Benjamin Tissoires <bentiss@...nel.org>
Subject: [PATCH v4 1/3] HID: hidraw: add HIDIOCREVOKE ioctl
From: Peter Hutterer <peter.hutterer@...-t.net>
There is a need for userspace applications to open HID devices directly.
Use-cases include configuration of gaming mice or direct access to
joystick devices. The latter is currently handled by the uaccess tag in
systemd, other devices include more custom/local configurations or just
sudo.
A better approach is what we already have for evdev devices: give the
application a file descriptor and revoke it when it may no longer access
that device.
This patch is the hidraw equivalent to the EVIOCREVOKE ioctl, see
commit c7dc65737c9a ("Input: evdev - add EVIOCREVOKE ioctl") for full
details.
An MR for systemd-logind has been filed here:
https://github.com/systemd/systemd/pull/33970
Signed-off-by: Peter Hutterer <peter.hutterer@...-t.net>
Signed-off-by: Benjamin Tissoires <bentiss@...nel.org>
---
First version of the patch:
https://patchwork.kernel.org/project/linux-input/patch/YmEAPZKDisM2HAsG@quokka/
Changes to v1:
- add the hidraw_is_revoked and hidraw_open_errno weak functions as
suggested by Benjamin
Changes to v2:
- use __bpf_hook_start/end to silence compiler warnings (see kernel
test bot)
Changes to v3 (bentiss):
- drop the BPF related changes, the API is not clear ATM, and it's wrong
in v2, as ERROR_INJECTION should not be used for normal fmodret.
- drop hidraw_open_errno() new function. The API seems interesting, but
nothing is really ready, so let's not add a useless API for nothing.
---
drivers/hid/hidraw.c | 39 +++++++++++++++++++++++++++++++++++----
include/linux/hidraw.h | 1 +
include/uapi/linux/hidraw.h | 1 +
3 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 716294e40e8a..c887f48756f4 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -38,12 +38,20 @@ static const struct class hidraw_class = {
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DECLARE_RWSEM(minors_rwsem);
+static inline bool hidraw_is_revoked(struct hidraw_list *list)
+{
+ return list->revoked;
+}
+
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct hidraw_list *list = file->private_data;
int ret = 0, len;
DECLARE_WAITQUEUE(wait, current);
+ if (hidraw_is_revoked(list))
+ return -ENODEV;
+
mutex_lock(&list->read_mutex);
while (ret == 0) {
@@ -161,9 +169,13 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
+ struct hidraw_list *list = file->private_data;
ssize_t ret;
down_read(&minors_rwsem);
- ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
+ if (hidraw_is_revoked(list))
+ ret = -ENODEV;
+ else
+ ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
up_read(&minors_rwsem);
return ret;
}
@@ -256,7 +268,7 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait)
poll_wait(file, &list->hidraw->wait, wait);
if (list->head != list->tail)
mask |= EPOLLIN | EPOLLRDNORM;
- if (!list->hidraw->exist)
+ if (!list->hidraw->exist || hidraw_is_revoked(list))
mask |= EPOLLERR | EPOLLHUP;
return mask;
}
@@ -320,6 +332,9 @@ static int hidraw_fasync(int fd, struct file *file, int on)
{
struct hidraw_list *list = file->private_data;
+ if (hidraw_is_revoked(list))
+ return -ENODEV;
+
return fasync_helper(fd, file, on, &list->fasync);
}
@@ -372,6 +387,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
return 0;
}
+static int hidraw_revoke(struct hidraw_list *list)
+{
+ list->revoked = true;
+
+ return 0;
+}
+
static long hidraw_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -379,11 +401,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
unsigned int minor = iminor(inode);
long ret = 0;
struct hidraw *dev;
+ struct hidraw_list *list = file->private_data;
void __user *user_arg = (void __user*) arg;
down_read(&minors_rwsem);
dev = hidraw_table[minor];
- if (!dev || !dev->exist) {
+ if (!dev || !dev->exist || hidraw_is_revoked(list)) {
ret = -ENODEV;
goto out;
}
@@ -421,6 +444,14 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
ret = -EFAULT;
break;
}
+ case HIDIOCREVOKE:
+ {
+ if (user_arg)
+ ret = -EINVAL;
+ else
+ ret = hidraw_revoke(list);
+ break;
+ }
default:
{
struct hid_device *hid = dev->hid;
@@ -527,7 +558,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
list_for_each_entry(list, &dev->list, node) {
int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
- if (new_head == list->tail)
+ if (hidraw_is_revoked(list) || new_head == list->tail)
continue;
if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {
diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h
index cd67f4ca5599..18fd30a288de 100644
--- a/include/linux/hidraw.h
+++ b/include/linux/hidraw.h
@@ -32,6 +32,7 @@ struct hidraw_list {
struct hidraw *hidraw;
struct list_head node;
struct mutex read_mutex;
+ bool revoked;
};
#ifdef CONFIG_HIDRAW
diff --git a/include/uapi/linux/hidraw.h b/include/uapi/linux/hidraw.h
index 33ebad81720a..d5ee269864e0 100644
--- a/include/uapi/linux/hidraw.h
+++ b/include/uapi/linux/hidraw.h
@@ -46,6 +46,7 @@ struct hidraw_devinfo {
/* The first byte of SOUTPUT and GOUTPUT is the report number */
#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
#define HIDIOCGOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0C, len)
+#define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */
#define HIDRAW_FIRST_MINOR 0
#define HIDRAW_MAX_DEVICES 64
--
2.46.0
Powered by blists - more mailing lists