Commit 35556bed authored by Marc Zyngier's avatar Marc Zyngier Committed by Benjamin Tissoires

HID: core: Sanitize event code and type when mapping input

When calling into hid_map_usage(), the passed event code is
blindly stored as is, even if it doesn't fit in the associated bitmap.

This event code can come from a variety of sources, including devices
masquerading as input devices, only a bit more "programmable".

Instead of taking the event code at face value, check that it actually
fits the corresponding bitmap, and if it doesn't:
- spit out a warning so that we know which device is acting up
- NULLify the bitmap pointer so that we catch unexpected uses

Code paths that can make use of untrusted inputs can now check
that the mapping was indeed correct and bail out if not.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@gmail.com>
parent bce1305c
...@@ -1132,6 +1132,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -1132,6 +1132,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
} }
mapped: mapped:
/* Mapping failed, bail out */
if (!bit)
return;
if (device->driver->input_mapped && if (device->driver->input_mapped &&
device->driver->input_mapped(device, hidinput, field, usage, device->driver->input_mapped(device, hidinput, field, usage,
&bit, &max) < 0) { &bit, &max) < 0) {
......
...@@ -856,6 +856,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -856,6 +856,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
code = BTN_0 + ((usage->hid - 1) & HID_USAGE); code = BTN_0 + ((usage->hid - 1) & HID_USAGE);
hid_map_usage(hi, usage, bit, max, EV_KEY, code); hid_map_usage(hi, usage, bit, max, EV_KEY, code);
if (!*bit)
return -1;
input_set_capability(hi->input, EV_KEY, code); input_set_capability(hi->input, EV_KEY, code);
return 1; return 1;
......
...@@ -959,34 +959,49 @@ static inline void hid_device_io_stop(struct hid_device *hid) { ...@@ -959,34 +959,49 @@ static inline void hid_device_io_stop(struct hid_device *hid) {
* @max: maximal valid usage->code to consider later (out parameter) * @max: maximal valid usage->code to consider later (out parameter)
* @type: input event type (EV_KEY, EV_REL, ...) * @type: input event type (EV_KEY, EV_REL, ...)
* @c: code which corresponds to this usage and type * @c: code which corresponds to this usage and type
*
* The value pointed to by @bit will be set to NULL if either @type is
* an unhandled event type, or if @c is out of range for @type. This
* can be used as an error condition.
*/ */
static inline void hid_map_usage(struct hid_input *hidinput, static inline void hid_map_usage(struct hid_input *hidinput,
struct hid_usage *usage, unsigned long **bit, int *max, struct hid_usage *usage, unsigned long **bit, int *max,
__u8 type, __u16 c) __u8 type, unsigned int c)
{ {
struct input_dev *input = hidinput->input; struct input_dev *input = hidinput->input;
unsigned long *bmap = NULL;
usage->type = type; unsigned int limit = 0;
usage->code = c;
switch (type) { switch (type) {
case EV_ABS: case EV_ABS:
*bit = input->absbit; bmap = input->absbit;
*max = ABS_MAX; limit = ABS_MAX;
break; break;
case EV_REL: case EV_REL:
*bit = input->relbit; bmap = input->relbit;
*max = REL_MAX; limit = REL_MAX;
break; break;
case EV_KEY: case EV_KEY:
*bit = input->keybit; bmap = input->keybit;
*max = KEY_MAX; limit = KEY_MAX;
break; break;
case EV_LED: case EV_LED:
*bit = input->ledbit; bmap = input->ledbit;
*max = LED_MAX; limit = LED_MAX;
break; break;
} }
if (unlikely(c > limit || !bmap)) {
pr_warn_ratelimited("%s: Invalid code %d type %d\n",
input->name, c, type);
*bit = NULL;
return;
}
usage->type = type;
usage->code = c;
*max = limit;
*bit = bmap;
} }
/** /**
...@@ -1000,7 +1015,8 @@ static inline void hid_map_usage_clear(struct hid_input *hidinput, ...@@ -1000,7 +1015,8 @@ static inline void hid_map_usage_clear(struct hid_input *hidinput,
__u8 type, __u16 c) __u8 type, __u16 c)
{ {
hid_map_usage(hidinput, usage, bit, max, type, c); hid_map_usage(hidinput, usage, bit, max, type, c);
clear_bit(c, *bit); if (*bit)
clear_bit(usage->code, *bit);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment