mirror of
https://gitlab.freedesktop.org/libevdev/libevdev.git
synced 2025-12-25 01:10:10 +01:00
Cap slot values to the announced maximum
A malicious device may announce N slots but then send a slot index >= N. The slot state is almost always allocated (definitely the case in libevdev and true for most callers), so providing a slot number higher than the announced maximum is likely to lead to invalid dereferences. Don't allow that. Likewise, don't allow negative slot numbers. Note that the kernel filters these events anyway, the only way to trigger this is to change the device fd to something outside the kernel's control. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
parent
4ba56ac309
commit
66fee1bec4
3 changed files with 108 additions and 1 deletions
|
|
@ -809,6 +809,26 @@ read_more_events(struct libevdev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize/modify events where needed.
|
||||
* @return 0 if untouched, 1 if modified.
|
||||
*/
|
||||
static inline int
|
||||
sanitize_event(const struct libevdev *dev, struct input_event *ev)
|
||||
{
|
||||
if (unlikely(dev->num_slots > -1 &&
|
||||
libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT) &&
|
||||
(ev->value < 0 || ev->value >= dev->num_slots))) {
|
||||
log_bug("Device %s received an invalid slot index %d."
|
||||
"Capping to announced max slot number %d.\n",
|
||||
dev->name, ev->value, dev->num_slots - 1);
|
||||
ev->value = dev->num_slots - 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBEVDEV_EXPORT int
|
||||
libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
|
||||
{
|
||||
|
|
@ -875,6 +895,7 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event
|
|||
if (queue_shift(dev, ev) != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
sanitize_event(dev, ev);
|
||||
update_state(dev, ev);
|
||||
|
||||
/* if we disabled a code, get the next event instead */
|
||||
|
|
@ -1072,6 +1093,9 @@ libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int c
|
|||
e.code = code;
|
||||
e.value = value;
|
||||
|
||||
if (sanitize_event(dev, &e))
|
||||
return -1;
|
||||
|
||||
switch(type) {
|
||||
case EV_ABS: rc = update_abs_state(dev, &e); break;
|
||||
case EV_KEY: rc = update_key_state(dev, &e); break;
|
||||
|
|
|
|||
|
|
@ -1111,6 +1111,10 @@ int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsi
|
|||
* event code is the value of the currently active slot. You should use
|
||||
* libevdev_set_slot_value() instead.
|
||||
*
|
||||
* If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is
|
||||
* ABS_MT_SLOT, the value must be a positive number less then the number of
|
||||
* slots on the device. Otherwise, libevdev_set_event_value() returns -1.
|
||||
*
|
||||
* @param dev The evdev device, already initialized with libevdev_set_fd()
|
||||
* @param type The event type for the code to query (EV_SYN, EV_REL, etc.)
|
||||
* @param code The event code to set the value for, one of ABS_X, LED_NUML, etc.
|
||||
|
|
@ -1118,7 +1122,8 @@ int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsi
|
|||
*
|
||||
* @return 0 on success, or -1 on failure.
|
||||
* @retval -1 the device does not have the event type or code enabled, or the code is outside the
|
||||
* allowed limits for the given type, or the type cannot be set.
|
||||
* allowed limits for the given type, or the type cannot be set, or the
|
||||
* value is not permitted for the given code.
|
||||
*
|
||||
* @see libevdev_set_slot_value
|
||||
* @see libevdev_get_event_value
|
||||
|
|
|
|||
|
|
@ -1143,6 +1143,83 @@ START_TEST(test_mt_event_values_invalid)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_mt_slot_ranges_invalid)
|
||||
{
|
||||
struct uinput_device* uidev;
|
||||
struct libevdev *dev;
|
||||
struct input_event ev[2];
|
||||
int rc;
|
||||
struct input_absinfo abs[5];
|
||||
int num_slots = 2;
|
||||
int pipefd[2];
|
||||
|
||||
memset(abs, 0, sizeof(abs));
|
||||
abs[0].value = ABS_X;
|
||||
abs[0].maximum = 1000;
|
||||
abs[1].value = ABS_MT_POSITION_X;
|
||||
abs[1].maximum = 1000;
|
||||
|
||||
abs[2].value = ABS_Y;
|
||||
abs[2].maximum = 1000;
|
||||
abs[3].value = ABS_MT_POSITION_Y;
|
||||
abs[3].maximum = 1000;
|
||||
|
||||
abs[4].value = ABS_MT_SLOT;
|
||||
abs[4].maximum = num_slots - 1;
|
||||
|
||||
rc = test_create_abs_device(&uidev, &dev,
|
||||
5, abs,
|
||||
EV_SYN, SYN_REPORT,
|
||||
-1);
|
||||
ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
|
||||
|
||||
rc = pipe2(pipefd, O_NONBLOCK);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
libevdev_change_fd(dev, pipefd[0]);
|
||||
|
||||
ev[0].type = EV_ABS;
|
||||
ev[0].code = ABS_MT_SLOT;
|
||||
ev[0].value = num_slots;
|
||||
ev[1].type = EV_SYN;
|
||||
ev[1].code = SYN_REPORT;
|
||||
ev[1].value = 0;
|
||||
rc = write(pipefd[1], ev, sizeof(ev));
|
||||
ck_assert_int_eq(rc, sizeof(ev));
|
||||
|
||||
libevdev_set_log_function(test_logfunc_ignore_error, NULL);
|
||||
|
||||
rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
|
||||
ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
|
||||
ck_assert_int_eq(ev[0].value, num_slots - 1);
|
||||
|
||||
/* drain the EV_SYN */
|
||||
libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
|
||||
|
||||
ev[0].type = EV_ABS;
|
||||
ev[0].code = ABS_MT_SLOT;
|
||||
ev[0].value = -1;
|
||||
ev[1].type = EV_SYN;
|
||||
ev[1].code = SYN_REPORT;
|
||||
ev[1].value = 0;
|
||||
rc = write(pipefd[1], ev, sizeof(ev));
|
||||
ck_assert_int_eq(rc, sizeof(ev));
|
||||
|
||||
rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev);
|
||||
ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT));
|
||||
ck_assert_int_eq(ev[0].value, num_slots - 1);
|
||||
|
||||
ck_assert_int_eq(libevdev_get_current_slot(dev), num_slots - 1);
|
||||
|
||||
ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, num_slots), -1);
|
||||
ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, -1), -1);
|
||||
|
||||
libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
|
||||
|
||||
uinput_device_free(uidev);
|
||||
libevdev_free(dev);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_ev_rep_values)
|
||||
{
|
||||
struct uinput_device* uidev;
|
||||
|
|
@ -1447,6 +1524,7 @@ libevdev_events(void)
|
|||
tcase_add_test(tc, test_event_values_invalid);
|
||||
tcase_add_test(tc, test_mt_event_values);
|
||||
tcase_add_test(tc, test_mt_event_values_invalid);
|
||||
tcase_add_test(tc, test_mt_slot_ranges_invalid);
|
||||
tcase_add_test(tc, test_ev_rep_values);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue