Skip to content

Commit

Permalink
HID: bpf: introduce hid_hw_request()
Browse files Browse the repository at this point in the history
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>

--

changes in v12:
- variable dereferenced before check 'ctx'
  |Reported-by: kernel test robot <lkp@intel.com>
  |Reported-by: Dan Carpenter <error27@gmail.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
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
bentiss authored and Jiri Kosina committed Nov 15, 2022
1 parent 0330f72 commit 91a7f80
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 1 deletion.
134 changes: 134 additions & 0 deletions drivers/hid/bpf/hid_bpf_dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,143 @@ 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_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;

hdev = (struct hid_device *)ctx->hid; /* discard const */

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 = {
Expand Down
2 changes: 2 additions & 0 deletions drivers/hid/hid-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2919,6 +2919,8 @@ EXPORT_SYMBOL_GPL(hid_check_keys_pressed);

#ifdef CONFIG_HID_BPF
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,
};
Expand Down
13 changes: 12 additions & 1 deletion include/linux/hid_bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ enum hid_bpf_attach_flags {
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
Expand All @@ -99,7 +103,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;
};
Expand Down

0 comments on commit 91a7f80

Please sign in to comment.