mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 15:12:13 +02:00
HID: pass the buffer size to hid_report_raw_event
commit0a3fe972a7("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:0a3fe972a7("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>
This commit is contained in:
parent
b08665fe80
commit
2c85c61d13
|
|
@ -24,7 +24,8 @@ EXPORT_SYMBOL(hid_ops);
|
||||||
|
|
||||||
u8 *
|
u8 *
|
||||||
dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
|
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 = {
|
struct hid_bpf_ctx_kern ctx_kern = {
|
||||||
.ctx = {
|
.ctx = {
|
||||||
|
|
@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
|
||||||
*size = ret;
|
*size = ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*buf_size = ctx_kern.ctx.allocated_size;
|
||||||
return ctx_kern.data;
|
return ctx_kern.data;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
|
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)
|
if (ret)
|
||||||
return 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);
|
lock_already_taken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2033,24 +2033,32 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__hid_request);
|
EXPORT_SYMBOL_GPL(__hid_request);
|
||||||
|
|
||||||
int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
|
int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
|
||||||
int interrupt)
|
size_t bufsize, u32 size, int interrupt)
|
||||||
{
|
{
|
||||||
struct hid_report_enum *report_enum = hid->report_enum + type;
|
struct hid_report_enum *report_enum = hid->report_enum + type;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_driver *hdrv;
|
struct hid_driver *hdrv;
|
||||||
int max_buffer_size = HID_MAX_BUFFER_SIZE;
|
int max_buffer_size = HID_MAX_BUFFER_SIZE;
|
||||||
u32 rsize, csize = size;
|
u32 rsize, csize = size;
|
||||||
|
size_t bsize = bufsize;
|
||||||
u8 *cdata = data;
|
u8 *cdata = data;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
report = hid_get_report(report_enum, data);
|
report = hid_get_report(report_enum, data);
|
||||||
if (!report)
|
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) {
|
if (report_enum->numbered) {
|
||||||
cdata++;
|
cdata++;
|
||||||
csize--;
|
csize--;
|
||||||
|
bsize--;
|
||||||
}
|
}
|
||||||
|
|
||||||
rsize = hid_compute_report_size(report);
|
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)
|
else if (rsize > max_buffer_size)
|
||||||
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) {
|
if (csize < rsize) {
|
||||||
hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
|
dbg_hid("report %d is too short, (%d < %d)\n", report->id,
|
||||||
report->id, rsize, csize);
|
csize, rsize);
|
||||||
ret = -EINVAL;
|
memset(cdata + csize, 0, rsize - csize);
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
|
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) {
|
if (hid->claimed & HID_CLAIMED_HIDRAW) {
|
||||||
ret = hidraw_report_event(hid, data, size);
|
ret = hidraw_report_event(hid, data, size);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
|
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)
|
if (hid->claimed & HID_CLAIMED_INPUT)
|
||||||
hidinput_report_event(hid, report);
|
hidinput_report_event(hid, report);
|
||||||
out:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_report_raw_event);
|
EXPORT_SYMBOL_GPL(hid_report_raw_event);
|
||||||
|
|
||||||
|
|
||||||
static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
|
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,
|
u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
|
||||||
bool lock_already_taken)
|
bool from_bpf, bool lock_already_taken)
|
||||||
{
|
{
|
||||||
struct hid_report_enum *report_enum;
|
struct hid_report_enum *report_enum;
|
||||||
struct hid_driver *hdrv;
|
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;
|
report_enum = hid->report_enum + type;
|
||||||
hdrv = hid->driver;
|
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)) {
|
if (IS_ERR(data)) {
|
||||||
ret = PTR_ERR(data);
|
ret = PTR_ERR(data);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
@ -2149,7 +2163,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hid_report_raw_event(hid, type, data, size, interrupt);
|
ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
if (!lock_already_taken)
|
if (!lock_already_taken)
|
||||||
|
|
@ -2171,7 +2185,7 @@ 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 hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
|
||||||
int interrupt)
|
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, /* from_bpf */
|
||||||
false /* lock_already_taken */);
|
false /* lock_already_taken */);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||||
switch (data[1]) {
|
switch (data[1]) {
|
||||||
case GFRM100_SEARCH_KEY_DOWN:
|
case GFRM100_SEARCH_KEY_DOWN:
|
||||||
ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
|
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;
|
break;
|
||||||
|
|
||||||
case GFRM100_SEARCH_KEY_AUDIO_DATA:
|
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:
|
case GFRM100_SEARCH_KEY_UP:
|
||||||
ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, 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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -3673,7 +3673,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
|
||||||
memcpy(&consumer_report[1], &data[3], 4);
|
memcpy(&consumer_report[1], &data[3], 4);
|
||||||
/* We are called from atomic context */
|
/* We are called from atomic context */
|
||||||
hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
|
hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
|
||||||
consumer_report, 5, 1);
|
consumer_report, sizeof(consumer_report), 5, 1);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
|
||||||
size, 0);
|
size, size, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_warn(&hdev->dev, "failed to report feature\n");
|
dev_warn(&hdev->dev, "failed to report feature\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
|
||||||
data[0] |= (1 << (data[idx] - 0xE0));
|
data[0] |= (1 << (data[idx] - 0xE0));
|
||||||
data[idx] = 0;
|
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;
|
return 1;
|
||||||
|
|
||||||
default: /* unknown report */
|
default: /* unknown report */
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
|
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
|
||||||
report_len, 0);
|
report_len, report_len, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_warn(&hdev->dev, "failed to report feature %d\n",
|
dev_warn(&hdev->dev, "failed to report feature %d\n",
|
||||||
field->report->id);
|
field->report->id);
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
continue;
|
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) {
|
if (err) {
|
||||||
hid_warn(hdev, "%s: unable to flush event due to error %d\n",
|
hid_warn(hdev, "%s: unable to flush event due to error %d\n",
|
||||||
__func__, err);
|
__func__, err);
|
||||||
|
|
@ -334,7 +334,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
||||||
data, n, WAC_CMD_RETRIES);
|
data, n, WAC_CMD_RETRIES);
|
||||||
if (ret == n && features->type == HID_GENERIC) {
|
if (ret == n && features->type == HID_GENERIC) {
|
||||||
ret = hid_report_raw_event(hdev,
|
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) {
|
} else if (ret == 2 && features->type != HID_GENERIC) {
|
||||||
features->touch_max = data[1];
|
features->touch_max = data[1];
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -395,7 +395,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
||||||
data, n, WAC_CMD_RETRIES);
|
data, n, WAC_CMD_RETRIES);
|
||||||
if (ret == n) {
|
if (ret == n) {
|
||||||
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
|
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
|
||||||
data, n, 0);
|
data, n, n, 0);
|
||||||
} else {
|
} else {
|
||||||
hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
|
hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
|
||||||
|
|
@ -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
|
* we just need to setup the input fields, so using
|
||||||
* hid_report_raw_event is safe.
|
* 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)
|
static void gb_hid_init_reports(struct gb_hid *ghid)
|
||||||
|
|
|
||||||
|
|
@ -1298,8 +1298,8 @@ static inline u32 hid_report_len(struct hid_report *report)
|
||||||
return DIV_ROUND_UP(report->size, 8) + (report->id > 0);
|
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 hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
|
||||||
int interrupt);
|
size_t bufsize, u32 size, int interrupt);
|
||||||
|
|
||||||
/* HID quirks API */
|
/* HID quirks API */
|
||||||
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
|
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ struct hid_ops {
|
||||||
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
|
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
|
||||||
u64 source, bool from_bpf);
|
u64 source, bool from_bpf);
|
||||||
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, u64 source, bool from_bpf,
|
u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
|
||||||
bool lock_already_taken);
|
bool from_bpf, bool lock_already_taken);
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
const struct bus_type *bus_type;
|
const struct bus_type *bus_type;
|
||||||
};
|
};
|
||||||
|
|
@ -200,7 +200,8 @@ struct hid_bpf {
|
||||||
|
|
||||||
#ifdef CONFIG_HID_BPF
|
#ifdef CONFIG_HID_BPF
|
||||||
u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
|
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,
|
int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
|
||||||
unsigned char reportnum, __u8 *buf,
|
unsigned char reportnum, __u8 *buf,
|
||||||
u32 size, enum hid_report_type rtype,
|
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);
|
const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
|
||||||
#else /* CONFIG_HID_BPF */
|
#else /* CONFIG_HID_BPF */
|
||||||
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
||||||
u8 *data, u32 *size, int interrupt,
|
u8 *data, size_t *buf_size, u32 *size,
|
||||||
u64 source, bool from_bpf) { return data; }
|
int interrupt, u64 source, bool from_bpf)
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
|
static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
|
||||||
unsigned char reportnum, u8 *buf,
|
unsigned char reportnum, u8 *buf,
|
||||||
u32 size, enum hid_report_type rtype,
|
u32 size, enum hid_report_type rtype,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user