mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 22:54:05 +08:00
HID: bpf: allow to change the report descriptor
Add a new tracepoint hid_bpf_rdesc_fixup() so we can trigger a report descriptor fixup in the bpf world. Whenever the program gets attached/detached, the device is reconnected meaning that userspace will see it disappearing and reappearing with the new report descriptor. Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
4f7153cf46
commit
ad190df11a
@ -15,6 +15,7 @@
|
|||||||
#include <linux/hid_bpf.h>
|
#include <linux/hid_bpf.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kfifo.h>
|
#include <linux/kfifo.h>
|
||||||
|
#include <linux/minmax.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include "hid_bpf_dispatch.h"
|
#include "hid_bpf_dispatch.h"
|
||||||
@ -85,6 +86,65 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
|
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_bpf_rdesc_fixup - Called when the probe function parses the report
|
||||||
|
* descriptor of the HID device
|
||||||
|
*
|
||||||
|
* @ctx: The HID-BPF context
|
||||||
|
*
|
||||||
|
* @return 0 on success and keep processing; a positive value to change the
|
||||||
|
* incoming size buffer; a negative error code to interrupt the processing
|
||||||
|
* of this event
|
||||||
|
*
|
||||||
|
* Declare an %fmod_ret tracing bpf program to this function and attach this
|
||||||
|
* program through hid_bpf_attach_prog() to have this helper called before any
|
||||||
|
* parsing of the report descriptor by HID.
|
||||||
|
*/
|
||||||
|
/* never used by the kernel but declared so we can load and attach a tracepoint */
|
||||||
|
__weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ALLOW_ERROR_INJECTION(hid_bpf_rdesc_fixup, ERRNO);
|
||||||
|
|
||||||
|
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct hid_bpf_ctx_kern ctx_kern = {
|
||||||
|
.ctx = {
|
||||||
|
.hid = hdev,
|
||||||
|
.size = *size,
|
||||||
|
.allocated_size = HID_MAX_DESCRIPTOR_SIZE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL);
|
||||||
|
if (!ctx_kern.data)
|
||||||
|
goto ignore_bpf;
|
||||||
|
|
||||||
|
memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE));
|
||||||
|
|
||||||
|
ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern);
|
||||||
|
if (ret < 0)
|
||||||
|
goto ignore_bpf;
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
if (ret > ctx_kern.ctx.allocated_size)
|
||||||
|
goto ignore_bpf;
|
||||||
|
|
||||||
|
*size = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL);
|
||||||
|
|
||||||
|
return rdesc;
|
||||||
|
|
||||||
|
ignore_bpf:
|
||||||
|
kfree(ctx_kern.data);
|
||||||
|
return kmemdup(rdesc, *size, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx
|
* hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx
|
||||||
*
|
*
|
||||||
@ -176,6 +236,14 @@ static int hid_bpf_allocate_event_data(struct hid_device *hdev)
|
|||||||
return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data);
|
return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hid_bpf_reconnect(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))
|
||||||
|
return device_reprobe(&hdev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device
|
* hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device
|
||||||
*
|
*
|
||||||
@ -217,7 +285,17 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
|
err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) {
|
||||||
|
err = hid_bpf_reconnect(hdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,6 +18,7 @@ int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_t
|
|||||||
void __hid_bpf_destroy_device(struct hid_device *hdev);
|
void __hid_bpf_destroy_device(struct hid_device *hdev);
|
||||||
int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
|
int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
|
||||||
struct hid_bpf_ctx_kern *ctx_kern);
|
struct hid_bpf_ctx_kern *ctx_kern);
|
||||||
|
int hid_bpf_reconnect(struct hid_device *hdev);
|
||||||
|
|
||||||
struct bpf_prog;
|
struct bpf_prog;
|
||||||
|
|
||||||
|
@ -58,12 +58,15 @@ static DECLARE_WORK(release_work, hid_bpf_release_progs);
|
|||||||
|
|
||||||
BTF_ID_LIST(hid_bpf_btf_ids)
|
BTF_ID_LIST(hid_bpf_btf_ids)
|
||||||
BTF_ID(func, hid_bpf_device_event) /* HID_BPF_PROG_TYPE_DEVICE_EVENT */
|
BTF_ID(func, hid_bpf_device_event) /* HID_BPF_PROG_TYPE_DEVICE_EVENT */
|
||||||
|
BTF_ID(func, hid_bpf_rdesc_fixup) /* HID_BPF_PROG_TYPE_RDESC_FIXUP */
|
||||||
|
|
||||||
static int hid_bpf_max_programs(enum hid_bpf_prog_type type)
|
static int hid_bpf_max_programs(enum hid_bpf_prog_type type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case HID_BPF_PROG_TYPE_DEVICE_EVENT:
|
case HID_BPF_PROG_TYPE_DEVICE_EVENT:
|
||||||
return HID_BPF_MAX_PROGS_PER_DEV;
|
return HID_BPF_MAX_PROGS_PER_DEV;
|
||||||
|
case HID_BPF_PROG_TYPE_RDESC_FIXUP:
|
||||||
|
return 1;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -233,6 +236,10 @@ static void hid_bpf_release_progs(struct work_struct *work)
|
|||||||
if (next->hdev == hdev && next->type == type)
|
if (next->hdev == hdev && next->type == type)
|
||||||
next->hdev = NULL;
|
next->hdev = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if type was rdesc fixup, reconnect device */
|
||||||
|
if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP)
|
||||||
|
hid_bpf_reconnect(hdev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1218,7 +1218,8 @@ int hid_open_report(struct hid_device *device)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
size = device->dev_rsize;
|
size = device->dev_rsize;
|
||||||
|
|
||||||
buf = kmemdup(start, size, GFP_KERNEL);
|
/* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */
|
||||||
|
buf = call_hid_bpf_rdesc_fixup(device, start, &size);
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ enum hid_bpf_attach_flags {
|
|||||||
|
|
||||||
/* Following functions are tracepoints that BPF programs can attach to */
|
/* Following functions are tracepoints that BPF programs can attach to */
|
||||||
int hid_bpf_device_event(struct hid_bpf_ctx *ctx);
|
int hid_bpf_device_event(struct hid_bpf_ctx *ctx);
|
||||||
|
int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx);
|
||||||
|
|
||||||
/* Following functions are kfunc that we export to BPF programs */
|
/* Following functions are kfunc that we export to BPF programs */
|
||||||
/* available everywhere in HID-BPF */
|
/* available everywhere in HID-BPF */
|
||||||
@ -100,6 +101,7 @@ int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx);
|
|||||||
enum hid_bpf_prog_type {
|
enum hid_bpf_prog_type {
|
||||||
HID_BPF_PROG_TYPE_UNDEF = -1,
|
HID_BPF_PROG_TYPE_UNDEF = -1,
|
||||||
HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */
|
HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */
|
||||||
|
HID_BPF_PROG_TYPE_RDESC_FIXUP,
|
||||||
HID_BPF_PROG_TYPE_MAX,
|
HID_BPF_PROG_TYPE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,6 +145,7 @@ int hid_bpf_connect_device(struct hid_device *hdev);
|
|||||||
void hid_bpf_disconnect_device(struct hid_device *hdev);
|
void hid_bpf_disconnect_device(struct hid_device *hdev);
|
||||||
void hid_bpf_destroy_device(struct hid_device *hid);
|
void hid_bpf_destroy_device(struct hid_device *hid);
|
||||||
void hid_bpf_device_init(struct hid_device *hid);
|
void hid_bpf_device_init(struct hid_device *hid);
|
||||||
|
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size);
|
||||||
#else /* CONFIG_HID_BPF */
|
#else /* CONFIG_HID_BPF */
|
||||||
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
||||||
u8 *data, u32 *size, int interrupt) { return NULL; }
|
u8 *data, u32 *size, int interrupt) { return NULL; }
|
||||||
@ -150,6 +153,11 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; }
|
|||||||
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
|
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
|
||||||
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
|
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
|
||||||
static inline void hid_bpf_device_init(struct hid_device *hid) {}
|
static inline void hid_bpf_device_init(struct hid_device *hid) {}
|
||||||
|
static inline u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
|
||||||
|
{
|
||||||
|
return kmemdup(rdesc, *size, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_HID_BPF */
|
#endif /* CONFIG_HID_BPF */
|
||||||
|
|
||||||
#endif /* __HID_BPF_H */
|
#endif /* __HID_BPF_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user