]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
HID: pass the buffer size to hid_report_raw_event
authorBenjamin Tissoires <bentiss@kernel.org>
Mon, 4 May 2026 08:47:22 +0000 (10:47 +0200)
committerJiri Kosina <jkosina@suse.com>
Tue, 12 May 2026 16:03:37 +0000 (18:03 +0200)
commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing
bogus memset()") enforced the provided data to be at least the size of
the declared buffer in the report descriptor to prevent a buffer
overflow. However, we can try to be smarter by providing both the buffer
size and the data size, meaning that hid_report_raw_event() can make
better decision whether we should plaining reject the buffer (buffer
overflow attempt) or if we can safely memset it to 0 and pass it to the
rest of the stack.

Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()")
Cc: stable@vger.kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Acked-by: Johan Hovold <johan@kernel.org>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
drivers/hid/bpf/hid_bpf_dispatch.c
drivers/hid/hid-core.c
drivers/hid/hid-gfrm.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-primax.c
drivers/hid/hid-vivaldi-common.c
drivers/hid/wacom_sys.c
drivers/staging/greybus/hid.c
include/linux/hid.h
include/linux/hid_bpf.h

index 50c7b45c59e3fb763cd6a424ab024324174fdf58..d0130658091b02afe02d540b9e420d8eb8b0d104 100644 (file)
@@ -24,7 +24,8 @@ 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, bool from_bpf)
+                             size_t *buf_size, u32 *size, int interrupt, u64 source,
+                             bool from_bpf)
 {
        struct hid_bpf_ctx_kern ctx_kern = {
                .ctx = {
@@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
                *size = ret;
        }
 
+       *buf_size = ctx_kern.ctx.allocated_size;
        return ctx_kern.data;
 }
 EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
@@ -505,7 +507,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b
        if (ret)
                return ret;
 
-       return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true,
+       return hid_ops->hid_input_report(ctx->hid, type, buf, size, size, 0, (u64)(long)ctx, true,
                                         lock_already_taken);
 }
 
index 61afec5915ecfa7fd57e0ceb3f044c44428fe223..a806820df7e55371adf1e17e6eae705e06f21b95 100644 (file)
@@ -2033,24 +2033,32 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
 }
 EXPORT_SYMBOL_GPL(__hid_request);
 
-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
-                        int interrupt)
+int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
+                        size_t bufsize, u32 size, int interrupt)
 {
        struct hid_report_enum *report_enum = hid->report_enum + type;
        struct hid_report *report;
        struct hid_driver *hdrv;
        int max_buffer_size = HID_MAX_BUFFER_SIZE;
        u32 rsize, csize = size;
+       size_t bsize = bufsize;
        u8 *cdata = data;
        int ret = 0;
 
        report = hid_get_report(report_enum, data);
        if (!report)
-               goto out;
+               return 0;
+
+       if (unlikely(bsize < csize)) {
+               hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
+                                    report->id, csize, bsize);
+               return -EINVAL;
+       }
 
        if (report_enum->numbered) {
                cdata++;
                csize--;
+               bsize--;
        }
 
        rsize = hid_compute_report_size(report);
@@ -2063,11 +2071,16 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
        else if (rsize > max_buffer_size)
                rsize = max_buffer_size;
 
+       if (bsize < rsize) {
+               hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
+                                    report->id, rsize, bsize);
+               return -EINVAL;
+       }
+
        if (csize < rsize) {
-               hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
-                                    report->id, rsize, csize);
-               ret = -EINVAL;
-               goto out;
+               dbg_hid("report %d is too short, (%d < %d)\n", report->id,
+                       csize, rsize);
+               memset(cdata + csize, 0, rsize - csize);
        }
 
        if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
@@ -2075,7 +2088,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
        if (hid->claimed & HID_CLAIMED_HIDRAW) {
                ret = hidraw_report_event(hid, data, size);
                if (ret)
-                       goto out;
+                       return ret;
        }
 
        if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
@@ -2087,15 +2100,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
 
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_report_event(hid, report);
-out:
+
        return ret;
 }
 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, bool from_bpf,
-                             bool lock_already_taken)
+                             u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
+                             bool from_bpf, bool lock_already_taken)
 {
        struct hid_report_enum *report_enum;
        struct hid_driver *hdrv;
@@ -2120,7 +2133,8 @@ 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, from_bpf);
+       data = dispatch_hid_bpf_device_event(hid, type, data, &bufsize, &size, interrupt,
+                                            source, from_bpf);
        if (IS_ERR(data)) {
                ret = PTR_ERR(data);
                goto unlock;
@@ -2149,7 +2163,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
                        goto unlock;
        }
 
-       ret = hid_report_raw_event(hid, type, data, size, interrupt);
+       ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);
 
 unlock:
        if (!lock_already_taken)
@@ -2171,7 +2185,7 @@ unlock:
 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, size, interrupt, 0,
                                  false, /* from_bpf */
                                  false /* lock_already_taken */);
 }
index 699186ff2349e92a6ee70ef23b93e1f6af6f4e88..d2a56bf92b416efd0c42f99a111cd24160b1bba0 100644 (file)
@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
        switch (data[1]) {
        case GFRM100_SEARCH_KEY_DOWN:
                ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
-                                          sizeof(search_key_dn), 1);
+                                          sizeof(search_key_dn), sizeof(search_key_dn), 1);
                break;
 
        case GFRM100_SEARCH_KEY_AUDIO_DATA:
@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
 
        case GFRM100_SEARCH_KEY_UP:
                ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
-                                          sizeof(search_key_up), 1);
+                                          sizeof(search_key_up), sizeof(search_key_up), 1);
                break;
 
        default:
index b1330d23bd2d03e4daa1ce2d8db90efd9d74af1c..b3ff9265377b956bc1eb12626d3045a6235fc329 100644 (file)
@@ -3673,7 +3673,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
        memcpy(&consumer_report[1], &data[3], 4);
        /* We are called from atomic context */
        hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
-                            consumer_report, 5, 1);
+                            consumer_report, sizeof(consumer_report), 5, 1);
 
        return 1;
 }
index e82a3c4e5b44ef9c3dcc89248e503dcd7fae3f84..eeab0b6e32ccce626472252b61c3b18333d00e4b 100644 (file)
@@ -533,7 +533,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
                }
 
                ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
-                                          size, 0);
+                                          size, size, 0);
                if (ret)
                        dev_warn(&hdev->dev, "failed to report feature\n");
        }
index e44d79dff8de630f0248fc67ef20422c0d184388..8db054280afbcdae4b09d62187a3c0fa1d4b5ca4 100644 (file)
@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
                        data[0] |= (1 << (data[idx] - 0xE0));
                        data[idx] = 0;
                }
-               hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0);
+               hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, size, 0);
                return 1;
 
        default:        /* unknown report */
index bf734055d4b69ddebd39a0c3c933872b75e0fa7a..b12bb5cc091aa3471fd7d3398e3b0189833c8643 100644 (file)
@@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
        }
 
        ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
-                                  report_len, 0);
+                                  report_len, report_len, 0);
        if (ret) {
                dev_warn(&hdev->dev, "failed to report feature %d\n",
                         field->report->id);
index 0d1c6d90fe21c562be547bf404ae85c8b41bcb6c..a32320b351e3eeda96394c090146455014f59c61 100644 (file)
@@ -90,7 +90,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
                        kfree(buf);
                        continue;
                }
-               err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false);
+               err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false);
                if (err) {
                        hid_warn(hdev, "%s: unable to flush event due to error %d\n",
                                 __func__, err);
@@ -334,7 +334,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
                                               data, n, WAC_CMD_RETRIES);
                        if (ret == n && features->type == HID_GENERIC) {
                                ret = hid_report_raw_event(hdev,
-                                       HID_FEATURE_REPORT, data, n, 0);
+                                       HID_FEATURE_REPORT, data, n, n, 0);
                        } else if (ret == 2 && features->type != HID_GENERIC) {
                                features->touch_max = data[1];
                        } else {
@@ -395,7 +395,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
                                        data, n, WAC_CMD_RETRIES);
                if (ret == n) {
                        ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
-                                                  data, n, 0);
+                                                  data, n, n, 0);
                } else {
                        hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
                                 __func__);
index 1f58c907c03683dc5930cad5308c9bfbe14f3779..f1f9f6fbc00e5251cbcb967af0936722c3f19030 100644 (file)
@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)
         * we just need to setup the input fields, so using
         * hid_report_raw_event is safe.
         */
-       hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1);
+       hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size, 1);
 }
 
 static void gb_hid_init_reports(struct gb_hid *ghid)
index 442a80d79e899d96b35fe102471e132a632995f8..ac432a2ef415aa383d773a3ecca6afc4aac05f60 100644 (file)
@@ -1298,8 +1298,8 @@ static inline u32 hid_report_len(struct hid_report *report)
        return DIV_ROUND_UP(report->size, 8) + (report->id > 0);
 }
 
-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
-                        int interrupt);
+int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
+                        size_t bufsize, u32 size, int interrupt);
 
 /* HID quirks API */
 unsigned long hid_lookup_quirk(const struct hid_device *hdev);
index a2e47dbcf82c8bbbc547ebc18c82e11bfe8c6236..19fffa4574a47cd6c8af5c732fb1ecb9add2c4ff 100644 (file)
@@ -72,8 +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, bool from_bpf,
-                               bool lock_already_taken);
+                               u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
+                               bool from_bpf, bool lock_already_taken);
        struct module *owner;
        const struct bus_type *bus_type;
 };
@@ -200,7 +200,8 @@ 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, bool from_bpf);
+                                 size_t *buf_size, 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,
@@ -215,8 +216,11 @@ int hid_bpf_device_init(struct hid_device *hid);
 const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const 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,
-                                               u64 source, bool from_bpf) { return data; }
+                                               u8 *data, size_t *buf_size, u32 *size,
+                                               int interrupt, 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,