* [PATCH 1/3] selftests/hid: add define for commonly used buf size
2026-09-04 12:52 [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation Benjamin Tissoires
@ 2026-09-04 12:52 ` Benjamin Tissoires
2026-09-04 12:53 ` [PATCH 2/3] HID: bpf: fix __hid_bpf_hw_check_params report length Benjamin Tissoires
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Benjamin Tissoires @ 2026-09-04 12:52 UTC (permalink / raw)
To: Jiri Kosina, Shuah Khan
Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires
If we want to add another report descriptor without report IDs with a
report size bigger than 10, we have multiple magic values to replace.
Put a #define once and for all, so we don't have dangling ones.
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
tools/testing/selftests/hid/hid_bpf.c | 38 ++++++++++++++++----------------
tools/testing/selftests/hid/hid_common.h | 3 ++-
2 files changed, 21 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index b851339308c2..e865ee55b4f1 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -5,7 +5,7 @@
#include <bpf/bpf.h>
struct hid_hw_request_syscall_args {
- __u8 data[10];
+ __u8 data[MAX_BUF_SIZE];
unsigned int hid;
int retval;
size_t size;
@@ -168,7 +168,7 @@ TEST_F(hid_bpf, raw_event)
const struct test_program progs[] = {
{ .name = "hid_first_event" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -219,7 +219,7 @@ TEST_F(hid_bpf, subprog_raw_event)
const struct test_program progs[] = {
{ .name = "hid_subprog_first_event" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -277,7 +277,7 @@ TEST_F(hid_bpf, test_attach_detach)
{ .name = "hid_second_event" },
};
struct bpf_link *link;
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err, link_fd;
LOAD_PROGRAMS(progs);
@@ -362,7 +362,7 @@ TEST_F(hid_bpf, test_hid_change_report)
const struct test_program progs[] = {
{ .name = "hid_change_report_id" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -389,13 +389,13 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
{
struct hid_hw_request_syscall_args args = {
.retval = -1,
- .size = 10,
+ .size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
.ctx_size_in = sizeof(args),
);
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err, prog_fd;
LOAD_BPF;
@@ -435,7 +435,7 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
{
struct hid_hw_request_syscall_args args = {
.retval = -1,
- .size = 10,
+ .size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
@@ -484,7 +484,7 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
.retval = -1,
.type = HID_FEATURE_REPORT,
.request_type = HID_REQ_GET_REPORT,
- .size = 10,
+ .size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
@@ -517,7 +517,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_filter_raw_request" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -570,7 +570,7 @@ TEST_F(hid_bpf, test_hid_change_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_hidraw_raw_request" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -596,7 +596,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_raw_request" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -619,7 +619,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_filter_output_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -672,7 +672,7 @@ TEST_F(hid_bpf, test_hid_change_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_hidraw_output_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -696,7 +696,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_output_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -722,7 +722,7 @@ TEST_F(hid_bpf, test_multiply_events_wq)
const struct test_program progs[] = {
{ .name = "hid_test_multiply_events_wq" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -760,7 +760,7 @@ TEST_F(hid_bpf, test_multiply_events)
const struct test_program progs[] = {
{ .name = "hid_test_multiply_events" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -794,7 +794,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_input_report" },
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@@ -848,7 +848,7 @@ TEST_F(hid_bpf, test_hid_attach_flags)
.insert_head = 0,
},
};
- __u8 buf[10] = {0};
+ __u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
diff --git a/tools/testing/selftests/hid/hid_common.h b/tools/testing/selftests/hid/hid_common.h
index e3b267446fa0..4567336f131d 100644
--- a/tools/testing/selftests/hid/hid_common.h
+++ b/tools/testing/selftests/hid/hid_common.h
@@ -13,6 +13,7 @@
#include <linux/uhid.h>
#define SHOW_UHID_DEBUG 0
+#define MAX_BUF_SIZE 10
#define min(a, b) \
({ __typeof__(a) _a = (a); \
@@ -110,7 +111,7 @@ static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;
-static unsigned char output_report[10];
+static unsigned char output_report[MAX_BUF_SIZE];
/* no need to protect uhid_stopped, only one thread accesses it */
static bool uhid_stopped;
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/3] HID: bpf: fix __hid_bpf_hw_check_params report length
2026-09-04 12:52 [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation Benjamin Tissoires
2026-09-04 12:52 ` [PATCH 1/3] selftests/hid: add define for commonly used buf size Benjamin Tissoires
@ 2026-09-04 12:53 ` Benjamin Tissoires
2026-09-04 12:53 ` [PATCH 3/3] selftests/hid: add unnumbered variant to the hid_bpf tests Benjamin Tissoires
2026-09-14 16:07 ` [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation Benjamin Tissoires
3 siblings, 0 replies; 5+ messages in thread
From: Benjamin Tissoires @ 2026-09-04 12:53 UTC (permalink / raw)
To: Jiri Kosina, Shuah Khan
Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires
Turns out that USB, I2C and other transport drivers (except uhid which
just passes the data) still need to have the report ID in the first
byte.
Because they expect the first byte to be the report ID or 0, when the
report ID is 0, they strip that first byte before forwarding to the
device. This means that the transport layer forwards a buffer of size
N-1 to the device, which gets rejected.
Fixes: 5599f8019661 ("HID: bpf: export hid_hw_output_report as a BPF kfunc")
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
drivers/hid/bpf/hid_bpf_dispatch.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 536f6d01fd14..b1de1dd0f21d 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -359,7 +359,7 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx)
static int
__hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,
- enum hid_report_type rtype)
+ enum hid_report_type rtype, bool hw_request)
{
struct hid_report_enum *report_enum;
struct hid_report *report;
@@ -388,6 +388,10 @@ __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,
report_len = hid_report_len(report);
+ /* unnumbered reports need to have a report ID reserved in the first byte */
+ if (hw_request && report_enum->numbered == 0)
+ report_len += 1;
+
if (*buf__sz > report_len)
*buf__sz = report_len;
@@ -420,7 +424,7 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
return -EDEADLOCK;
/* check arguments */
- ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype);
+ ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype, true);
if (ret)
return ret;
@@ -480,7 +484,7 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
return -EDEADLOCK;
/* check arguments */
- ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT);
+ ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT, true);
if (ret)
return ret;
@@ -506,7 +510,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b
return -EDEADLOCK;
/* check arguments */
- ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
+ ret = __hid_bpf_hw_check_params(ctx, buf, &size, type, false);
if (ret)
return ret;
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/3] selftests/hid: add unnumbered variant to the hid_bpf tests
2026-09-04 12:52 [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation Benjamin Tissoires
2026-09-04 12:52 ` [PATCH 1/3] selftests/hid: add define for commonly used buf size Benjamin Tissoires
2026-09-04 12:53 ` [PATCH 2/3] HID: bpf: fix __hid_bpf_hw_check_params report length Benjamin Tissoires
@ 2026-09-04 12:53 ` Benjamin Tissoires
2026-09-14 16:07 ` [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation Benjamin Tissoires
3 siblings, 0 replies; 5+ messages in thread
From: Benjamin Tissoires @ 2026-09-04 12:53 UTC (permalink / raw)
To: Jiri Kosina, Shuah Khan
Cc: linux-input, linux-kselftest, linux-kernel, Benjamin Tissoires
A bug appeared in hid_bpf_dispatch.c where it wasn't properly handling
unnumbered reports. Add a device variant without report IDs so we can
also test them.
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
tools/testing/selftests/hid/hid_bpf.c | 53 ++++++++++++++++++++++++++------
tools/testing/selftests/hid/hid_common.h | 24 ++++++++++++++-
tools/testing/selftests/hid/progs/hid.c | 2 +-
3 files changed, 68 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index e865ee55b4f1..d1bd07caf991 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -54,11 +54,27 @@ FIXTURE_TEARDOWN(hid_bpf) {
hid_bpf_teardown(_metadata, self, variant); \
} while (0)
+FIXTURE_VARIANT(hid_bpf) {
+ __u8 *rdesc;
+ size_t rdesc_size;
+};
+
+FIXTURE_VARIANT_ADD(hid_bpf, numbered) {
+ .rdesc = rdesc,
+ .rdesc_size = sizeof(rdesc),
+};
+
+FIXTURE_VARIANT_ADD(hid_bpf, unnumbered) {
+ .rdesc = fido2_rdesc,
+ .rdesc_size = sizeof(fido2_rdesc),
+};
+
FIXTURE_SETUP(hid_bpf)
{
int err;
- err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36, rdesc, sizeof(rdesc));
+ err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36,
+ variant->rdesc, variant->rdesc_size);
ASSERT_OK(err);
}
@@ -402,8 +418,11 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */
- args.data[1] = 2; /* report ID */
- args.data[2] = 42; /* report ID */
+ args.data[1] = 2;
+ args.data[2] = 42;
+
+ if (variant->rdesc == fido2_rdesc)
+ args.data[0] = 0;
prog_fd = bpf_program__fd(self->skel->progs.hid_user_input_report);
@@ -421,8 +440,13 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
/* read the data from hidraw */
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
- ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
- ASSERT_EQ(buf[0], 1);
+ if (variant->rdesc == rdesc) {
+ ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
+ } else {
+ ASSERT_EQ(err, 64)
+ TH_LOG("read_hidraw");
+ }
+ ASSERT_EQ(buf[0], args.data[0]);
ASSERT_EQ(buf[1], 2);
ASSERT_EQ(buf[2], 42);
}
@@ -448,8 +472,11 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */
- args.data[1] = 2; /* report ID */
- args.data[2] = 42; /* report ID */
+ args.data[1] = 2;
+ args.data[2] = 42;
+
+ if (variant->rdesc == fido2_rdesc)
+ args.data[0] = 0;
prog_fd = bpf_program__fd(self->skel->progs.hid_user_output_report);
@@ -465,9 +492,14 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
ASSERT_OK(cond_err) TH_LOG("error while calling waiting for the condition");
- ASSERT_EQ(args.retval, 3);
+ if (variant->rdesc == rdesc) {
+ ASSERT_EQ(args.retval, 3);
+ } else if (variant->rdesc == fido2_rdesc) {
+ ASSERT_EQ(args.retval, 65)
+ TH_LOG("report size error, should have 64 + 1 extra byte for the report ID 0");
+ }
- ASSERT_EQ(output_report[0], 1);
+ ASSERT_EQ(output_report[0], args.data[0]);
ASSERT_EQ(output_report[1], 2);
ASSERT_EQ(output_report[2], 42);
@@ -879,6 +911,9 @@ TEST_F(hid_bpf, test_rdesc_fixup)
};
int err, desc_size;
+ if (variant->rdesc != rdesc)
+ SKIP(return, "not compatible report descriptor");
+
LOAD_PROGRAMS(progs);
/* check that hid_rdesc_fixup() was executed */
diff --git a/tools/testing/selftests/hid/hid_common.h b/tools/testing/selftests/hid/hid_common.h
index 4567336f131d..b7890ba2878f 100644
--- a/tools/testing/selftests/hid/hid_common.h
+++ b/tools/testing/selftests/hid/hid_common.h
@@ -13,7 +13,7 @@
#include <linux/uhid.h>
#define SHOW_UHID_DEBUG 0
-#define MAX_BUF_SIZE 10
+#define MAX_BUF_SIZE 128
#define min(a, b) \
({ __typeof__(a) _a = (a); \
@@ -98,6 +98,28 @@ static unsigned char rdesc[] = {
static __u8 feature_data[] = { 1, 2 };
+static __maybe_unused unsigned char fido2_rdesc[] = {
+ 0x06, 0xd0, 0xf1, /* Usage Page (FIDO Alliance) */
+ 0x09, 0x01, /* Usage (U2F Authenticator Device) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x20, /* Usage (Input Report Data) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x40, /* Report Count (64) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x21, /* Usage (Output Report Data) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x40, /* Report Count (64) */
+ 0x91, 0x02, /* Output (Data,Var,Abs) */
+ 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
+ 0x09, 0x22, /* Usage (Vendor Usage 0x22) */
+ 0xb1, 0x02, /* Feature (Data,Var,Abs) */
+ 0xc0, /* End Collection */
+};
+
#define ASSERT_OK(data) ASSERT_FALSE(data)
#define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr)
diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c
index b21fbb13c926..605d8a5f9cb2 100644
--- a/tools/testing/selftests/hid/progs/hid.c
+++ b/tools/testing/selftests/hid/progs/hid.c
@@ -98,7 +98,7 @@ struct hid_bpf_ops change_report_id = {
struct hid_hw_request_syscall_args {
/* data needs to come at offset 0 so we can use it in calls */
- __u8 data[10];
+ __u8 data[128];
unsigned int hid;
int retval;
size_t size;
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation
2026-09-04 12:52 [PATCH 0/3] HID: bpf: fix __hid_bpf_hw_check_params report length computation Benjamin Tissoires
` (2 preceding siblings ...)
2026-09-04 12:53 ` [PATCH 3/3] selftests/hid: add unnumbered variant to the hid_bpf tests Benjamin Tissoires
@ 2026-09-14 16:07 ` Benjamin Tissoires
3 siblings, 0 replies; 5+ messages in thread
From: Benjamin Tissoires @ 2026-09-14 16:07 UTC (permalink / raw)
To: Jiri Kosina, Shuah Khan, Benjamin Tissoires
Cc: linux-input, linux-kselftest, linux-kernel
On Fri, 04 Sep 2026 14:52:58 +0200, Benjamin Tissoires wrote:
> While playing with hid_bpf_hw_output_report() on a device without report
> IDs, I realized I wasn't able to send proper reports to the USB
> endpoint.
>
> This is because the __hid_bpf_hw_check_params() check wasn't taking into
> account the report ID reserved space, and it was trimming down one
> element in the report.
>
> [...]
Applied to https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git (for-7.3/upstream-fixes), thanks!
[1/3] selftests/hid: add define for commonly used buf size
https://git.kernel.org/hid/hid/c/9d1e523b92d8
[2/3] HID: bpf: fix __hid_bpf_hw_check_params report length
https://git.kernel.org/hid/hid/c/c4afa4862b87
[3/3] selftests/hid: add unnumbered variant to the hid_bpf tests
https://git.kernel.org/hid/hid/c/b6f69097c827
Cheers,
--
Benjamin Tissoires <bentiss@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread