This function can not be called under IRQ, thus it is only available
while in SEC("syscall").
For consistency, this function requires a HID-BPF context to work with,
and so we also provide a helper to create one based on the HID unique
ID.
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
--
no changes in v11
no changes in v10
changes in v9:
- fixed kfunc declaration aaccording to latest upstream changes
no changes in v8
changes in v7:
- hid_bpf_allocate_context: remove unused variable
- ensures buf is not NULL
changes in v6:
- rename parameter size into buf__sz to teach the verifier about
the actual buffer size used by the call
- remove the allocated data in the user created context, it's not used
new-ish in v5
---
drivers/hid/bpf/hid_bpf_dispatch.c | 132 +++++++++++++++++++++++++++++
drivers/hid/hid-core.c | 2 +
include/linux/hid_bpf.h | 13 ++-
3 files changed, 146 insertions(+), 1 deletion(-)
Hi Benjamin,
url: https://github.com/intel-lab-lkp/linux/commits/Benjamin-Tissoires/Introduce-eBPF-support-for-HID-devices/20221025-173852
base: https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git master
patch link: https://lore.kernel.org/r/20221025093458.457089-8-benjamin.tissoires%40redhat.com
patch subject: [PATCH hid v11 07/14] HID: bpf: introduce hid_hw_request()
config: riscv-randconfig-m041-20221029
compiler: riscv32-linux-gcc (GCC) 12.1.0
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
smatch warnings:
drivers/hid/bpf/hid_bpf_dispatch.c:297 hid_bpf_hw_request() warn: variable dereferenced before check 'ctx' (see line 289)
vim +/ctx +297 drivers/hid/bpf/hid_bpf_dispatch.c
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 285 noinline int
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 286 hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 287 enum hid_report_type rtype, enum hid_class_request reqtype)
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 288 {
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 @289 struct hid_device *hdev = (struct hid_device *)ctx->hid; /* discard const */
^^^^^^^^
Dereference
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 290 struct hid_report *report;
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 291 struct hid_report_enum *report_enum;
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 292 u8 *dma_data;
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 293 u32 report_len;
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 294 int ret;
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 295
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 296 /* check arguments */
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 @297 if (!ctx || !hid_bpf_ops || !buf)
^^^
Checked too late.
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 298 return -EINVAL;
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 299
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 300 switch (rtype) {
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 301 case HID_INPUT_REPORT:
eaf51f2d4f8561 Benjamin Tissoires 2022-10-25 302 case HID_OUTPUT_REPORT:
@@ -220,9 +220,141 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
}
+/**
+ * hid_bpf_allocate_context - Allocate a context to the given HID device
+ *
+ * @hid_id: the system unique identifier of the HID device
+ *
+ * @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error.
+ */
+noinline struct hid_bpf_ctx *
+hid_bpf_allocate_context(unsigned int hid_id)
+{
+ struct hid_device *hdev;
+ struct hid_bpf_ctx_kern *ctx_kern = NULL;
+ struct device *dev;
+
+ if (!hid_bpf_ops)
+ return NULL;
+
+ dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id);
+ if (!dev)
+ return NULL;
+
+ hdev = to_hid_device(dev);
+
+ ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL);
+ if (!ctx_kern)
+ return NULL;
+
+ ctx_kern->ctx.hid = hdev;
+
+ return &ctx_kern->ctx;
+}
+
+/**
+ * hid_bpf_release_context - Release the previously allocated context @ctx
+ *
+ * @ctx: the HID-BPF context to release
+ *
+ */
+noinline void
+hid_bpf_release_context(struct hid_bpf_ctx *ctx)
+{
+ struct hid_bpf_ctx_kern *ctx_kern;
+
+ if (!ctx)
+ return;
+
+ ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
+
+ kfree(ctx_kern);
+}
+
+/**
+ * hid_bpf_hw_request - Communicate with a HID device
+ *
+ * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
+ * @buf: a %PTR_TO_MEM buffer
+ * @buf__sz: the size of the data to transfer
+ * @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
+ * @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...)
+ *
+ * @returns %0 on success, a negative error code otherwise.
+ */
+noinline int
+hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
+ enum hid_report_type rtype, enum hid_class_request reqtype)
+{
+ struct hid_device *hdev = (struct hid_device *)ctx->hid; /* discard const */
+ struct hid_report *report;
+ struct hid_report_enum *report_enum;
+ u8 *dma_data;
+ u32 report_len;
+ int ret;
+
+ /* check arguments */
+ if (!ctx || !hid_bpf_ops || !buf)
+ return -EINVAL;
+
+ switch (rtype) {
+ case HID_INPUT_REPORT:
+ case HID_OUTPUT_REPORT:
+ case HID_FEATURE_REPORT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ case HID_REQ_GET_IDLE:
+ case HID_REQ_GET_PROTOCOL:
+ case HID_REQ_SET_REPORT:
+ case HID_REQ_SET_IDLE:
+ case HID_REQ_SET_PROTOCOL:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (buf__sz < 1)
+ return -EINVAL;
+
+ report_enum = hdev->report_enum + rtype;
+ report = hid_bpf_ops->hid_get_report(report_enum, buf);
+ if (!report)
+ return -EINVAL;
+
+ report_len = hid_report_len(report);
+
+ if (buf__sz > report_len)
+ buf__sz = report_len;
+
+ dma_data = kmemdup(buf, buf__sz, GFP_KERNEL);
+ if (!dma_data)
+ return -ENOMEM;
+
+ ret = hid_bpf_ops->hid_hw_raw_request(hdev,
+ dma_data[0],
+ dma_data,
+ buf__sz,
+ rtype,
+ reqtype);
+
+ if (ret > 0)
+ memcpy(buf, dma_data, ret);
+
+ kfree(dma_data);
+ return ret;
+}
+
/* for syscall HID-BPF */
BTF_SET8_START(hid_bpf_syscall_kfunc_ids)
BTF_ID_FLAGS(func, hid_bpf_attach_prog)
+BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
+BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
+BTF_ID_FLAGS(func, hid_bpf_hw_request)
BTF_SET8_END(hid_bpf_syscall_kfunc_ids)
static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
@@ -2918,6 +2918,8 @@ int hid_check_keys_pressed(struct hid_device *hid)
EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
static struct hid_bpf_ops hid_ops = {
+ .hid_get_report = hid_get_report,
+ .hid_hw_raw_request = hid_hw_raw_request,
.owner = THIS_MODULE,
.bus_type = &hid_bus_type,
};
@@ -61,11 +61,15 @@ struct hid_bpf_ctx {
int hid_bpf_device_event(struct hid_bpf_ctx *ctx);
/* Following functions are kfunc that we export to BPF programs */
-/* only available in tracing */
+/* available everywhere in HID-BPF */
__u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz);
/* only available in syscall */
int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags);
+int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
+ enum hid_report_type rtype, enum hid_class_request reqtype);
+struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id);
+void hid_bpf_release_context(struct hid_bpf_ctx *ctx);
/*
* Below is HID internal
@@ -84,7 +88,14 @@ enum hid_bpf_prog_type {
HID_BPF_PROG_TYPE_MAX,
};
+struct hid_report_enum;
+
struct hid_bpf_ops {
+ struct hid_report *(*hid_get_report)(struct hid_report_enum *report_enum, const u8 *data);
+ int (*hid_hw_raw_request)(struct hid_device *hdev,
+ unsigned char reportnum, __u8 *buf,
+ size_t len, enum hid_report_type rtype,
+ enum hid_class_request reqtype);
struct module *owner;
struct bus_type *bus_type;
};