[hid,v11,09/14] HID: bpf: allow to change the report descriptor

Message ID 20221025093458.457089-10-benjamin.tissoires@redhat.com
State New
Headers
Series Introduce eBPF support for HID devices |

Commit Message

Benjamin Tissoires Oct. 25, 2022, 9:34 a.m. UTC
  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>

---

no changes in v11

no changes in v10

no changes in v9

no changes in v8

no changes in v7

changes in v6:
- use BTF_ID to get the btf_id of hid_bpf_rdesc_fixup

changes in v5:
- adapted for new API

not in v4

changes in v3:
- ensure the ctx.size is properly bounded by allocated size
- s/link_attached/post_link_attach/
- removed the switch statement with only one case

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 drivers/hid/bpf/hid_bpf_dispatch.c  | 77 ++++++++++++++++++++++++++++-
 drivers/hid/bpf/hid_bpf_dispatch.h  |  1 +
 drivers/hid/bpf/hid_bpf_jmp_table.c |  7 +++
 drivers/hid/hid-core.c              |  3 +-
 include/linux/hid_bpf.h             |  8 +++
 5 files changed, 94 insertions(+), 2 deletions(-)
  

Comments

kernel test robot Oct. 31, 2022, 3:22 p.m. UTC | #1
Greeting,

FYI, we noticed BUG:KASAN:slab-out-of-bounds_in_kmemdup due to commit (built with gcc-11):

commit: 885b4af99f79cf1e1f3afb0323f9b6cb8b265fee ("[PATCH hid v11 09/14] HID: bpf: allow to change the report descriptor")
url: https://github.com/intel-lab-lkp/linux/commits/Benjamin-Tissoires/Introduce-eBPF-support-for-HID-devices/20221025-173852
base: https://git.kernel.org/cgit/linux/kernel/git/hid/hid.git master
patch link: https://lore.kernel.org/lkml/20221025093458.457089-10-benjamin.tissoires@redhat.com
patch subject: [PATCH hid v11 09/14] HID: bpf: allow to change the report descriptor

in testcase: kernel-selftests
version: kernel-selftests-x86_64-9313ba54-1_20221017
with following parameters:

	sc_nr_hugepages: 2
	group: vm

test-description: The kernel contains a set of "self tests" under the tools/testing/selftests/ directory. These are intended to be small unit tests to exercise individual code paths in the kernel.
test-url: https://www.kernel.org/doc/Documentation/kselftest.txt


on test machine: 128 threads 2 sockets Intel(R) Xeon(R) Platinum 8358 CPU @ 2.60GHz (Ice Lake) with 128G memory

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):



If you fix the issue, kindly add following tag
| Reported-by: kernel test robot <oliver.sang@intel.com>
| Link: https://lore.kernel.org/oe-lkp/202210312248.4040feba-oliver.sang@intel.com


[ 52.216359][ T712] BUG: KASAN: slab-out-of-bounds in kmemdup (??:?) 
[   52.216359][  T712] Read of size 4096 at addr ff11001095bf1600 by task kworker/0:2/712
[   52.216359][  T712]
[   52.216359][  T712] CPU: 0 PID: 712 Comm: kworker/0:2 Not tainted 6.1.0-rc1-00225-g885b4af99f79 #1
[   52.233046][    T1]  pin0d, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[   52.216359][  T712] Workqueue: usb_hub_wq hub_event
[   52.216359][  T712] Call Trace:
[   52.216359][  T712]  <TASK>
[ 52.216359][ T712] dump_stack_lvl (??:?) 
[ 52.216359][ T712] print_address_description+0x87/0x2a1 
[   52.247482][    T1]  pin0e, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] print_report (report.c:?) 
[ 52.216359][ T712] ? kasan_addr_to_slab (??:?) 
[   52.258662][    T1]  pin0f, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? kmemdup (??:?) 
[ 52.216359][ T712] kasan_report (??:?) 
[   52.272200][    T1]  pin10, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? kmemdup (??:?) 
[ 52.216359][ T712] kasan_check_range (??:?) 
[   52.278146][    T1]  pin11, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] memcpy (??:?) 
[ 52.216359][ T712] kmemdup (??:?) 
[   52.288942][    T1]  pin12, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] call_hid_bpf_rdesc_fixup (??:?) 
[ 52.216359][ T712] ? hid_bpf_disconnect_device (??:?) 
[   52.302011][    T1]  pin13, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? hid_lookup_quirk (??:?) 
[ 52.216359][ T712] ? lock_release (??:?) 
[ 52.216359][ T712] ? __mutex_unlock_slowpath (mutex.c:?) 
[ 52.216359][ T712] ? mutex_lock_io_nested (??:?) 
[   52.315484][    T1]  pin14, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] hid_open_report (??:?) 
[ 52.216359][ T712] ? hid_process_report (??:?) 
[   52.323592][    T1]  pin15, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] hid_generic_probe (hid-generic.c:?) 
[ 52.216359][ T712] hid_device_probe (hid-core.c:?) 
[ 52.216359][ T712] really_probe (dd.c:?) 
[   52.336327][    T1]  pin16, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] __driver_probe_device (dd.c:?) 
[ 52.216359][ T712] driver_probe_device (dd.c:?) 
[ 52.216359][ T712] __device_attach_driver (dd.c:?) 
[ 52.216359][ T712] ? driver_allows_async_probing (dd.c:?) 
[ 52.216359][ T712] bus_for_each_drv (??:?) 
[   52.349636][    T1]  pin17, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? bus_for_each_dev (??:?) 
[ 52.216359][ T712] ? lockdep_hardirqs_on_prepare (lockdep.c:?) 
[   52.357053][    T1]  pin18, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? lockdep_hardirqs_on (??:?) 
[ 52.216359][ T712] ? _raw_spin_unlock_irqrestore (??:?) 
[   52.371058][    T1]  pin19, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] __device_attach (dd.c:?) 
[ 52.216359][ T712] ? device_driver_attach (dd.c:?) 
[   52.385237][    T1]  pin1a, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] bus_probe_device (??:?) 
[   52.394562][    T1]  pin1b, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] device_add (??:?) 
[ 52.216359][ T712] ? __up_write (rwsem.c:?) 
[   52.405466][    T1]  pin1c, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? __debugfs_create_file (inode.c:?) 
[ 52.216359][ T712] ? __fw_devlink_link_to_suppliers (??:?) 
[ 52.216359][ T712] ? __debugfs_create_file (inode.c:?) 
[ 52.216359][ T712] hid_add_device (??:?) 
[   52.418760][    T1]  pin1d, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? lockdep_init_map_type (??:?) 
[ 52.216359][ T712] ? modalias_show (pci-sysfs.c:?) 
[   52.432504][    T1]  pin1e, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? lockdep_count_forward_deps (??:?) 
[ 52.216359][ T712] usbhid_probe (hid-core.c:?) 
[   52.441830][    T1]  pin1f, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] usb_probe_interface (driver.c:?) 
[   52.454895][    T1]  pin20, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] really_probe (dd.c:?) 
[ 52.216359][ T712] __driver_probe_device (dd.c:?) 
[   52.465003][    T1]  pin21, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? usb_match_id (driver.c:?) 
[ 52.216359][ T712] driver_probe_device (dd.c:?) 
[ 52.216359][ T712] __device_attach_driver (dd.c:?) 
[ 52.216359][ T712] ? driver_allows_async_probing (dd.c:?) 
[ 52.216359][ T712] bus_for_each_drv (??:?) 
[ 52.216359][ T712] ? bus_for_each_dev (??:?) 
[   52.475962][    T1]  pin22, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? lockdep_hardirqs_on_prepare (lockdep.c:?) 
[ 52.216359][ T712] ? lockdep_hardirqs_on (??:?) 
[   52.489358][    T1]  pin23, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? _raw_spin_unlock_irqrestore (??:?) 
[ 52.216359][ T712] __device_attach (dd.c:?) 
[   52.500677][    T1]  pin24, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? device_driver_attach (dd.c:?) 
[ 52.216359][ T712] bus_probe_device (??:?) 
[   52.514423][    T1]  pin25, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] device_add (??:?) 
[ 52.216359][ T712] ? __fw_devlink_link_to_suppliers (??:?) 
[   52.528774][    T1]  pin26, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? usb_cache_string (??:?) 
[ 52.216359][ T712] usb_set_configuration (??:?) 
[   52.538619][    T1]  pin27, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? kernfs_create_link (??:?) 
[   52.552017][    T1]  pin28, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)
[ 52.216359][ T712] ? do_raw_spin_unlock (??:?) 
[ 52.216359][ T712] usb_generic_driver_probe (??:?) 
[   52.564984][    T1]  pin29, disabled, edge , high, V(00), IRR(0), S(0), physical, D(0000), M(0)


To reproduce:

        git clone https://github.com/intel/lkp-tests.git
        cd lkp-tests
        sudo bin/lkp install job.yaml           # job file is attached in this email
        bin/lkp split-job --compatible job.yaml # generate the yaml file for lkp run
        sudo bin/lkp run generated-yaml-file

        # if come across any failure that blocks the test,
        # please remove ~/.lkp and /lkp dir to run from a clean state.
  

Patch

diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index c657222f914d..aea8019faca6 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -85,6 +85,63 @@  dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
 }
 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 = kmemdup(rdesc, ctx_kern.ctx.allocated_size, GFP_KERNEL);
+	if (!ctx_kern.data)
+		goto ignore_bpf;
+
+	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
  *
@@ -176,6 +233,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);
 }
 
+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
  *
@@ -217,7 +282,17 @@  hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
 			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;
 }
 
 /**
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h
index e2d64faa3932..2eeab1f746dd 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.h
+++ b/drivers/hid/bpf/hid_bpf_dispatch.h
@@ -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);
 int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
 		     struct hid_bpf_ctx_kern *ctx_kern);
+int hid_bpf_reconnect(struct hid_device *hdev);
 
 struct bpf_prog;
 
diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
index b78925f7fa81..17646ec28eab 100644
--- a/drivers/hid/bpf/hid_bpf_jmp_table.c
+++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
@@ -58,12 +58,15 @@  static DECLARE_WORK(release_work, hid_bpf_release_progs);
 
 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_rdesc_fixup)			/* HID_BPF_PROG_TYPE_RDESC_FIXUP */
 
 static int hid_bpf_max_programs(enum hid_bpf_prog_type type)
 {
 	switch (type) {
 	case HID_BPF_PROG_TYPE_DEVICE_EVENT:
 		return HID_BPF_MAX_PROGS_PER_DEV;
+	case HID_BPF_PROG_TYPE_RDESC_FIXUP:
+		return 1;
 	default:
 		return -EINVAL;
 	}
@@ -233,6 +236,10 @@  static void hid_bpf_release_progs(struct work_struct *work)
 				if (next->hdev == hdev && next->type == type)
 					next->hdev = NULL;
 			}
+
+			/* if type was rdesc fixup, reconnect device */
+			if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP)
+				hid_bpf_reconnect(hdev);
 		}
 	}
 
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 5dac2a1cc205..d4db0dcbdc10 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1218,7 +1218,8 @@  int hid_open_report(struct hid_device *device)
 		return -ENODEV;
 	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)
 		return -ENOMEM;
 
diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
index bf83d5811c51..18a98d2347bc 100644
--- a/include/linux/hid_bpf.h
+++ b/include/linux/hid_bpf.h
@@ -59,6 +59,7 @@  struct hid_bpf_ctx {
 
 /* Following functions are tracepoints that BPF programs can attach to */
 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 */
 /* available everywhere in HID-BPF */
@@ -85,6 +86,7 @@  int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx);
 enum hid_bpf_prog_type {
 	HID_BPF_PROG_TYPE_UNDEF = -1,
 	HID_BPF_PROG_TYPE_DEVICE_EVENT,			/* an event is emitted from the device */
+	HID_BPF_PROG_TYPE_RDESC_FIXUP,
 	HID_BPF_PROG_TYPE_MAX,
 };
 
@@ -128,6 +130,7 @@  int hid_bpf_connect_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_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 */
 static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
 						u8 *data, u32 *size, int interrupt) { return 0; }
@@ -135,6 +138,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_destroy_device(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 /* __HID_BPF_H */