diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c index a77c466..d9e2517 100644 --- a/libevdev/libevdev.c +++ b/libevdev/libevdev.c @@ -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; diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h index eab02ba..1855cd3 100644 --- a/libevdev/libevdev.h +++ b/libevdev/libevdev.h @@ -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 diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c index c9ceb33..8d1bd8b 100644 --- a/test/test-libevdev-events.c +++ b/test/test-libevdev-events.c @@ -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);