2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

HID: hidraw: add HIDIOCREVOKE ioctl

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 c7dc65737c ("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@who-t.net>
Link: https://patch.msgid.link/20240827-hidraw-revoke-v5-1-d004a7451aea@kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
This commit is contained in:
Peter Hutterer 2024-08-27 17:19:29 +09:00 committed by Benjamin Tissoires
parent 6e4436539a
commit b31c9d9dc3
3 changed files with 37 additions and 4 deletions

View File

@ -38,12 +38,20 @@ static const struct class hidraw_class = {
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DECLARE_RWSEM(minors_rwsem); 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) static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{ {
struct hidraw_list *list = file->private_data; struct hidraw_list *list = file->private_data;
int ret = 0, len; int ret = 0, len;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
if (hidraw_is_revoked(list))
return -ENODEV;
mutex_lock(&list->read_mutex); mutex_lock(&list->read_mutex);
while (ret == 0) { while (ret == 0) {
@ -161,9 +169,13 @@ out:
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 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; ssize_t ret;
down_read(&minors_rwsem); 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); up_read(&minors_rwsem);
return ret; return ret;
} }
@ -256,7 +268,7 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait)
poll_wait(file, &list->hidraw->wait, wait); poll_wait(file, &list->hidraw->wait, wait);
if (list->head != list->tail) if (list->head != list->tail)
mask |= EPOLLIN | EPOLLRDNORM; mask |= EPOLLIN | EPOLLRDNORM;
if (!list->hidraw->exist) if (!list->hidraw->exist || hidraw_is_revoked(list))
mask |= EPOLLERR | EPOLLHUP; mask |= EPOLLERR | EPOLLHUP;
return mask; return mask;
} }
@ -320,6 +332,9 @@ static int hidraw_fasync(int fd, struct file *file, int on)
{ {
struct hidraw_list *list = file->private_data; struct hidraw_list *list = file->private_data;
if (hidraw_is_revoked(list))
return -ENODEV;
return fasync_helper(fd, file, on, &list->fasync); return fasync_helper(fd, file, on, &list->fasync);
} }
@ -372,6 +387,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
return 0; 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, static long hidraw_ioctl(struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
@ -379,11 +401,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
unsigned int minor = iminor(inode); unsigned int minor = iminor(inode);
long ret = 0; long ret = 0;
struct hidraw *dev; struct hidraw *dev;
struct hidraw_list *list = file->private_data;
void __user *user_arg = (void __user*) arg; void __user *user_arg = (void __user*) arg;
down_read(&minors_rwsem); down_read(&minors_rwsem);
dev = hidraw_table[minor]; dev = hidraw_table[minor];
if (!dev || !dev->exist) { if (!dev || !dev->exist || hidraw_is_revoked(list)) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
} }
@ -421,6 +444,14 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
ret = -EFAULT; ret = -EFAULT;
break; break;
} }
case HIDIOCREVOKE:
{
if (user_arg)
ret = -EINVAL;
else
ret = hidraw_revoke(list);
break;
}
default: default:
{ {
struct hid_device *hid = dev->hid; 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) { list_for_each_entry(list, &dev->list, node) {
int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); 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; continue;
if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) { if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {

View File

@ -32,6 +32,7 @@ struct hidraw_list {
struct hidraw *hidraw; struct hidraw *hidraw;
struct list_head node; struct list_head node;
struct mutex read_mutex; struct mutex read_mutex;
bool revoked;
}; };
#ifdef CONFIG_HIDRAW #ifdef CONFIG_HIDRAW

View File

@ -46,6 +46,7 @@ struct hidraw_devinfo {
/* The first byte of SOUTPUT and GOUTPUT is the report number */ /* The first byte of SOUTPUT and GOUTPUT is the report number */
#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len) #define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
#define HIDIOCGOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0C, 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_FIRST_MINOR 0
#define HIDRAW_MAX_DEVICES 64 #define HIDRAW_MAX_DEVICES 64