From patchwork Wed Jun 26 13:46:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712949 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9067A18412F; Wed, 26 Jun 2024 13:46:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409607; cv=none; b=FVGehEvVshVK/uy+D+qk8hIed+3H0hOPA3JoJzCSLoQvOe7o9U2HXy9/s5PbS+WRmpYejkdtFiNmS8+udfis6yCmYRUmEVCbTlAchR2Pg4f/R9DhrxOtPk0ZzQCMg+j7+5DfMC8A947/78ArinpYG+zwQ2xZk8mXA9NFQBfBS7Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409607; c=relaxed/simple; bh=GtU69AclojvTXAELtzrkbmBbNsYZg/7S9LDXSSiB/1k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kSQl4XF14aLbz/RrrWjGW+v6LtpYB59Ni2XS0yhb2pqLZdT6tp5zI1lMk56yAM5B0aIoaUiWRzOpEn7ysMNQgG605rwcLXHZrOI+Pb5WecjHHSfjY/mVrdXgg7aWMDsHN66QHPJlR+VAW0yVcUadscjVLy5t13Wu4cLj8ABiXCs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qp4VFuwu; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qp4VFuwu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1C897C4AF0A; Wed, 26 Jun 2024 13:46:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409607; bh=GtU69AclojvTXAELtzrkbmBbNsYZg/7S9LDXSSiB/1k=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qp4VFuwuqQaoyDzGEp8H4cMEdE/DyvHDmXRCYpnYMKFAARwNxG1H5T/1Pj1NGH/Ec oLDbDyNEGULy5qAvsT34TXfQ6taL/nyL119KOoBcJNsgj7IgkVM5IFmjAGu+mQnqM3 KmfJwdyHUVNmR71fEHIDj/5G+F4wrGetdyknc/V/l4IzGlkwZmUXBGSs40s81Rdght SJpGYjsrINsUGC2yU4dk4tcKpXsuczU0PljrxIqRUx8CF1DDrlfh0CsPrNdQkTdEIy o3lz8jOO9rm8VqwE2EhJP9WtZ0/ICXlOvs4Fgij6GnbLqV8/mgVrZDEA9UMs9XNxnP loX6M31R0o7AA== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:22 +0200 Subject: [PATCH HID v2 01/13] HID: bpf: fix dispatch_hid_bpf_device_event uninitialized ret value Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-1-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires , Dan Carpenter X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=1348; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=GtU69AclojvTXAELtzrkbmBbNsYZg/7S9LDXSSiB/1k=; b=UlCY4s8u+cxp33zBsPAQOuYqWQ0xbzGfujIKMNFxsFyCRVxN9Prsc5k487uOwZtlTQZBv2XnV hvRRZQtklceBKKbswmEGnsMv14WUwM5RfBSXuXIn/PkZt9vH1uqDdFz X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Looks like if a bpf program gets inserted and then removed, hdev->bpf.device_data is then allocated, but the loop iterating over the bpf program is never assigning ret. This is a problem and also revealed another bug in which only the last value of ret was checked. This effectively meant than only the last program in the chain could change the size of the incoming buffer. Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/00f7b624-219f-4a05-a7ad-5335f15a41c7@moroto.mountain Fixes: 4a86220e046d ("HID: bpf: remove tracing HID-BPF capability") Signed-off-by: Benjamin Tissoires --- no changes in v2 --- drivers/hid/bpf/hid_bpf_dispatch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 06cc628e7bb4..b7b11a7c69db 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -57,11 +57,12 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type } if (ret) - ctx_kern.ctx.retval = ret; + ctx_kern.ctx.size = ret; } } rcu_read_unlock(); + ret = ctx_kern.ctx.size; if (ret) { if (ret > ctx_kern.ctx.allocated_size) return ERR_PTR(-EINVAL); From patchwork Wed Jun 26 13:46:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712950 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B35614532F; Wed, 26 Jun 2024 13:46:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409609; cv=none; b=bnkx5Io6LmW9OGmIZsMgPcR8lZoHvUOFoTbi3wxM8Rn2NqH0YKUppF+RyoUD4bf/wa+GHNIxEmZqEuS4MU9x84MBjTS8AB8daIMgAAdTVSCQxxzKlwMxVXJEisEY0EqLX1Givt35qnm96B0QQz4+QMQ2p/wSeU8eAdpIHlrZsCE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409609; c=relaxed/simple; bh=tAsYMlo1Ovv0H5S+75rkuRY9Nw2ig/ce307eySvXa60=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aGpzNFra/IjlDFgWYhhRl2AQSmWZygDOq55kxV03mXzOmceJDfliKUxwl8fvK6e62UhqVyTfIhWW47LlEMZUaBIyY44g51PJvUWUrMJidV6OoHqi4BnD/gKkFl2oAQleqnm4rnErtvlEzubogjcuG63gNm6GjdTBpwwIj1nLaxw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=f3FFU1Xc; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="f3FFU1Xc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 96E4CC32782; Wed, 26 Jun 2024 13:46:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409609; bh=tAsYMlo1Ovv0H5S+75rkuRY9Nw2ig/ce307eySvXa60=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=f3FFU1XcFPE00VmE4pkVIEGq+CNRfcReImX2ssGtzDo0QTaWrmaF7n/9+KUpz5OYC IFh5eEJ/UUZ0p6bzOeJDcGgDhh+rsqpR3g/iKybLPyW9kTZBeGlxlhzBWQ/oxZHPP/ TuK38jmqo8mmi1LkK9VqOt9QhVJoOF2/uSNvSgcepINAseUixsqZilmpzPhciH0zVr PAO9+HQNJfyPs/JhkbXUe8NYHXvyrZAGUeW6hNgXYnSqA7Xc07t6pjOQcO889ZG5gk yL+BVkegfUcFYXpkKnR/MgpAxrEv3I9k7F5FbOBu4f0q8oPxTPCTUT82PK11C3/P/X 2pdedVBvCjr6g== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:23 +0200 Subject: [PATCH HID v2 02/13] HID: add source argument to HID low level functions Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-2-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=12470; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=tAsYMlo1Ovv0H5S+75rkuRY9Nw2ig/ce307eySvXa60=; b=99vHk1K8HHmAA6iAhZKj9V+UMcoTd6LneDJ1BK4kzWEfQqxbLE4PGKk7RWNBsEmx6wcoG+f6D 5RWKs7q8LQCBPRPpggGG5p/x+i3J/a6245sssKP+Dk6JTel4fNn/RoV X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= This allows to know who actually sent what when we process the request to the device. This will be useful for a BPF firewall program to allow or not requests coming from a dedicated hidraw node client. Signed-off-by: Benjamin Tissoires --- no changes in v2 --- drivers/hid/bpf/hid_bpf_dispatch.c | 12 ++--- drivers/hid/bpf/hid_bpf_struct_ops.c | 2 +- drivers/hid/hid-core.c | 85 ++++++++++++++++++++++-------------- drivers/hid/hidraw.c | 10 ++--- include/linux/hid.h | 6 +++ include/linux/hid_bpf.h | 16 ++++--- 6 files changed, 82 insertions(+), 49 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index b7b11a7c69db..2df31decaac3 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -24,7 +24,7 @@ EXPORT_SYMBOL(hid_ops); u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 *size, int interrupt) + u32 *size, int interrupt, u64 source) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { @@ -50,7 +50,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type rcu_read_lock(); list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { if (e->hid_device_event) { - ret = e->hid_device_event(&ctx_kern.ctx, type); + ret = e->hid_device_event(&ctx_kern.ctx, type, source); if (ret < 0) { rcu_read_unlock(); return ERR_PTR(ret); @@ -359,7 +359,8 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, dma_data, size, rtype, - reqtype); + reqtype, + (__u64)ctx); if (ret > 0) memcpy(buf, dma_data, ret); @@ -398,7 +399,8 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) ret = hid_ops->hid_hw_output_report(hdev, dma_data, - size); + size, + (__u64)ctx); kfree(dma_data); return ret; @@ -429,7 +431,7 @@ hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf hdev = (struct hid_device *)ctx->hid; /* discard const */ - return hid_ops->hid_input_report(hdev, type, buf, size, 0); + return hid_ops->hid_input_report(hdev, type, buf, size, 0, (__u64)ctx); } __bpf_kfunc_end_defs(); diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 5f200557ff12..8063db1c8d62 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -257,7 +257,7 @@ static void hid_bpf_unreg(void *kdata) hid_put_device(hdev); } -static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type) +static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type, __u64 source) { return 0; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index aed8850a4d01..0775a32f5272 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2025,19 +2025,9 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * } EXPORT_SYMBOL_GPL(hid_report_raw_event); -/** - * hid_input_report - report data from lower layer (usb, bt...) - * - * @hid: hid device - * @type: HID report type (HID_*_REPORT) - * @data: report contents - * @size: size of data parameter - * @interrupt: distinguish between interrupt and control transfers - * - * This is data entry for lower layers. - */ -int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, - int interrupt) + +static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + u8 *data, u32 size, int interrupt, u64 source) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -2057,7 +2047,7 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data report_enum = hid->report_enum + type; hdrv = hid->driver; - data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt); + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source); if (IS_ERR(data)) { ret = PTR_ERR(data); goto unlock; @@ -2092,6 +2082,23 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data up(&hid->driver_input_lock); return ret; } + +/** + * hid_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @size: size of data parameter + * @interrupt: distinguish between interrupt and control transfers + * + * This is data entry for lower layers. + */ +int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) +{ + return __hid_input_report(hid, type, data, size, interrupt, 0); +} EXPORT_SYMBOL_GPL(hid_input_report); bool hid_match_one_id(const struct hid_device *hdev, @@ -2392,6 +2399,24 @@ void hid_hw_request(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(hid_hw_request); +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, + __u64 source) +{ + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + + if (hdev->ll_driver->max_buffer_size) + max_buffer_size = hdev->ll_driver->max_buffer_size; + + if (len < 1 || len > max_buffer_size || !buf) + return -EINVAL; + + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, + rtype, reqtype); +} + /** * hid_hw_raw_request - send report request to device * @@ -2409,6 +2434,12 @@ EXPORT_SYMBOL_GPL(hid_hw_request); 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) +{ + return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0); +} +EXPORT_SYMBOL_GPL(hid_hw_raw_request); + +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source) { unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; @@ -2418,10 +2449,11 @@ int hid_hw_raw_request(struct hid_device *hdev, if (len < 1 || len > max_buffer_size || !buf) return -EINVAL; - return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, - rtype, reqtype); + if (hdev->ll_driver->output_report) + return hdev->ll_driver->output_report(hdev, buf, len); + + return -ENOSYS; } -EXPORT_SYMBOL_GPL(hid_hw_raw_request); /** * hid_hw_output_report - send output report to device @@ -2434,18 +2466,7 @@ EXPORT_SYMBOL_GPL(hid_hw_raw_request); */ int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) { - unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; - - if (hdev->ll_driver->max_buffer_size) - max_buffer_size = hdev->ll_driver->max_buffer_size; - - if (len < 1 || len > max_buffer_size || !buf) - return -EINVAL; - - if (hdev->ll_driver->output_report) - return hdev->ll_driver->output_report(hdev, buf, len); - - return -ENOSYS; + return __hid_hw_output_report(hdev, buf, len, 0); } EXPORT_SYMBOL_GPL(hid_hw_output_report); @@ -2972,9 +2993,9 @@ EXPORT_SYMBOL_GPL(hid_check_keys_pressed); #ifdef CONFIG_HID_BPF static struct hid_ops __hid_ops = { .hid_get_report = hid_get_report, - .hid_hw_raw_request = hid_hw_raw_request, - .hid_hw_output_report = hid_hw_output_report, - .hid_input_report = hid_input_report, + .hid_hw_raw_request = __hid_hw_raw_request, + .hid_hw_output_report = __hid_hw_output_report, + .hid_input_report = __hid_input_report, .owner = THIS_MODULE, .bus_type = &hid_bus_type, }; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 2bc762d31ac7..6d2a6d38e42a 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -140,7 +140,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, if ((report_type == HID_OUTPUT_REPORT) && !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { - ret = hid_hw_output_report(dev, buf, count); + ret = __hid_hw_output_report(dev, buf, count, (__u64)file); /* * compatibility with old implementation of USB-HID and I2C-HID: * if the device does not support receiving output reports, @@ -150,8 +150,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, goto out_free; } - ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, - HID_REQ_SET_REPORT); + ret = __hid_hw_raw_request(dev, buf[0], buf, count, report_type, + HID_REQ_SET_REPORT, (__u64)file); out_free: kfree(buf); @@ -227,8 +227,8 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t goto out_free; } - ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, - HID_REQ_GET_REPORT); + ret = __hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT, (__u64)file); if (ret < 0) goto out_free; diff --git a/include/linux/hid.h b/include/linux/hid.h index 8e06d89698e6..dac2804b4562 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1125,6 +1125,12 @@ int __must_check hid_hw_open(struct hid_device *hdev); void hid_hw_close(struct hid_device *hdev); void hid_hw_request(struct hid_device *hdev, struct hid_report *report, enum hid_class_request reqtype); +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, + __u64 source); +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source); int hid_hw_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, enum hid_report_type rtype, diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 65d7e0acc8c2..a54741db0415 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -66,10 +66,12 @@ struct hid_ops { 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); - int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len); + enum hid_class_request reqtype, + __u64 source); + int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, + __u64 source); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt); + u8 *data, u32 size, int interrupt, u64 source); struct module *owner; const struct bus_type *bus_type; }; @@ -110,7 +112,8 @@ struct hid_bpf_ops { * * Context: Interrupt context. */ - int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type); + int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type, + __u64 source); /** * @hid_rdesc_fixup: called when the probe function parses the report descriptor @@ -146,7 +149,7 @@ struct hid_bpf { #ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, - u32 *size, int interrupt); + u32 *size, int interrupt, u64 source); 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); @@ -154,7 +157,8 @@ 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 data; } + u8 *data, u32 *size, int interrupt, + u64 source) { return data; } 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) {} From patchwork Wed Jun 26 13:46:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712951 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EACB2186284; Wed, 26 Jun 2024 13:46:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409612; cv=none; b=Tp++CmDr2qr+vIveRozu8W4ufC2rONq+wxTJszCZikzoEzryDAzvWhvYsftXakD5AFaf/bOfJivxNzOjHlGsSN8ekxjxsWx5EEf8xA6J+UEXIEwQAT6lTneRGo31v+bXk/NL+8/i1v4HswFF7RXFH2HxbXg4IOwW/CHJt+8NqSE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409612; c=relaxed/simple; bh=hLxv1tQ4ND5CQTyY2sweAPBolNhSrfyuhdyzuv4wmEQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bmdVID6RKLA75IIk3dKQ7fpIxZKpltnuPFe2/YMt78MWXI/F0DqL3J1YfYh9ujSD7wmTrqa6qUhh+RwuNFwqQl+3shUVgewaRmogGVDgydW0tSBMJ/cGF2cMe/eONe1OQy1zeAUqJGP6rUxI1H0SH1zDNp82VnLeWstRSDv403Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=E3fBmUvQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="E3fBmUvQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E582EC32782; Wed, 26 Jun 2024 13:46:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409611; bh=hLxv1tQ4ND5CQTyY2sweAPBolNhSrfyuhdyzuv4wmEQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=E3fBmUvQVBBtJnrFsB8vp3Wx0IQLJ/rpj/nd0NfLfHdPAQS69pBNmecEA4ownvMJ0 KKOl21La9grVtvaJ3l7kf6YHSKc3Et7bgc34qRW6ct08qFzV+MrI2OkK7z8D72rsou GczODF2wC7M04Lk093R6U2ncJilKTpTuDyJzS/s3EvGpO9JLBBAIthcwy+ir1fTULw lPT4ULKcMj8G5cnD4g9EEJ7jU2prjlW2lghncGJxGpLK/dkmhPkt5PAIquyZayDOyY S3ALJv619EXJfNLrzyUexE0Ny+hqk5uCFM0qVU/FpBQA+2koAuEftI+Jf4HnWKYPmi /Nq+Y+PE70GEQ== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:24 +0200 Subject: [PATCH HID v2 03/13] HID: bpf: protect HID-BPF prog_list access by a SRCU Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-3-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=4479; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=hLxv1tQ4ND5CQTyY2sweAPBolNhSrfyuhdyzuv4wmEQ=; b=euocECF82/VA2Yc9x6TuL1sM4rve+X1wAuPOCXC7da15J48DHtEaZKST0aTxrBeZDrc99mjAk u3xTD//3BTrBKmVBwF7DR3V8NHnR8gYVaCb4mc0Zns1ol3tglsSnXjh X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= We want to add sleepable callbacks for hid_hw_raw_request() and hid_hw_output_report(), but we can not use a plain RCU for those. Prepare for a SRCU so we can extend HID-BPF. This changes a little bit how hid_bpf_device_init() behaves, as it may now fail, so there is a tiny hid-core.c change to accommodate for this. Signed-off-by: Benjamin Tissoires --- new in v2 --- drivers/hid/bpf/hid_bpf_dispatch.c | 6 +++++- drivers/hid/bpf/hid_bpf_struct_ops.c | 2 ++ drivers/hid/hid-core.c | 8 +++++++- include/linux/hid_bpf.h | 6 ++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 2df31decaac3..c026248e3d73 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -506,13 +506,17 @@ void hid_bpf_destroy_device(struct hid_device *hdev) hdev->bpf.destroyed = true; __hid_bpf_ops_destroy_device(hdev); + + synchronize_srcu(&hdev->bpf.srcu); + cleanup_srcu_struct(&hdev->bpf.srcu); } EXPORT_SYMBOL_GPL(hid_bpf_destroy_device); -void hid_bpf_device_init(struct hid_device *hdev) +int hid_bpf_device_init(struct hid_device *hdev) { INIT_LIST_HEAD(&hdev->bpf.prog_list); mutex_init(&hdev->bpf.prog_list_lock); + return init_srcu_struct(&hdev->bpf.srcu); } EXPORT_SYMBOL_GPL(hid_bpf_device_init); diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 8063db1c8d62..d34731a1b457 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -214,6 +214,7 @@ static int hid_bpf_reg(void *kdata) list_add_rcu(&ops->list, &hdev->bpf.prog_list); else list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list); + synchronize_srcu(&hdev->bpf.srcu); out_unlock: mutex_unlock(&hdev->bpf.prog_list_lock); @@ -244,6 +245,7 @@ static void hid_bpf_unreg(void *kdata) mutex_lock(&hdev->bpf.prog_list_lock); list_del_rcu(&ops->list); + synchronize_srcu(&hdev->bpf.srcu); reconnect = hdev->bpf.rdesc_ops == ops; if (reconnect) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 0775a32f5272..ad08289752da 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2875,9 +2875,15 @@ struct hid_device *hid_allocate_device(void) mutex_init(&hdev->ll_open_lock); kref_init(&hdev->ref); - hid_bpf_device_init(hdev); + ret = hid_bpf_device_init(hdev); + if (ret) + goto out_err; return hdev; + +out_err: + hid_destroy_device(hdev); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(hid_allocate_device); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index a54741db0415..f93845de5cac 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -5,6 +5,7 @@ #include #include +#include #include struct hid_device; @@ -145,6 +146,7 @@ struct hid_bpf { struct hid_bpf_ops *rdesc_ops; struct list_head prog_list; struct mutex prog_list_lock; /* protects prog_list update */ + struct srcu_struct srcu; /* protects prog_list read-only access */ }; #ifdef CONFIG_HID_BPF @@ -153,7 +155,7 @@ u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type t 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); +int 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, @@ -162,7 +164,7 @@ static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid 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 int hid_bpf_device_init(struct hid_device *hid) { return 0; } #define call_hid_bpf_rdesc_fixup(_hdev, _rdesc, _size) \ ((u8 *)kmemdup(_rdesc, *(_size), GFP_KERNEL)) From patchwork Wed Jun 26 13:46:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712952 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 89D67186E39; Wed, 26 Jun 2024 13:46:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409614; cv=none; b=qSbQHyPwqtGELt7ZR5eD2p9Z9YD64X5vpCDiDveXVwa5C/Tih4itFO0wo90E9LF0PNVE5Vl2YPm7PBMD0j3O+fSztxGsKFaf+bCDnqfT5gr72VD3Z/KE8rJ5pdDW1wkxyx5f3yqA+z0kDhwVI0DchIU0tvAfY7ougNtVAcb11uo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409614; c=relaxed/simple; bh=B5RcoIbutWmEtYXbPfsY1oRwm9bGSb4lgVEA9mGXIHs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=O7a5z7ZhqNLFmSW9PvCHpZcPPy59J8RLDvyEHjdLvoNV0RoTJgAvXhBhNMalUCoMGW1dqBcjHwg9CKW/ZI1RmW4z0FTYWkL9HTNNbQ8a9+lzk4Jj5eb+A0kj4U116YB1X+zXPCXu4jYAFd6HPXuSpbRAcW33MO35fEOTGAO+5FQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YupSo94w; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YupSo94w" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3FFB4C4AF0A; Wed, 26 Jun 2024 13:46:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409614; bh=B5RcoIbutWmEtYXbPfsY1oRwm9bGSb4lgVEA9mGXIHs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=YupSo94w9dMTCVttRtG+nZkRrPO6C77Je1EZLD6PByg7H6+YP0oEpSwmVeAclvrdh OECU3Ow2vCqk6l/QGrd7NUWA035AvzSzPfiJrpk2r2Ghx6OJAgznJLiYXxSwazNrQm bqfmVeGOxa5jrclVd90ZflMNNMhJAdw49hUZMcuHND1OsSognN+/KeptcccbWN38AU OT8+Zuv4Awtlvnlck/6W3kjCAcrMPHqATOh4upQwSjh+0ytgenQJB2fpMHwZPLsJLa zOpEKi4ROg5fds9u3/1w0SfpCis935WxTEeIWh6VQkQpL0TIJHH9Cymbk0L5L6VC63 DRkL9cLdoaURA== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:25 +0200 Subject: [PATCH HID v2 04/13] HID: bpf: add HID-BPF hooks for hid_hw_raw_requests Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-4-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=6531; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=B5RcoIbutWmEtYXbPfsY1oRwm9bGSb4lgVEA9mGXIHs=; b=ap86IjvdzdO19bJIsBM5btV8JBPEV8eM2lIZmsCe0aR9wqgGDLxjjZ9MI5CLwBFW4XQ8M8Um2 hEpPQo2xrkoAG/eWRRopfr01NCYxNjH1D7t3WiMrM+Nm3QB4TIJsohZ X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= This allows to intercept and prevent or change the behavior of hid_hw_raw_request() from a bpf program. The intent is to solve a couple of use case: - firewalling a HID device: a firewall can monitor who opens the hidraw nodes and then prevent or allow access to write operations on that hidraw node. - change the behavior of a device and emulate a new HID feature request The hook is allowed to be run as sleepable so it can itself call hid_bpf_hw_request(), which allows to "convert" one feature request into another or even call the feature request on a different HID device on the same physical device. Signed-off-by: Benjamin Tissoires --- changes in v2: - make use of SRCU --- drivers/hid/bpf/hid_bpf_dispatch.c | 37 ++++++++++++++++++++++++++++++++++++ drivers/hid/bpf/hid_bpf_struct_ops.c | 1 + drivers/hid/hid-core.c | 6 ++++++ include/linux/hid_bpf.h | 35 ++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index c026248e3d73..ac98bab4c96d 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -74,6 +74,43 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); +int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, + unsigned char reportnum, u8 *buf, + u32 size, enum hid_report_type rtype, + enum hid_class_request reqtype, + u64 source) +{ + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .allocated_size = size, + .size = size, + }, + .data = buf, + }; + struct hid_bpf_ops *e; + int ret, idx; + + if (rtype >= HID_REPORT_TYPES) + return -EINVAL; + + idx = srcu_read_lock(&hdev->bpf.srcu); + list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list, + srcu_read_lock_held(&hdev->bpf.srcu)) { + if (e->hid_hw_request) { + ret = e->hid_hw_request(&ctx_kern.ctx, reportnum, rtype, reqtype, source); + if (ret) + goto out; + } + } + ret = 0; + +out: + srcu_read_unlock(&hdev->bpf.srcu, idx); + return ret; +} +EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests); + u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) { int ret; diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index d34731a1b457..a540a4417174 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -44,6 +44,7 @@ static int hid_bpf_ops_check_member(const struct btf_type *t, switch (moff) { case offsetof(struct hid_bpf_ops, hid_rdesc_fixup): + case offsetof(struct hid_bpf_ops, hid_hw_request): break; default: if (prog->sleepable) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ad08289752da..16731804c6bd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2406,6 +2406,7 @@ int __hid_hw_raw_request(struct hid_device *hdev, __u64 source) { unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; if (hdev->ll_driver->max_buffer_size) max_buffer_size = hdev->ll_driver->max_buffer_size; @@ -2413,6 +2414,11 @@ int __hid_hw_raw_request(struct hid_device *hdev, if (len < 1 || len > max_buffer_size || !buf) return -EINVAL; + ret = dispatch_hid_bpf_raw_requests(hdev, reportnum, buf, len, rtype, + reqtype, source); + if (ret) + return ret; + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, rtype, reqtype); } diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index f93845de5cac..3c01f7f8b6fc 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -130,6 +130,31 @@ struct hid_bpf_ops { */ int (*hid_rdesc_fixup)(struct hid_bpf_ctx *ctx); + /** + * @hid_hw_request: called whenever a hid_hw_raw_request() call is emitted + * on the HID device + * + * It has the following arguments: + * + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx + * ``reportnum``: the report number, as in hid_hw_raw_request() + * ``rtype``: the report type (``HID_INPUT_REPORT``, ``HID_FEATURE_REPORT``, + * ``HID_OUTPUT_REPORT``) + * ``reqtype``: the request + * ``source``: a u64 referring to a uniq but identifiable source. If %0, the + * kernel itself emitted that call. For hidraw, ``source`` is set + * to the associated ``struct file *``. + * + * Return: %0 to keep processing the request by hid-core; any other value + * stops hid-core from processing that event. A positive value should be + * returned with the number of bytes returned in the incoming buffer; a + * negative error code interrupts the processing of this call. + */ + int (*hid_hw_request)(struct hid_bpf_ctx *ctx, unsigned char reportnum, + enum hid_report_type rtype, enum hid_class_request reqtype, + __u64 source); + + /* private: do not show up in the docs */ struct hid_device *hdev; }; @@ -152,6 +177,11 @@ struct hid_bpf { #ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 *size, int interrupt, u64 source); +int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + u32 size, enum hid_report_type rtype, + enum hid_class_request reqtype, + __u64 source); 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); @@ -161,6 +191,11 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 *size, int interrupt, u64 source) { return data; } +static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, + unsigned char reportnum, u8 *buf, + u32 size, enum hid_report_type rtype, + enum hid_class_request reqtype, + u64 source) { return 0; } 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) {} From patchwork Wed Jun 26 13:46:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712953 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 925F4187324; Wed, 26 Jun 2024 13:46:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409616; cv=none; b=rYzdgDlRWAgdZGS8gtzySU9FBkc0xF/h9laSsUbB6BxlaNSEEUligzco7wAerVvyTBBP73ZOG2HXGzYjXf85dXUGAXQRzGOsrKYr/vA4ePqOWRnjaYPDezxnaH/Bj1hI//wruY7LrQ6xyZg9dOKXy1FExANPzNBE1PxitmVvx4c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409616; c=relaxed/simple; bh=zgFjsVKA5GCyGoEitRy4IKTzbG1geJgnmIDInaqxiwA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=UGPLrr4l1kCrleQePX3IWLtWobMsXhwRfQP8inenDddAiFKqbVtdAhBf2TEaKVf1GeqVzhMYaYYAfdw18pmtUtJA3QZnlLU02kBh2yvakV5AN3nWtpqaEPRlkDtkF0n0ySvPh2wHOWs75Y7bQnI7HdVFcg551ufL4XHygbW4T4E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pZGmq9Mx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pZGmq9Mx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8FD9DC2BD10; Wed, 26 Jun 2024 13:46:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409616; bh=zgFjsVKA5GCyGoEitRy4IKTzbG1geJgnmIDInaqxiwA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=pZGmq9MxSp//BrMsO3SvdWCBgF2LU1KzOG3DmoVYd6qkaH8A6yN9M5jzZYle+IoiS v4dawjJp3o9LRnph/A9LDrZM4GAotJ2Pa1FGBUGtFtVCbSaAjNleYt6WUqNZPYHHSx AhiuqIoIKbEaBaqn4JfyOtmD05W0m8v1EJb+DZcVRKgdEKRuFY5nOsZv0wts0NWgaW RumD03rNnoj2OJnIXDOou5Tpcgvyki/7FDwFB9r2NKD22dlMpEnFlZZpHD8OHScLU5 5MfCaAkLzmf72JJpvzW+6zNDZo/kgLLHZsi2SBGcm6aoWMWFlkdB60aESB5WB+H6KO X/HZ+S2L6HE0w== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:26 +0200 Subject: [PATCH HID v2 05/13] HID: bpf: prevent infinite recursions with hid_hw_raw_requests hooks Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-5-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=7314; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=zgFjsVKA5GCyGoEitRy4IKTzbG1geJgnmIDInaqxiwA=; b=2pDwmgR+gEDtEi6vh1vAp+UQcwTa3hCKUMFmn4nv3WbmPMLmBJCGwXdo37WdCpy/ALpV0p+h4 yd4rKNgBpsoCJ0NlLOstE1s0tc7aigs/OiUgrJd4z3COU8NmTlEdfG/ X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= When we attach a sleepable hook to hid_hw_raw_requests, we can (and in many cases should) call ourself hid_bpf_raw_request(), to actually fetch data from the device itself. However, this means that we might enter an infinite loop between hid_hw_raw_requests hooks and hid_bpf_hw_request() call. To prevent that, if a hid_bpf_hw_request() call is emitted, we prevent any new call of this kfunc by storing the information in the context. This way we can always trace/monitor/filter the incoming bpf requests, while preventing those loops to happen. I don't think exposing "from_bpf" is very interesting because while writing such a bpf program, you need to match at least the report number and/or the source of the call. So a blind "if there is a hid_hw_raw_request() call, I'm emitting another one" makes no real sense. Signed-off-by: Benjamin Tissoires --- no changes in v2 --- drivers/hid/bpf/hid_bpf_dispatch.c | 12 ++++++++++-- drivers/hid/bpf/hid_bpf_dispatch.h | 1 + drivers/hid/hid-core.c | 6 +++--- drivers/hid/hidraw.c | 4 ++-- include/linux/hid.h | 2 +- include/linux/hid_bpf.h | 6 +++--- 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index ac98bab4c96d..80c6787195f7 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -78,7 +78,7 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, u8 *buf, u32 size, enum hid_report_type rtype, enum hid_class_request reqtype, - u64 source) + u64 source, bool from_bpf) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { @@ -87,6 +87,7 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, .size = size, }, .data = buf, + .from_bpf = from_bpf, }; struct hid_bpf_ops *e; int ret, idx; @@ -363,11 +364,17 @@ __bpf_kfunc 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_kern *ctx_kern; struct hid_device *hdev; size_t size = buf__sz; u8 *dma_data; int ret; + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + + if (ctx_kern->from_bpf) + return -EDEADLOCK; + /* check arguments */ ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype); if (ret) @@ -397,7 +404,8 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, size, rtype, reqtype, - (__u64)ctx); + (__u64)ctx, + true); /* prevent infinite recursions */ if (ret > 0) memcpy(buf, dma_data, ret); diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h index 835e6f69f479..44c6ea22233f 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.h +++ b/drivers/hid/bpf/hid_bpf_dispatch.h @@ -8,6 +8,7 @@ struct hid_bpf_ctx_kern { struct hid_bpf_ctx ctx; u8 *data; + bool from_bpf; }; struct hid_device *hid_get_device(unsigned int hid_id); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 16731804c6bd..2038ba08eaa1 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2403,7 +2403,7 @@ 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, - __u64 source) + __u64 source, bool from_bpf) { unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; int ret; @@ -2415,7 +2415,7 @@ int __hid_hw_raw_request(struct hid_device *hdev, return -EINVAL; ret = dispatch_hid_bpf_raw_requests(hdev, reportnum, buf, len, rtype, - reqtype, source); + reqtype, source, from_bpf); if (ret) return ret; @@ -2441,7 +2441,7 @@ 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) { - return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0); + return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0, false); } EXPORT_SYMBOL_GPL(hid_hw_raw_request); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 6d2a6d38e42a..4ba3131de614 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -151,7 +151,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, } ret = __hid_hw_raw_request(dev, buf[0], buf, count, report_type, - HID_REQ_SET_REPORT, (__u64)file); + HID_REQ_SET_REPORT, (__u64)file, false); out_free: kfree(buf); @@ -228,7 +228,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t } ret = __hid_hw_raw_request(dev, report_number, buf, count, report_type, - HID_REQ_GET_REPORT, (__u64)file); + HID_REQ_GET_REPORT, (__u64)file, false); if (ret < 0) goto out_free; diff --git a/include/linux/hid.h b/include/linux/hid.h index dac2804b4562..24d0d7c0bd33 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1129,7 +1129,7 @@ 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, - __u64 source); + __u64 source, bool from_bpf); int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source); int hid_hw_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 3c01f7f8b6fc..088c94b6d8ec 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -68,7 +68,7 @@ struct hid_ops { unsigned char reportnum, __u8 *buf, size_t len, enum hid_report_type rtype, enum hid_class_request reqtype, - __u64 source); + __u64 source, bool from_bpf); int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, @@ -181,7 +181,7 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, u32 size, enum hid_report_type rtype, enum hid_class_request reqtype, - __u64 source); + __u64 source, bool from_bpf); 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); @@ -195,7 +195,7 @@ static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, u8 *buf, u32 size, enum hid_report_type rtype, enum hid_class_request reqtype, - u64 source) { return 0; } + u64 source, bool from_bpf) { return 0; } 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) {} From patchwork Wed Jun 26 13:46:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712954 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E23BE18732F; Wed, 26 Jun 2024 13:46:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409619; cv=none; b=QN9QAIzQqkeBZn4qwMYvS+eToiyduZwqlP7yfBpf5zTj01bjtv4UQZD690kv3/a/Q0W8cAeQw0c004PxhnOSvXSPzYxFGmkoXjoiJkGJBpLcUEfLrzZwDTXm76a2n53akzbyuHxqBtUGNh5ll5f9G9BtLm8cx2GzHRUxSgdD8o0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409619; c=relaxed/simple; bh=jrGb1XosYynHCDb0fEHEmq8mfYlpvC5f5ygZfOeVxXg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oWik7kkyzD9Md7zEjW0EjW3fEIBYKcpnvWXn9K9zScAYDf61xGz4A7GdqnXnis/3rg4mDtYtNy97PZ76DGlJ8HIuUgadQosPhJzsEPd/NyQbQZuM/5xfKXhzAfF5M2Av9qf0G6/LupLwjj03q8W18sPfORa0VpG2nTaKYKCpjLU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jn6jEX7W; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jn6jEX7W" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DEF69C32782; Wed, 26 Jun 2024 13:46:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409618; bh=jrGb1XosYynHCDb0fEHEmq8mfYlpvC5f5ygZfOeVxXg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=jn6jEX7WpVVkLtLAYHU2DipG1dxYJJV6z5zBW6izMMG4kgMV6suJBuO+FeDUEOSNC AmGpg1Q8Go2e+/aRZpDQYd5ZgibJzyri/mRd1vGpn6ws40uypbKoaNmiksZlDoWBnf hDm/MCmQT0cQkxwiqp5VPUyDqotBGAFtyGrKV3ZzYVuqaMhIoz/2+v6rX1bXLMKkzt 69yq+S3p6IZLpC1Kh86CcTBTAtxdEHfNcPWpuBjTYXgy4OaZeOJ2dRAFBIhajQsWk+ vR0FGdYzdzb7e1yvCcTTmVfhmqai2zeLx90Va2ym3Vac8QL2CyX3Af1MWWue1fNsiS frSB7tevY9qiw== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:27 +0200 Subject: [PATCH HID v2 06/13] selftests/hid: add tests for hid_hw_raw_request HID-BPF hooks Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-6-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=7406; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=jrGb1XosYynHCDb0fEHEmq8mfYlpvC5f5ygZfOeVxXg=; b=hieq1TemhrcUUJNP2ybL5EDuoMHEG87jWAqEJplqsSoC5QIqotz4BrQvJRYXUTCZEUkhd3kNC RNZLqZGZWZQC1235Z/QLGKwAKjX837NsNTqPYMh/J8a//Y24nqlCSUz X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= We add 3 new tests: - first, we make sure we can prevent the raw_request to happen - second, we make sure that we can detect that a given hidraw client was actually doing the request, and for that client only, call ourself hid_bpf_hw_request(), returning a custom value - last, we ensure that we can not loop between hooks for hid_hw_raw_request() and manual calls to hid_bpf_hw_request() from that hook Signed-off-by: Benjamin Tissoires --- no changes in v2 --- tools/testing/selftests/hid/hid_bpf.c | 109 ++++++++++++++++++++++++++++++++ tools/testing/selftests/hid/progs/hid.c | 79 +++++++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 45e173db35bd..f97d56337d8a 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -470,6 +470,11 @@ static void detach_bpf(FIXTURE_DATA(hid_bpf) * self) close(self->hidraw_fd); self->hidraw_fd = 0; + if (!self->skel) + return; + + hid__detach(self->skel); + for (i = 0; i < ARRAY_SIZE(self->hid_links); i++) { if (self->hid_links[i]) bpf_link__destroy(self->hid_links[i]); @@ -575,6 +580,8 @@ static void load_programs(const struct test_program programs[], programs[i].name + 4); } + hid__attach(self->skel); + self->hidraw_fd = open_hidraw(self->dev_id); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); } @@ -919,6 +926,108 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call) ASSERT_EQ(args.data[1], 2); } +/* + * Call hid_hw_raw_request against the given uhid device, + * check that the program is called and prevents the + * call to uhid. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* first check that we did not attach to device_event */ + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + uhid_send_event(_metadata, self->uhid_fd, buf, 6); + + /* 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); + ASSERT_EQ(buf[1], 42); + ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test"); + + /* now check that our program is preventing hid_hw_raw_request() */ + + /* emit hid_hw_raw_request from hidraw */ + /* Get Feature */ + memset(buf, 0, sizeof(buf)); + buf[0] = 0x1; /* Report Number */ + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); + ASSERT_LT(err, 0) TH_LOG("unexpected success while reading HIDIOCGFEATURE: %d", err); + ASSERT_EQ(errno, 20) TH_LOG("unexpected error code while reading HIDIOCGFEATURE: %d", + errno); + + /* remove our bpf program and check that we can now emit commands */ + + /* detach the program */ + detach_bpf(self); + + self->hidraw_fd = open_hidraw(self->dev_id); + ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); + + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); + ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGFEATURE: %d", err); +} + +/* + * Call hid_hw_raw_request against the given uhid device, + * check that the program is called and can issue the call + * to uhid and transform the answer. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* emit hid_hw_raw_request from hidraw */ + /* Get Feature */ + memset(buf, 0, sizeof(buf)); + buf[0] = 0x1; /* Report Number */ + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); + ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err); + + ASSERT_EQ(buf[0], 2); + ASSERT_EQ(buf[1], 3); + ASSERT_EQ(buf[2], 4); +} + +/* + * Call hid_hw_raw_request against the given uhid device, + * check that the program is not making infinite loops. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* emit hid_hw_raw_request from hidraw */ + /* Get Feature */ + memset(buf, 0, sizeof(buf)); + buf[0] = 0x1; /* Report Number */ + err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); + ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err); +} + /* * Attach hid_insert{0,1,2} to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 2e7e5a736dc6..0ad452fcca58 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -306,3 +306,82 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_insert3 = { .hid_device_event = (void *)hid_test_insert3, }; + +SEC("?struct_ops/hid_hw_request") +int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) +{ + return -20; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_filter_raw_request = { + .hid_hw_request = (void *)hid_test_filter_raw_request, +}; + +static struct file *current_file; + +SEC("fentry/hidraw_open") +int BPF_PROG(hidraw_open, struct inode *inode, struct file *file) +{ + current_file = file; + return 0; +} + +SEC("?struct_ops.s/hid_hw_request") +int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); + int ret; + + if (!data) + return 0; /* EPERM check */ + + /* check if the incoming request comes from our hidraw operation */ + if (source == (__u64)current_file) { + data[0] = reportnum; + + ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype); + if (ret != 2) + return -1; + data[0] = reportnum + 1; + data[1] = reportnum + 2; + data[2] = reportnum + 3; + return 3; + } + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_hidraw_raw_request = { + .hid_hw_request = (void *)hid_test_hidraw_raw_request, +}; + +SEC("?struct_ops.s/hid_hw_request") +int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum, + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); + int ret; + + if (!data) + return 0; /* EPERM check */ + + /* always forward the request as-is to the device, hid-bpf should prevent + * infinite loops. + */ + data[0] = reportnum; + + ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype); + if (ret == 2) + return 3; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_infinite_loop_raw_request = { + .hid_hw_request = (void *)hid_test_infinite_loop_raw_request, +}; From patchwork Wed Jun 26 13:46:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712955 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 45DBE18413A; Wed, 26 Jun 2024 13:47:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409621; cv=none; b=RF4C4Z1zk6g6MW96lguGWuGj19fQIPx5r0Ox5bSDvhvuE/sEIL275LRS5J7EEvTaTEkWpTo+5HWUg/tyxwi4CleSUouzQGsrh1eKq3Td594IHud8dePvV4Jg066gzQ/jy1E+znIthhWhhzQ5SG7O4eC89hOOF36T8t9A0AoTuQI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409621; c=relaxed/simple; bh=/dnMGIJskf4JLzZSF124XaWtykOIOK/lC5HMHqdtbmw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=o79D5e1tj6ZjfbBlIWbcCC1kYF8KsZz/3yHKPmiHKQh0WCabXPPhrY9eG8HZz0h8+4tq85eCS9HfklQJesWB91rHNrCMlhxDsVX6X2pmOmT8tgOiY86zQwWYE8wOjSer78vMa97urO4nNbGSiQhB0F7ukvzm0tlSswfxZj4edMc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FN7FQoUC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FN7FQoUC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 399CBC32782; Wed, 26 Jun 2024 13:46:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409621; bh=/dnMGIJskf4JLzZSF124XaWtykOIOK/lC5HMHqdtbmw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=FN7FQoUCKH9Q5AlWlKUsbAXMQdfUm6nrsQ6dXpsG9dB4YlvmPj3IznFHlc4XcItd5 KmbjSlfef7jZt9NnoMeLaFgyy4KxHHY+7Kyv1mf1N+bRHnVz7mAUG143CKmlDvb9nk nyRC1eExdPZrqVEAYLTNZqDHUPrgOZbtGbWcmBjfUkJHDirDe4Y5/KEmJjHQ8aL5Fj HnT/TKol1q61OBj9kzgT9EyGQqI2m4bKreJtmrZrxuie5x3mQ7rqYYphbJ+1vIylcA I3X1qht0KJtBeR1AmiFD0Pf0p+fM3mpiCQvmIO2ic4YOfKn7idDfpe0E2MBq4zObh5 bfEKIhR01e2Qg== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:28 +0200 Subject: [PATCH HID v2 07/13] HID: bpf: add HID-BPF hooks for hid_hw_output_report Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-7-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=8779; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=/dnMGIJskf4JLzZSF124XaWtykOIOK/lC5HMHqdtbmw=; b=kXsZuXqLU2YmSFoWMhhe3049AwraRfQIA2qqlSeQPQL397Jx2GB8g/Aajklpfp9udfXuyUBWO PrIc5bXf2XtBpKrE/VjrF53zoVn7HDLmcYZUBKOlyni6EVPCgC6w57h X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Same story than hid_hw_raw_requests: This allows to intercept and prevent or change the behavior of hid_hw_output_report() from a bpf program. The intent is to solve a couple of use case: - firewalling a HID device: a firewall can monitor who opens the hidraw nodes and then prevent or allow access to write operations on that hidraw node. - change the behavior of a device and emulate a new HID feature request The hook is allowed to be run as sleepable so it can itself call hid_hw_output_report(), which allows to "convert" one feature request into another or even call the feature request on a different HID device on the same physical device. Signed-off-by: Benjamin Tissoires --- changes in v2: - make use of SRCU --- drivers/hid/bpf/hid_bpf_dispatch.c | 38 ++++++++++++++++++++++++++++++++---- drivers/hid/bpf/hid_bpf_struct_ops.c | 1 + drivers/hid/hid-core.c | 10 ++++++++-- drivers/hid/hidraw.c | 2 +- include/linux/hid.h | 3 ++- include/linux/hid_bpf.h | 24 ++++++++++++++++++++++- 6 files changed, 69 insertions(+), 9 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 80c6787195f7..49f261054418 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -112,6 +112,39 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests); +int dispatch_hid_bpf_output_report(struct hid_device *hdev, + __u8 *buf, u32 size, __u64 source, + bool from_bpf) +{ + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .allocated_size = size, + .size = size, + }, + .data = buf, + .from_bpf = from_bpf, + }; + struct hid_bpf_ops *e; + int ret, idx; + + idx = srcu_read_lock(&hdev->bpf.srcu); + list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list, + srcu_read_lock_held(&hdev->bpf.srcu)) { + if (e->hid_hw_output_report) { + ret = e->hid_hw_output_report(&ctx_kern.ctx, source); + if (ret) + goto out; + } + } + ret = 0; + +out: + srcu_read_unlock(&hdev->bpf.srcu, idx); + return ret; +} +EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report); + u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) { int ret; @@ -442,10 +475,7 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) if (!dma_data) return -ENOMEM; - ret = hid_ops->hid_hw_output_report(hdev, - dma_data, - size, - (__u64)ctx); + ret = hid_ops->hid_hw_output_report(hdev, dma_data, size, (__u64)ctx, true); kfree(dma_data); return ret; diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index a540a4417174..37d2f8e2413a 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -45,6 +45,7 @@ static int hid_bpf_ops_check_member(const struct btf_type *t, switch (moff) { case offsetof(struct hid_bpf_ops, hid_rdesc_fixup): case offsetof(struct hid_bpf_ops, hid_hw_request): + case offsetof(struct hid_bpf_ops, hid_hw_output_report): break; default: if (prog->sleepable) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2038ba08eaa1..bb6f334f05bd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2445,9 +2445,11 @@ int hid_hw_raw_request(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(hid_hw_raw_request); -int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source) +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, + bool from_bpf) { unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; if (hdev->ll_driver->max_buffer_size) max_buffer_size = hdev->ll_driver->max_buffer_size; @@ -2455,6 +2457,10 @@ int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 if (len < 1 || len > max_buffer_size || !buf) return -EINVAL; + ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf); + if (ret) + return ret; + if (hdev->ll_driver->output_report) return hdev->ll_driver->output_report(hdev, buf, len); @@ -2472,7 +2478,7 @@ int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 */ int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) { - return __hid_hw_output_report(hdev, buf, len, 0); + return __hid_hw_output_report(hdev, buf, len, 0, false); } EXPORT_SYMBOL_GPL(hid_hw_output_report); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 4ba3131de614..c2396916cdaa 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -140,7 +140,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, if ((report_type == HID_OUTPUT_REPORT) && !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { - ret = __hid_hw_output_report(dev, buf, count, (__u64)file); + ret = __hid_hw_output_report(dev, buf, count, (__u64)file, false); /* * compatibility with old implementation of USB-HID and I2C-HID: * if the device does not support receiving output reports, diff --git a/include/linux/hid.h b/include/linux/hid.h index 24d0d7c0bd33..1533c9dcd3a6 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1130,7 +1130,8 @@ int __hid_hw_raw_request(struct hid_device *hdev, size_t len, enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source, bool from_bpf); -int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source); +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, + bool from_bpf); int hid_hw_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, enum hid_report_type rtype, diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 088c94b6d8ec..f35508a73067 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -70,7 +70,7 @@ struct hid_ops { enum hid_class_request reqtype, __u64 source, bool from_bpf); int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, - __u64 source); + __u64 source, bool from_bpf); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, int interrupt, u64 source); struct module *owner; @@ -154,6 +154,24 @@ struct hid_bpf_ops { enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source); + /** + * @hid_hw_output_report: called whenever a hid_hw_output_report() call is emitted + * on the HID device + * + * It has the following arguments: + * + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx + * ``source``: a u64 referring to a uniq but identifiable source. If %0, the + * kernel itself emitted that call. For hidraw, ``source`` is set + * to the associated ``struct file *``. + * + * Return: %0 to keep processing the request by hid-core; any other value + * stops hid-core from processing that event. A positive value should be + * returned with the number of bytes written to the device; a negative error + * code interrupts the processing of this call. + */ + int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, __u64 source); + /* private: do not show up in the docs */ struct hid_device *hdev; @@ -182,6 +200,8 @@ int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, u32 size, enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source, bool from_bpf); +int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size, + __u64 source, bool from_bpf); 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); @@ -196,6 +216,8 @@ static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, u32 size, enum hid_report_type rtype, enum hid_class_request reqtype, u64 source, bool from_bpf) { return 0; } +static inline int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size, + __u64 source, bool from_bpf) { return 0; } 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) {} From patchwork Wed Jun 26 13:46:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712956 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1398187568; Wed, 26 Jun 2024 13:47:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409624; cv=none; b=Zu+pUvMLqq1cubESSu/8iFy7pqA0Nu9vPxg1W4G42sv7qOX2t18dpUtrUTxZHjsbN6GW01Yy4xw1V3eoWCTitkhuLr9sSm0lVMuVTVlSiYFEVTekbQEJEsxy0mGz+qWk4GsW9pBg21WZ8zSw354UOzS45eBGKf/yaWEdsO+4nQc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409624; c=relaxed/simple; bh=JZObegxagyPRgI3IWA8hj5O10mhsN7UzKEHnUkVEJs8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ipoWeu299u46NUJupZSja3aELK8M9rH+0+Rp/jgTM9VP7eMf30LWqskoZWj3jqsL0scvtH+Md3ET0NvvD430UlqNrWp2MN0G1UgEq9NEN0eBGQNs8ZgJPLl3V0x9YaTicnz5KxdebhQS78QC34vFAMmRP1qXnJF7TAjxjBGyv6E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RAZnXEcC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RAZnXEcC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 88FA3C32782; Wed, 26 Jun 2024 13:47:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409623; bh=JZObegxagyPRgI3IWA8hj5O10mhsN7UzKEHnUkVEJs8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=RAZnXEcCNJFwxXiqf8QqktBdhSVAOAq3ZjattCMLZM3REizJvM6OndUA2ANulyQJR n5YIjorv2QbWTDxRuASA98sE1m4i61x3zhLWqws4rzrCXR0JjygjaO24r86oCN73FB R0nBa/0Z59cfgzZ3XM0qIUPMAUHTJJRe/D1ClWKVqmGgcgj5yieEcqfP/8P/DVcqdG 7Y94ZgDK1qKQ+ihaxM38Bxzb/Lh48PpCA7b1sqLwwxide75NWLOA/9H5xbG1HT08YA je87BL6r4FcHpjQauabjANmEedBKcE387XF8Zq8d5Qp/HE47KresP0gOAbZOA/gdCl UnvRmz53rA8ig== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:29 +0200 Subject: [PATCH HID v2 08/13] selftests/hid: add tests for hid_hw_output_report HID-BPF hooks Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-8-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=6988; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=JZObegxagyPRgI3IWA8hj5O10mhsN7UzKEHnUkVEJs8=; b=BvcwpPMLAxxNBDFI90RPw8gatIQeiL+35KHX8d0UtB73rt+LFjSs6Xeuj69uGWdaU3ccScpQB XF1d0kfbggDChubQUTGt0X5wNZg6vk6qoc4effeWN2GepI9szQpqHp0 X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= We add 3 new tests: - first, we make sure we can prevent the output_report to happen - second, we make sure that we can detect that a given hidraw client was actually doing the request, and for that client only, call ourself hid_bpf_hw_output_report(), returning a custom value - last, we ensure that we can not loop between hooks for hid_hw_output_report() and manual calls to hid_bpf_hw_output_report() from that same hook Signed-off-by: Benjamin Tissoires --- no changes in v2 --- drivers/hid/bpf/hid_bpf_dispatch.c | 5 ++ tools/testing/selftests/hid/hid_bpf.c | 102 ++++++++++++++++++++++++++++++++ tools/testing/selftests/hid/progs/hid.c | 58 ++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 49f261054418..0aab49a2b869 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -459,11 +459,16 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, __bpf_kfunc int hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) { + struct hid_bpf_ctx_kern *ctx_kern; struct hid_device *hdev; size_t size = buf__sz; u8 *dma_data; int ret; + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + if (ctx_kern->from_bpf) + return -EDEADLOCK; + /* check arguments */ ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT); if (ret) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index f97d56337d8a..40aedd1d9dc5 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -1028,6 +1028,108 @@ TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call) ASSERT_EQ(err, 3) TH_LOG("unexpected returned size while reading HIDIOCGFEATURE: %d", err); } +/* + * Call hid_hw_output_report against the given uhid device, + * check that the program is called and prevents the + * call to uhid. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* first check that we did not attach to device_event */ + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + uhid_send_event(_metadata, self->uhid_fd, buf, 6); + + /* 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); + ASSERT_EQ(buf[1], 42); + ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test"); + + /* now check that our program is preventing hid_hw_output_report() */ + + buf[0] = 1; /* report ID */ + buf[1] = 2; + buf[2] = 42; + + err = write(self->hidraw_fd, buf, 3); + ASSERT_LT(err, 0) TH_LOG("unexpected success while sending hid_hw_output_report: %d", err); + ASSERT_EQ(errno, 25) TH_LOG("unexpected error code while sending hid_hw_output_report: %d", + errno); + + /* remove our bpf program and check that we can now emit commands */ + + /* detach the program */ + detach_bpf(self); + + self->hidraw_fd = open_hidraw(self->dev_id); + ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); + + err = write(self->hidraw_fd, buf, 3); + ASSERT_GE(err, 0) TH_LOG("error while sending hid_hw_output_report: %d", err); +} + +/* + * Call hid_hw_output_report against the given uhid device, + * check that the program is called and can issue the call + * to uhid and transform the answer. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* emit hid_hw_output_report from hidraw */ + buf[0] = 1; /* report ID */ + buf[1] = 2; + buf[2] = 42; + + err = write(self->hidraw_fd, buf, 10); + ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d", + err); +} + +/* + * Call hid_hw_output_report against the given uhid device, + * check that the program is not making infinite loops. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* emit hid_hw_output_report from hidraw */ + buf[0] = 1; /* report ID */ + buf[1] = 2; + buf[2] = 42; + + err = write(self->hidraw_fd, buf, 8); + ASSERT_EQ(err, 2) TH_LOG("unexpected returned size while sending hid_hw_output_report: %d", + err); +} + /* * Attach hid_insert{0,1,2} to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 0ad452fcca58..1fa288b76cd5 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -385,3 +385,61 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_infinite_loop_raw_request = { .hid_hw_request = (void *)hid_test_infinite_loop_raw_request, }; + +SEC("?struct_ops/hid_hw_output_report") +int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum, + enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source) +{ + return -25; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_filter_output_report = { + .hid_hw_output_report = (void *)hid_test_filter_output_report, +}; + +SEC("?struct_ops.s/hid_hw_output_report") +int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); + int ret; + + if (!data) + return 0; /* EPERM check */ + + /* check if the incoming request comes from our hidraw operation */ + if (source == (__u64)current_file) + return hid_bpf_hw_output_report(hctx, data, 2); + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_hidraw_output_report = { + .hid_hw_output_report = (void *)hid_test_hidraw_output_report, +}; + +SEC("?struct_ops.s/hid_hw_output_report") +int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */); + int ret; + + if (!data) + return 0; /* EPERM check */ + + /* always forward the request as-is to the device, hid-bpf should prevent + * infinite loops. + */ + + ret = hid_bpf_hw_output_report(hctx, data, 2); + if (ret == 2) + return 2; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_infinite_loop_output_report = { + .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report, +}; From patchwork Wed Jun 26 13:46:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712957 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 433B3188CD4; Wed, 26 Jun 2024 13:47:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409626; cv=none; b=lTPb9zC2gXM7wXfidHsNyOd9MGB8v7oU4xZDWoZkznB4VmgpTqXFod2IAA3/bhPfsl53guAWP4AQN3sQ4ErAtzDR2KyicTsATlLJHK8UCCjaMJDzoWfyHKc0U+zVr0agVXWmnu+bYdFpMzykDVk998k6RUM9tg9YWuqZK8R0bf4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409626; c=relaxed/simple; bh=A2KCTEHkQRa2jGrLaI9zdebQtOmt6mnuu33Vw1F99vc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=E52kK8+7ZutQaejVAcskPSyzOhEBqMi3u9rvFJ40OPUBqul3izg/MABhHRLFmH1KplFtISrzN3y19VtDSxQ0AiJQc6oLi0SP2pAkcq1yGvYmTEugomfbritRfI8NxJYlyPiumdhxR/MkLAZnfPyznMVh6hEvjBbHVZF9v4j8TnI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=G1iIM+PM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="G1iIM+PM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D8B0FC116B1; Wed, 26 Jun 2024 13:47:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409625; bh=A2KCTEHkQRa2jGrLaI9zdebQtOmt6mnuu33Vw1F99vc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=G1iIM+PMTzQtipisU3iHDAUZqLafiSBY7fJdNvknSLvl940g0fA1aBrgn4+71Q5AG FV2u7Zn6Xr7pKwQU485OPbRZ4Nun25dRg7oRIZhY6rL2m+lewgmcq8JRhRHNFu8Ylo TFIzAOt6mTaUV11hwyPvVQVAuIk6NcI683REexQXIf+KC1HmtqtVVcByWmTxDkly1b 6vOg6BlMpG4N4glNTp4j3QBd9CEL51wjWJAe1sQ/gDg+WuEpxHwo7iZ7eogyQ3p9iB qVg50dcbHLWsdPkKWjJGH8Ef53jaOTKgUrLR6KpL5uCRSl0dnN2atmOphZXfJ8Xd8m vkyQnpHBKPo/A== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:30 +0200 Subject: [PATCH HID v2 09/13] HID: bpf: make hid_bpf_input_report() sleep until the device is ready Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-9-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=4622; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=A2KCTEHkQRa2jGrLaI9zdebQtOmt6mnuu33Vw1F99vc=; b=Kj5Z+7cYlb/8qx5JsqopPbaBVUwokNlne88tubvAAQGc2cu0AUIi2HkdsWFQjH0/iJEDx3klz 0W5pK+5LFBABwwQeKOraN0PDxae7JFn2kMP9fLY1yUNOPM18P5CaFVa X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= hid_bpf_input_report() is already marked to be used in sleepable context only. So instead of hammering with timers the device to hopefully get an available slot where the device is not sending events, we can make that kfunc wait for the current event to be terminated before it goes in. This allows to work with the following pseudo code: in struct_ops/hid_device_event: - schedule a bpf_wq, which calls hid_bpf_input_report() - once this struct_ops function terminates, hid_bpf_input_report() immediately starts before the next event Signed-off-by: Benjamin Tissoires --- no changes in v2 --- drivers/hid/bpf/hid_bpf_dispatch.c | 16 ++++++++++++---- drivers/hid/hid-core.c | 16 ++++++++++++---- include/linux/hid_bpf.h | 3 ++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 0aab49a2b869..1fb321545db0 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -494,24 +494,32 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) * @buf: a %PTR_TO_MEM buffer * @buf__sz: the size of the data to transfer * - * Returns %0 on success, a negative error code otherwise. + * Returns %0 on success, a negative error code otherwise. This function will wait for the + * device to be available before injecting the event, thus needs to be called in sleepable + * context. */ __bpf_kfunc int hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, const size_t buf__sz) { - struct hid_device *hdev; size_t size = buf__sz; int ret; + ret = down_interruptible(&ctx->hid->driver_input_lock); + if (ret) + return ret; + /* check arguments */ ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); if (ret) return ret; - hdev = (struct hid_device *)ctx->hid; /* discard const */ + ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, + true /* lock_already_taken */); + + up(&ctx->hid->driver_input_lock); - return hid_ops->hid_input_report(hdev, type, buf, size, 0, (__u64)ctx); + return ret; } __bpf_kfunc_end_defs(); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bb6f334f05bd..e9b5f44683fd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2027,7 +2027,8 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source) + u8 *data, u32 size, int interrupt, u64 source, + bool lock_already_taken) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -2037,8 +2038,13 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_input_lock)) + ret = down_trylock(&hid->driver_input_lock); + if (lock_already_taken && !ret) { + up(&hid->driver_input_lock); + return -EINVAL; + } else if (!lock_already_taken && ret) { return -EBUSY; + } if (!hid->driver) { ret = -ENODEV; @@ -2079,7 +2085,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_input_lock); + if (!lock_already_taken) + up(&hid->driver_input_lock); return ret; } @@ -2097,7 +2104,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, int interrupt) { - return __hid_input_report(hid, type, data, size, interrupt, 0); + return __hid_input_report(hid, type, data, size, interrupt, 0, + false /* lock_already_taken */); } EXPORT_SYMBOL_GPL(hid_input_report); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index f35508a73067..7f04353d09e9 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -72,7 +72,8 @@ struct hid_ops { int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, bool from_bpf); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source); + u8 *data, u32 size, int interrupt, u64 source, + bool lock_already_taken); struct module *owner; const struct bus_type *bus_type; }; From patchwork Wed Jun 26 13:46:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712958 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B6881891C3; Wed, 26 Jun 2024 13:47:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409628; cv=none; b=L/tH5lvngPpeg1qUuQGP6QRPHCcpw3wcNj59vn3HTVOWO3zu1URKU4vB6ZMYzpTHkJ0Xcu3AUc910UKnGEbHMBP1ySEEbjK2ICqFeoWI4NospCkJxpuMpgFA4WZC6L3HwxnB50PB08D2KAUeXPyeKBafHVKhkUkU0xcUYaP2suE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409628; c=relaxed/simple; bh=pVxR2nGx0Ldj0qc8SZTnUn3ognYwSYvy6WKN1a2jEOQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=R5ABVKxaxtHvBwnTDtayNAhTBxaJ+s/oJfeHSVAjSiA+3SoawM/MT/gUc+WgaBmxfScdCna7gScgeFVq1eaj5j9L9IpLQjOv/Fy64QShKL767Z634LMTGDzOd9Dzyb3hT/QTe7N5CO3ljuW5U9R3tLiDl0hLvhtsbx5oaNCR3WQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IPs0rGUd; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IPs0rGUd" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 34F81C32782; Wed, 26 Jun 2024 13:47:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409628; bh=pVxR2nGx0Ldj0qc8SZTnUn3ognYwSYvy6WKN1a2jEOQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=IPs0rGUd9+VabFx5u5Xb+b655SWr5iHyktQnsgeUJfuzxi6q5DFrUP3D7W4383e13 uJYzPcsBXjgNumaINSL+vCBE76p6UGbFHeNHS/0/NwDLVfSNVzB++Mxj8JiZbY33jp nogx/SGSoPqsqswrM+eHU7a0kXFsx+19xloeDpGL/X7HMG65SE46tdgnOlEdzrxUa9 Irc3BZ+9VFSV1S7CF+TdBgWREMEH//yArZcVX1wFrFhrdzTDVihEjJR2iUygcIPikJ itVSAKdKqUnWRgJVk8fJD4XXaanP61Ub/I9i+05z5uwxFVnP/t8m+UXsxXRjf+R9x0 fKBM/EJ/ZxCcA== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:31 +0200 Subject: [PATCH HID v2 10/13] selftests/hid: add wq test for hid_bpf_input_report() Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-10-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=4993; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=pVxR2nGx0Ldj0qc8SZTnUn3ognYwSYvy6WKN1a2jEOQ=; b=d3PEEctrWjmF8CwiJUVeQMdGr7PihHRawOlWusQBes+Nb24I8nncPVMJMf0FjUoE5aZv4PYzW 9RVgvIjMVCcAFCi2h3LLpKhqZZAEIU0hgBhxzL7d19RKI2R770G2UWL X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Now that bpf_wq is available, we can write a test with it. Having hid_bpf_input_report() waiting for the device means that we can directly call it, and we get that event when the device is ready. Signed-off-by: Benjamin Tissoires --- no changes in v2 --- tools/testing/selftests/hid/hid_bpf.c | 38 +++++++++++ tools/testing/selftests/hid/progs/hid.c | 79 ++++++++++++++++++++++ .../testing/selftests/hid/progs/hid_bpf_helpers.h | 9 +++ 3 files changed, 126 insertions(+) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 40aedd1d9dc5..31637b3b8db5 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -1130,6 +1130,44 @@ TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call) err); } +/* + * Attach hid_multiply_event_wq to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the program sees it and can add extra data + */ +TEST_F(hid_bpf, test_multiply_events_wq) +{ + const struct test_program progs[] = { + { .name = "hid_test_multiply_events_wq" }, + }; + __u8 buf[10] = {0}; + int err; + + LOAD_PROGRAMS(progs); + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + uhid_send_event(_metadata, self->uhid_fd, buf, 6); + + /* 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); + ASSERT_EQ(buf[1], 47); + + usleep(100000); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(self->hidraw_fd, buf, sizeof(buf)); + ASSERT_EQ(err, 9) TH_LOG("read_hidraw"); + ASSERT_EQ(buf[0], 2); + ASSERT_EQ(buf[1], 3); +} + /* * Attach hid_insert{0,1,2} to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 1fa288b76cd5..f539a7a223cf 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -443,3 +443,82 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_infinite_loop_output_report = { .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report, }; + +struct elem { + struct bpf_wq work; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct elem); +} hmap SEC(".maps"); + +static int wq_cb_sleepable(void *map, int *key, struct bpf_wq *work) +{ + __u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10}; + struct hid_bpf_ctx *hid_ctx; + + hid_ctx = hid_bpf_allocate_context(*key); + if (!hid_ctx) + return 0; /* EPERM check */ + + hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf)); + + hid_bpf_release_context(hid_ctx); + + return 0; +} + +static int test_inject_input_report_callback(int *key) +{ + struct elem init = {}, *val; + struct bpf_wq *wq; + + if (bpf_map_update_elem(&hmap, key, &init, 0)) + return -1; + + val = bpf_map_lookup_elem(&hmap, key); + if (!val) + return -2; + + wq = &val->work; + if (bpf_wq_init(wq, &hmap, 0) != 0) + return -3; + + if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0)) + return -4; + + if (bpf_wq_start(wq, 0)) + return -5; + + return 0; +} + +SEC("?struct_ops/hid_device_event") +int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) +{ + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */); + int hid = hid_ctx->hid->id; + int ret; + + if (!data) + return 0; /* EPERM check */ + + if (data[0] != 1) + return 0; + + ret = test_inject_input_report_callback(&hid); + if (ret) + return ret; + + data[1] += 5; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_multiply_events_wq = { + .hid_device_event = (void *)hid_test_multiply_events_wq, +}; diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h index e02e24e3eab3..8014383846d2 100644 --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h @@ -90,4 +90,13 @@ extern int hid_bpf_input_report(struct hid_bpf_ctx *ctx, __u8 *data, size_t buf__sz) __ksym; +/* bpf_wq implementation */ +extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym; +extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym; +extern int bpf_wq_set_callback_impl(struct bpf_wq *wq, + int (callback_fn)(void *map, int *key, struct bpf_wq *wq), + unsigned int flags__k, void *aux__ign) __ksym; +#define bpf_wq_set_callback(timer, cb, flags) \ + bpf_wq_set_callback_impl(timer, cb, flags, NULL) + #endif /* __HID_BPF_HELPERS_H */ From patchwork Wed Jun 26 13:46:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712959 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D8A6618A935; Wed, 26 Jun 2024 13:47:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409631; cv=none; b=F7y19zPwQd+4kt9Q3kRBYiiMj0reaOmyESzp9i6VSAP3GcguHBfb1MfwlqgsTuOPlL9lVbAcejzDij/BBvR6pSrBVDl/nN75OzDG7YJx4bG40fj/etmsX6+Al89W9mtHUZ4Up9t/MomqWgxIVrCKvYzH6ceovTeQ+0ed2u5b4fw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409631; c=relaxed/simple; bh=hyfLdKibLMSyU1dBbJDQVjZCoODwC6YGKovbqR6PglA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CMs0Fb97/Nf6yDLX7m3DV4ilu5OK/nSechOFS0o7ZoukJR90qmCnhyKjotoNn4EC/wN1pTUyS8MtsyCxShTn4sCarbzXUnzOzN7qyKg8rlgN2R9xCZpbTTfBXOdSJrroorTRL+vehR3EmQAaWIih8z9o+fkeknGs1wF5VSyYlrE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CFY3prtF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CFY3prtF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 843D4C32789; Wed, 26 Jun 2024 13:47:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409630; bh=hyfLdKibLMSyU1dBbJDQVjZCoODwC6YGKovbqR6PglA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CFY3prtF1kVwvW5ygB4BQ9HytpUPjuxL/LiM8IB8TgfO304gbZ7TUEmV5ZO6xr1CE T/hY98O6Shqs0U5vMc81mb/hiK9ELoimFj6lOkgemudbc3geWBa3Q9179htJBFncdy mLP7hZx6ESALLadgm0tngm12LFRdkX3EWlt61ayBbXs1goNnirizia7/NAewjy+tQ6 a38vlI218Sc8+UVBBL1v5NCB7+YXojCS4rTaoj40E7Me9UQJDndGXcEjTfl8uPKMWi z5bHhXzmk3IgukM+YRuADAod2/g70OAFGRsbf+8uap6wSQhCc8HUrdvCmVzVD+R3nE 13Vu+wtOzcEFw== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:32 +0200 Subject: [PATCH HID v2 11/13] HID: bpf: allow hid_device_event hooks to inject input reports on self Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-11-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=8364; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=hyfLdKibLMSyU1dBbJDQVjZCoODwC6YGKovbqR6PglA=; b=4nq40RQYWfU9QD8TJ/y5+gtnU30IuWvlup3ZU+fqs3pHkQD0yjaQBe6mHc1TQweBUZPfwOjjU 6RJVgfE4QU9DCvA+d6TBzA3wMKo85x9h6KoqUyDrktfDDNW58Ck/rat X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= This is the same logic than hid_hw_raw_request or hid_hw_output_report: we can allow hid_bpf_try_input_report to be called from a hook on hid_input_report if we ensure that the call can not be made twice in a row. There is one extra subtlety in which there is a lock in hid_input_report. But given that we can detect if we are already in the hook, we can notify hid_input_report to not take the lock. This is done by checking if ctx_kern data is valid or null, and if it is equal to the dedicated incoming data buffer. In order to have more control on whether the lock needs to be taken or not we introduce a new kfunc for it: hid_bpf_try_input_report() Signed-off-by: Benjamin Tissoires --- no changes in v2 --- Documentation/hid/hid-bpf.rst | 2 +- drivers/hid/bpf/hid_bpf_dispatch.c | 56 ++++++++++++++++++++++++++++++++------ drivers/hid/hid-core.c | 5 ++-- include/linux/hid_bpf.h | 6 ++-- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/Documentation/hid/hid-bpf.rst b/Documentation/hid/hid-bpf.rst index 8ae8f49801cb..5939eeafb361 100644 --- a/Documentation/hid/hid-bpf.rst +++ b/Documentation/hid/hid-bpf.rst @@ -202,7 +202,7 @@ Available API that can be used in syscall HID-BPF programs or in sleepable HID-B ------------------------------------------------------------------------------------------------------- .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c - :identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_allocate_context hid_bpf_release_context + :identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_try_input_report hid_bpf_allocate_context hid_bpf_release_context General overview of a HID-BPF program ===================================== diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 1fb321545db0..60d12fcd28bd 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -24,7 +24,7 @@ EXPORT_SYMBOL(hid_ops); u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 *size, int interrupt, u64 source) + u32 *size, int interrupt, u64 source, bool from_bpf) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { @@ -33,6 +33,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type .size = *size, }, .data = hdev->bpf.device_data, + .from_bpf = from_bpf, }; struct hid_bpf_ops *e; int ret; @@ -486,6 +487,50 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) return ret; } +static int +__hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + size_t size, bool lock_already_taken) +{ + struct hid_bpf_ctx_kern *ctx_kern; + int ret; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + if (ctx_kern->from_bpf) + return -EDEADLOCK; + + /* check arguments */ + ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); + if (ret) + return ret; + + return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, true, + lock_already_taken); +} + +/** + * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) + * @buf: a %PTR_TO_MEM buffer + * @buf__sz: the size of the data to transfer + * + * Returns %0 on success, a negative error code otherwise. This function will immediately + * fail if the device is not available, thus can be safely used in IRQ context. + */ +__bpf_kfunc int +hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + const size_t buf__sz) +{ + struct hid_bpf_ctx_kern *ctx_kern; + bool from_hid_event_hook; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data; + + return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook); +} + /** * hid_bpf_input_report - Inject a HID report in the kernel from a HID device * @@ -502,7 +547,6 @@ __bpf_kfunc int hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, const size_t buf__sz) { - size_t size = buf__sz; int ret; ret = down_interruptible(&ctx->hid->driver_input_lock); @@ -510,12 +554,7 @@ hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf return ret; /* check arguments */ - ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); - if (ret) - return ret; - - ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, - true /* lock_already_taken */); + ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */); up(&ctx->hid->driver_input_lock); @@ -534,6 +573,7 @@ BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE) BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE) BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE) BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_try_input_report) BTF_KFUNCS_END(hid_bpf_kfunc_ids) static const struct btf_kfunc_id_set hid_bpf_kfunc_set = { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e9b5f44683fd..52a75afe3e7d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2027,7 +2027,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, bool lock_already_taken) { struct hid_report_enum *report_enum; @@ -2053,7 +2053,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, report_enum = hid->report_enum + type; hdrv = hid->driver; - data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source); + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); if (IS_ERR(data)) { ret = PTR_ERR(data); goto unlock; @@ -2105,6 +2105,7 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data int interrupt) { return __hid_input_report(hid, type, data, size, interrupt, 0, + false, /* from_bpf */ false /* lock_already_taken */); } EXPORT_SYMBOL_GPL(hid_input_report); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 7f04353d09e9..93546ee7677a 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -72,7 +72,7 @@ struct hid_ops { int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, bool from_bpf); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, bool lock_already_taken); struct module *owner; const struct bus_type *bus_type; @@ -195,7 +195,7 @@ struct hid_bpf { #ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, - u32 *size, int interrupt, u64 source); + u32 *size, int interrupt, u64 source, bool from_bpf); int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, u32 size, enum hid_report_type rtype, @@ -211,7 +211,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s #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, - u64 source) { return data; } + u64 source, bool from_bpf) { return data; } static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, u8 *buf, u32 size, enum hid_report_type rtype, From patchwork Wed Jun 26 13:46:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712960 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2746E18C35C; Wed, 26 Jun 2024 13:47:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409633; cv=none; b=KytstjYPYD95HHvt+rxL7nS+qvLGcXQO5BrpNMpIHSfFh3PX3yFjpZnoBGDZGUly635oRWlM9NJdV2tpK04mMJ3P7VKFyorxdWX/m23dE6lnSFKYVZ0ELWC5rQLMsIUjOEQ1gINeun8jwUQn6r7081i/MduJAbnODKTitPz+PQA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409633; c=relaxed/simple; bh=8DVfI5Mgh3sBj3CpzjHKwk7ojiMdJpUgU0LXrBaKg2c=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=URdVfHAo3IPgOENqsE5SynGoHdFD0MuKtl8/hr3nUFhG7g75lQnkSNUs3zYwYnfivLIye66fJDD5t9/DvPlcSZndwca3LwppeEdjfw+IAvc9yJAWtLGynXZ6Osbu7ojSy7zeXxC8VSsbgu/1LPagesYlfWLd9oqtt6US3JksFoc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AnLkus0S; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AnLkus0S" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D388DC32782; Wed, 26 Jun 2024 13:47:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409632; bh=8DVfI5Mgh3sBj3CpzjHKwk7ojiMdJpUgU0LXrBaKg2c=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=AnLkus0ScJlNtHUTw9KITY2V/hjtnycgXxOlFC6L5c1V24zrpjWKsjW8TkYOaVf3v 9CItVAIaGnzFxkKbnhCVlRZ4/CwyQlgqkAw8SQv5sO3n46QT5juEiOCD8lgLZgFitt tiewbBnUoZqfVRSobPMgMIiGe40JN9fqfOHztEpTIa6cgNo+fl68HT9W/Ucdl9ykBw Mmjr2nxEXyYoRfH6pcaazP/ZcoBbK7ey7QVtmZzHfV52BISMc8DeTNRIdf8Eo0Xnm+ a7DXzkiDeOrKeetS+wTOSxFZRqNti19Ibj3OJl2v2sO15iYRtsQtwZVnJgu+bKJuRb qmprk5Se1UmAw== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:33 +0200 Subject: [PATCH HID v2 12/13] selftests/hid: add another test for injecting an event from an event hook Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-12-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=4137; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=8DVfI5Mgh3sBj3CpzjHKwk7ojiMdJpUgU0LXrBaKg2c=; b=DvtSTPl9Sbeel8MPOCTiv0xr/zIBIx8/BnIBFF/7gK7Yik5kufwDTjLOlFE4C8CavsG79lPZW dAQDSh/zHOnAqyrKfAvW448xXEFrBMKWxNpmKPQHR6ixZeHhFdbGTj6 X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Similar to test_multiply_events_wq: we receive one event and inject a new one. But given that this time we are already in the event hook, we can use hid_bpf_try_input_report() directly as this function will not sleep. Note that the injected event gets processed before the original one this way. Signed-off-by: Benjamin Tissoires --- no changes in v2 --- tools/testing/selftests/hid/hid_bpf.c | 36 ++++++++++++++++++++ tools/testing/selftests/hid/progs/hid.c | 39 ++++++++++++++++++++++ .../testing/selftests/hid/progs/hid_bpf_helpers.h | 4 +++ 3 files changed, 79 insertions(+) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 31637b3b8db5..36bbad8e0f9f 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -1168,6 +1168,42 @@ TEST_F(hid_bpf, test_multiply_events_wq) ASSERT_EQ(buf[1], 3); } +/* + * Attach hid_multiply_event to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the program sees it and can add extra data + */ +TEST_F(hid_bpf, test_multiply_events) +{ + const struct test_program progs[] = { + { .name = "hid_test_multiply_events" }, + }; + __u8 buf[10] = {0}; + int err; + + LOAD_PROGRAMS(progs); + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + uhid_send_event(_metadata, self->uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(self->hidraw_fd, buf, sizeof(buf)); + ASSERT_EQ(err, 9) TH_LOG("read_hidraw"); + ASSERT_EQ(buf[0], 2); + ASSERT_EQ(buf[1], 47); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(self->hidraw_fd, buf, sizeof(buf)); + ASSERT_EQ(err, 9) TH_LOG("read_hidraw"); + ASSERT_EQ(buf[0], 2); + ASSERT_EQ(buf[1], 52); +} + /* * Attach hid_insert{0,1,2} to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index f539a7a223cf..46feeb91d1d5 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -522,3 +522,42 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_multiply_events_wq = { .hid_device_event = (void *)hid_test_multiply_events_wq, }; + +SEC("?struct_ops/hid_device_event") +int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) +{ + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */); + __u8 buf[9]; + int ret; + + if (!data) + return 0; /* EPERM check */ + + if (data[0] != 1) + return 0; + + /* + * we have to use an intermediate buffer as hid_bpf_input_report + * will memset data to \0 + */ + __builtin_memcpy(buf, data, sizeof(buf)); + + buf[0] = 2; + buf[1] += 5; + ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf)); + if (ret < 0) + return ret; + + /* + * In real world we should reset the original buffer as data might be garbage now, + * but it actually now has the content of 'buf' + */ + data[1] += 5; + + return 9; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_multiply_events = { + .hid_device_event = (void *)hid_test_multiply_events, +}; diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h index 8014383846d2..c72e44321764 100644 --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h @@ -89,6 +89,10 @@ extern int hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, __u8 *data, size_t buf__sz) __ksym; +extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, + enum hid_report_type type, + __u8 *data, + size_t buf__sz) __ksym; /* bpf_wq implementation */ extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym; From patchwork Wed Jun 26 13:46:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 13712961 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 786F818E755; Wed, 26 Jun 2024 13:47:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409635; cv=none; b=YEGf4G0SDa+C1d2gy5fYZd8yIcziu92VBT6xALJkG/6NiOcjGbxUKRAeG9PQPhdOGnGv4tAYCgnxd5yBAcJkddX/h+ascP3nZnbbAayH4BgpgiQRfsfOCw/1pkTDM5nkW9aK0RDzvpW3xjLfRqFmFrANbi3d0EEg/eLx01L5gOM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719409635; c=relaxed/simple; bh=coSvairEXZ/UoBj93IfzT1BpGcOK/jnPrORkflCNJ84=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NPqwuL+s//UYPAZ8sBkQ2mDNJw94M/bHi+uageZJGGa+WtL3j5nq6J6H2kvaSdILv75kIcC/7+TD7WUQIYYLe/xi+2BZZJKcs7SzIb+6C55UyeV7uaIt418zdA+5NA6Y9HrzwgG0G1B71NNzWSyi3bps2U7mfDkAdJpF4eGvExI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=M7TgyvOq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="M7TgyvOq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2F55BC4AF07; Wed, 26 Jun 2024 13:47:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719409635; bh=coSvairEXZ/UoBj93IfzT1BpGcOK/jnPrORkflCNJ84=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=M7TgyvOqh+G8JBHZHxNOGoIIsb0OsioBMMg+gOn3TNV2PLOLkJ4AFX/7QXQhW2132 XYagHiEHIDF3QcAPTMADf7q5VPdhGTK7+owjeNWkzBJ1cXERnJ/1h1iykUccDUp4PE kuD1m9RrzITqb69QiZOIi/HxAY299c+0rPcgtu0J3NNyRRKFeEt6ndbKgLrReKiIKC wguo+WVHSMkOPKQiq7dWMNaB4ardr+pcIv2Rpi4IsAoT+ONvZ+YBv5mbuX82d2JhHw KXt2QnVFzV2gGVnLVKrRtAA2ERheboepDnc0EgKd8WTobmRObueqSzVRoAdBmWSofZ hIdu+Z7T9P6Fg== From: Benjamin Tissoires Date: Wed, 26 Jun 2024 15:46:34 +0200 Subject: [PATCH HID v2 13/13] selftests/hid: add an infinite loop test for hid_bpf_try_input_report Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240626-hid_hw_req_bpf-v2-13-cfd60fb6c79f@kernel.org> References: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> In-Reply-To: <20240626-hid_hw_req_bpf-v2-0-cfd60fb6c79f@kernel.org> To: Jiri Kosina , Alexei Starovoitov , Shuah Khan , Jonathan Corbet Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1719409602; l=3521; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=coSvairEXZ/UoBj93IfzT1BpGcOK/jnPrORkflCNJ84=; b=y3R38nLnIx95jobpAICJH1TPSo58IZ3qiL3BnUPgxNlYvk6mTJ8bIWv2euPxrMe7T3qg3vOGa jOkMsJiAcShAU0XVpvS0VDk6ymvazKa9awoDZgpz/V6UVXz4BJjAvYA X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= We don't want this call to allow an infinite loop in HID-BPF, so let's have some tests. Signed-off-by: Benjamin Tissoires --- no changes in v2 --- tools/testing/selftests/hid/hid_bpf.c | 41 +++++++++++++++++++++++++++++++++ tools/testing/selftests/hid/progs/hid.c | 37 +++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 36bbad8e0f9f..dc0408a831d0 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -1204,6 +1204,47 @@ TEST_F(hid_bpf, test_multiply_events) ASSERT_EQ(buf[1], 52); } +/* + * Call hid_bpf_input_report against the given uhid device, + * check that the program is not making infinite loops. + */ +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}; + int err; + + LOAD_PROGRAMS(progs); + + /* emit hid_hw_output_report from hidraw */ + buf[0] = 1; /* report ID */ + buf[1] = 2; + buf[2] = 42; + + uhid_send_event(_metadata, self->uhid_fd, buf, 6); + + /* 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); + ASSERT_EQ(buf[1], 3); + + /* read the data from hidraw: hid_bpf_try_input_report should work exactly one time */ + 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); + ASSERT_EQ(buf[1], 4); + + /* read the data from hidraw: there should be none */ + memset(buf, 0, sizeof(buf)); + err = read(self->hidraw_fd, buf, sizeof(buf)); + ASSERT_EQ(err, -1) TH_LOG("read_hidraw"); +} + /* * Attach hid_insert{0,1,2} to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 46feeb91d1d5..ee9bbbcf751b 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -561,3 +561,40 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_multiply_events = { .hid_device_event = (void *)hid_test_multiply_events, }; + +SEC("?struct_ops/hid_device_event") +int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx, + enum hid_report_type report_type, __u64 source) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */); + __u8 buf[6]; + + if (!data) + return 0; /* EPERM check */ + + /* + * we have to use an intermediate buffer as hid_bpf_input_report + * will memset data to \0 + */ + __builtin_memcpy(buf, data, sizeof(buf)); + + /* always forward the request as-is to the device, hid-bpf should prevent + * infinite loops. + * the return value is ignored so the event is passing to userspace. + */ + + hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf)); + + /* each time we process the event, we increment by one data[1]: + * after each successful call to hid_bpf_try_input_report, buf + * has been memcopied into data by the kernel. + */ + data[1] += 1; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_infinite_loop_input_report = { + .hid_device_event = (void *)hid_test_infinite_loop_input_report, +};