Skip to content

Commit 9286675

Browse files
author
Benjamin Tissoires
committed
HID: bpf: add HID-BPF hooks for hid_hw_output_report
Same story than hid_hw_raw_requests: This allows to intercept and prevent or change the behavior of hid_hw_output_report() from a bpf program. The intent is to solve a couple of use case: - firewalling a HID device: a firewall can monitor who opens the hidraw nodes and then prevent or allow access to write operations on that hidraw node. - change the behavior of a device and emulate a new HID feature request The hook is allowed to be run as sleepable so it can itself call hid_hw_output_report(), which allows to "convert" one feature request into another or even call the feature request on a different HID device on the same physical device. Link: https://patch.msgid.link/[email protected] Acked-by: Jiri Kosina <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent 015a4a2 commit 9286675

File tree

6 files changed

+70
-9
lines changed

6 files changed

+70
-9
lines changed

drivers/hid/bpf/hid_bpf_dispatch.c

+35-4
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,40 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
113113
}
114114
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests);
115115

116+
int dispatch_hid_bpf_output_report(struct hid_device *hdev,
117+
__u8 *buf, u32 size, __u64 source,
118+
bool from_bpf)
119+
{
120+
struct hid_bpf_ctx_kern ctx_kern = {
121+
.ctx = {
122+
.hid = hdev,
123+
.allocated_size = size,
124+
.size = size,
125+
},
126+
.data = buf,
127+
.from_bpf = from_bpf,
128+
};
129+
struct hid_bpf_ops *e;
130+
int ret, idx;
131+
132+
idx = srcu_read_lock(&hdev->bpf.srcu);
133+
list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,
134+
srcu_read_lock_held(&hdev->bpf.srcu)) {
135+
if (!e->hid_hw_output_report)
136+
continue;
137+
138+
ret = e->hid_hw_output_report(&ctx_kern.ctx, source);
139+
if (ret)
140+
goto out;
141+
}
142+
ret = 0;
143+
144+
out:
145+
srcu_read_unlock(&hdev->bpf.srcu, idx);
146+
return ret;
147+
}
148+
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report);
149+
116150
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
117151
{
118152
int ret;
@@ -443,10 +477,7 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
443477
if (!dma_data)
444478
return -ENOMEM;
445479

446-
ret = hid_ops->hid_hw_output_report(hdev,
447-
dma_data,
448-
size,
449-
(__u64)ctx);
480+
ret = hid_ops->hid_hw_output_report(hdev, dma_data, size, (__u64)ctx, true);
450481

451482
kfree(dma_data);
452483
return ret;

drivers/hid/bpf/hid_bpf_struct_ops.c

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ static int hid_bpf_ops_check_member(const struct btf_type *t,
4545
switch (moff) {
4646
case offsetof(struct hid_bpf_ops, hid_rdesc_fixup):
4747
case offsetof(struct hid_bpf_ops, hid_hw_request):
48+
case offsetof(struct hid_bpf_ops, hid_hw_output_report):
4849
break;
4950
default:
5051
if (prog->sleepable)

drivers/hid/hid-core.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -2445,16 +2445,22 @@ int hid_hw_raw_request(struct hid_device *hdev,
24452445
}
24462446
EXPORT_SYMBOL_GPL(hid_hw_raw_request);
24472447

2448-
int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source)
2448+
int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source,
2449+
bool from_bpf)
24492450
{
24502451
unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE;
2452+
int ret;
24512453

24522454
if (hdev->ll_driver->max_buffer_size)
24532455
max_buffer_size = hdev->ll_driver->max_buffer_size;
24542456

24552457
if (len < 1 || len > max_buffer_size || !buf)
24562458
return -EINVAL;
24572459

2460+
ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf);
2461+
if (ret)
2462+
return ret;
2463+
24582464
if (hdev->ll_driver->output_report)
24592465
return hdev->ll_driver->output_report(hdev, buf, len);
24602466

@@ -2472,7 +2478,7 @@ int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64
24722478
*/
24732479
int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len)
24742480
{
2475-
return __hid_hw_output_report(hdev, buf, len, 0);
2481+
return __hid_hw_output_report(hdev, buf, len, 0, false);
24762482
}
24772483
EXPORT_SYMBOL_GPL(hid_hw_output_report);
24782484

drivers/hid/hidraw.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
140140

141141
if ((report_type == HID_OUTPUT_REPORT) &&
142142
!(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
143-
ret = __hid_hw_output_report(dev, buf, count, (__u64)file);
143+
ret = __hid_hw_output_report(dev, buf, count, (__u64)file, false);
144144
/*
145145
* compatibility with old implementation of USB-HID and I2C-HID:
146146
* if the device does not support receiving output reports,

include/linux/hid.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1130,7 +1130,8 @@ int __hid_hw_raw_request(struct hid_device *hdev,
11301130
size_t len, enum hid_report_type rtype,
11311131
enum hid_class_request reqtype,
11321132
__u64 source, bool from_bpf);
1133-
int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source);
1133+
int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source,
1134+
bool from_bpf);
11341135
int hid_hw_raw_request(struct hid_device *hdev,
11351136
unsigned char reportnum, __u8 *buf,
11361137
size_t len, enum hid_report_type rtype,

include/linux/hid_bpf.h

+23-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct hid_ops {
7070
enum hid_class_request reqtype,
7171
__u64 source, bool from_bpf);
7272
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
73-
__u64 source);
73+
__u64 source, bool from_bpf);
7474
int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
7575
u8 *data, u32 size, int interrupt, u64 source);
7676
struct module *owner;
@@ -154,6 +154,24 @@ struct hid_bpf_ops {
154154
enum hid_report_type rtype, enum hid_class_request reqtype,
155155
__u64 source);
156156

157+
/**
158+
* @hid_hw_output_report: called whenever a hid_hw_output_report() call is emitted
159+
* on the HID device
160+
*
161+
* It has the following arguments:
162+
*
163+
* ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
164+
* ``source``: a u64 referring to a uniq but identifiable source. If %0, the
165+
* kernel itself emitted that call. For hidraw, ``source`` is set
166+
* to the associated ``struct file *``.
167+
*
168+
* Return: %0 to keep processing the request by hid-core; any other value
169+
* stops hid-core from processing that event. A positive value should be
170+
* returned with the number of bytes written to the device; a negative error
171+
* code interrupts the processing of this call.
172+
*/
173+
int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, __u64 source);
174+
157175

158176
/* private: do not show up in the docs */
159177
struct hid_device *hdev;
@@ -182,6 +200,8 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
182200
u32 size, enum hid_report_type rtype,
183201
enum hid_class_request reqtype,
184202
__u64 source, bool from_bpf);
203+
int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size,
204+
__u64 source, bool from_bpf);
185205
int hid_bpf_connect_device(struct hid_device *hdev);
186206
void hid_bpf_disconnect_device(struct hid_device *hdev);
187207
void hid_bpf_destroy_device(struct hid_device *hid);
@@ -196,6 +216,8 @@ static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
196216
u32 size, enum hid_report_type rtype,
197217
enum hid_class_request reqtype,
198218
u64 source, bool from_bpf) { return 0; }
219+
static inline int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size,
220+
__u64 source, bool from_bpf) { return 0; }
199221
static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; }
200222
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
201223
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}

0 commit comments

Comments
 (0)