Skip to content

Commit b31c9d9

Browse files
whotBenjamin Tissoires
authored andcommitted
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 c7dc657 ("Input: evdev - add EVIOCREVOKE ioctl") for full details. An MR for systemd-logind has been filed here: systemd/systemd#33970 Signed-off-by: Peter Hutterer <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent 6e44365 commit b31c9d9

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

drivers/hid/hidraw.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,20 @@ static const struct class hidraw_class = {
3838
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
3939
static DECLARE_RWSEM(minors_rwsem);
4040

41+
static inline bool hidraw_is_revoked(struct hidraw_list *list)
42+
{
43+
return list->revoked;
44+
}
45+
4146
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
4247
{
4348
struct hidraw_list *list = file->private_data;
4449
int ret = 0, len;
4550
DECLARE_WAITQUEUE(wait, current);
4651

52+
if (hidraw_is_revoked(list))
53+
return -ENODEV;
54+
4755
mutex_lock(&list->read_mutex);
4856

4957
while (ret == 0) {
@@ -161,9 +169,13 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
161169

162170
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
163171
{
172+
struct hidraw_list *list = file->private_data;
164173
ssize_t ret;
165174
down_read(&minors_rwsem);
166-
ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
175+
if (hidraw_is_revoked(list))
176+
ret = -ENODEV;
177+
else
178+
ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
167179
up_read(&minors_rwsem);
168180
return ret;
169181
}
@@ -256,7 +268,7 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait)
256268
poll_wait(file, &list->hidraw->wait, wait);
257269
if (list->head != list->tail)
258270
mask |= EPOLLIN | EPOLLRDNORM;
259-
if (!list->hidraw->exist)
271+
if (!list->hidraw->exist || hidraw_is_revoked(list))
260272
mask |= EPOLLERR | EPOLLHUP;
261273
return mask;
262274
}
@@ -320,6 +332,9 @@ static int hidraw_fasync(int fd, struct file *file, int on)
320332
{
321333
struct hidraw_list *list = file->private_data;
322334

335+
if (hidraw_is_revoked(list))
336+
return -ENODEV;
337+
323338
return fasync_helper(fd, file, on, &list->fasync);
324339
}
325340

@@ -372,18 +387,26 @@ static int hidraw_release(struct inode * inode, struct file * file)
372387
return 0;
373388
}
374389

390+
static int hidraw_revoke(struct hidraw_list *list)
391+
{
392+
list->revoked = true;
393+
394+
return 0;
395+
}
396+
375397
static long hidraw_ioctl(struct file *file, unsigned int cmd,
376398
unsigned long arg)
377399
{
378400
struct inode *inode = file_inode(file);
379401
unsigned int minor = iminor(inode);
380402
long ret = 0;
381403
struct hidraw *dev;
404+
struct hidraw_list *list = file->private_data;
382405
void __user *user_arg = (void __user*) arg;
383406

384407
down_read(&minors_rwsem);
385408
dev = hidraw_table[minor];
386-
if (!dev || !dev->exist) {
409+
if (!dev || !dev->exist || hidraw_is_revoked(list)) {
387410
ret = -ENODEV;
388411
goto out;
389412
}
@@ -421,6 +444,14 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
421444
ret = -EFAULT;
422445
break;
423446
}
447+
case HIDIOCREVOKE:
448+
{
449+
if (user_arg)
450+
ret = -EINVAL;
451+
else
452+
ret = hidraw_revoke(list);
453+
break;
454+
}
424455
default:
425456
{
426457
struct hid_device *hid = dev->hid;
@@ -527,7 +558,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
527558
list_for_each_entry(list, &dev->list, node) {
528559
int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
529560

530-
if (new_head == list->tail)
561+
if (hidraw_is_revoked(list) || new_head == list->tail)
531562
continue;
532563

533564
if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {

include/linux/hidraw.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct hidraw_list {
3232
struct hidraw *hidraw;
3333
struct list_head node;
3434
struct mutex read_mutex;
35+
bool revoked;
3536
};
3637

3738
#ifdef CONFIG_HIDRAW

include/uapi/linux/hidraw.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ struct hidraw_devinfo {
4646
/* The first byte of SOUTPUT and GOUTPUT is the report number */
4747
#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
4848
#define HIDIOCGOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0C, len)
49+
#define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */
4950

5051
#define HIDRAW_FIRST_MINOR 0
5152
#define HIDRAW_MAX_DEVICES 64

0 commit comments

Comments
 (0)