From 04a35384e6bd6a8d6d1f6c4c3bad7c79ae7ad163 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 3 Jun 2026 14:15:41 +1000 Subject: [PATCH] evdev: be stricter about devices with odd absinfo values Reject devices that have extreme min/max values (might cause integer overflow in libinput), negative resolutions and a min > max. The former two could be triggered by malicious input devices. Part-of: --- src/evdev.c | 22 +++++++++++++ test/test-device.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/evdev.c b/src/evdev.c index cac971f4..0043b11f 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1671,6 +1671,16 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code) return true; absinfo = libevdev_get_abs_info(evdev, code); + + if (((uint64_t)absinfo->maximum - (uint64_t)absinfo->minimum) > INT32_MAX / 2) { + evdev_log_bug_kernel(device, + "kernel axis range [%d, %d] on %s too extreme\n", + absinfo->minimum, + absinfo->maximum, + libevdev_event_code_get_name(EV_ABS, code)); + return false; + } + if (absinfo->minimum == absinfo->maximum) { /* Some devices have a sort-of legitimate min/max of 0 for * ABS_MISC and above (e.g. Roccat Kone XTD). Don't ignore @@ -1690,6 +1700,18 @@ evdev_check_min_max(struct evdev_device *device, unsigned int code) libevdev_event_code_get_name(EV_ABS, code)); return false; } + } else if (absinfo->minimum > absinfo->maximum) { + evdev_log_bug_kernel(device, + "device has min > max on %s\n", + libevdev_event_code_get_name(EV_ABS, code)); + return false; + } + + if (absinfo->resolution < 0) { + evdev_log_bug_kernel(device, + "kernel resolution is negative on %s\n", + libevdev_event_code_get_name(EV_ABS, code)); + return false; } return true; diff --git a/test/test-device.c b/test/test-device.c index 660754fe..6d4501f4 100644 --- a/test/test-device.c +++ b/test/test-device.c @@ -1656,6 +1656,77 @@ START_TEST(device_button_down_remove) } END_TEST +enum extreme_axis_range_idx { + EXTREME_RANGE_0_TO_INT32MAX, + EXTREME_RANGE_INT32MIN_TO_0, + EXTREME_RANGE_NEGHALF_TO_POSHALF, +}; + +START_TEST(abs_device_extreme_axis_range) +{ + struct libevdev_uinput *uinput; + struct libinput_device *device; + int idx = litest_test_param_get_i32(test_env->params, "range"); + /* All ranges exceed INT32_MAX/2, device should be rejected */ + const struct { + int32_t min, max; + } ranges[] = { + [EXTREME_RANGE_0_TO_INT32MAX] = { 0, INT32_MAX }, + [EXTREME_RANGE_INT32MIN_TO_0] = { INT32_MIN, 0 }, + [EXTREME_RANGE_NEGHALF_TO_POSHALF] = { -(INT32_MAX / 2), + INT32_MAX / 2 }, + }; + struct input_absinfo absinfo[] = { + { ABS_X, ranges[idx].min, ranges[idx].max, 0, 0, 0 }, + { ABS_Y, 0, 1000, 0, 0, 0 }, + { -1, -1, -1, -1, -1, -1 }, + }; + + _litest_context_destroy_ struct libinput *li = litest_create_context(); + litest_disable_log_handler(li); + /* clang-format off */ + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + /* clang-format on */ + device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); + litest_restore_log_handler(li); + litest_assert_ptr_null(device); + + libevdev_uinput_destroy(uinput); +} +END_TEST + +START_TEST(abs_device_negative_resolution) +{ + struct libevdev_uinput *uinput; + struct libinput_device *device; + struct input_absinfo absinfo[] = { + { ABS_X, 0, 1000, 0, 0, -1 }, /* negative resolution */ + { ABS_Y, 0, 1000, 0, 0, -1 }, /* negative resolution */ + { -1, -1, -1, -1, -1, -1 }, + }; + + _litest_context_destroy_ struct libinput *li = litest_create_context(); + litest_disable_log_handler(li); + /* clang-format off */ + uinput = litest_create_uinput_abs_device("test device", NULL, + absinfo, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_RIGHT, + -1); + /* clang-format on */ + device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput)); + litest_restore_log_handler(li); + /* Device should be rejected */ + litest_assert_ptr_null(device); + + libevdev_uinput_destroy(uinput); +} +END_TEST + TEST_COLLECTION(device) { /* clang-format off */ @@ -1713,6 +1784,13 @@ TEST_COLLECTION(device) litest_add(device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE|LITEST_TABLET); litest_add_no_device(device_accelerometer); + litest_with_parameters(params, "range", 'I', 3, + litest_named_i32(EXTREME_RANGE_0_TO_INT32MAX, "0-to-INT32MAX"), + litest_named_i32(EXTREME_RANGE_INT32MIN_TO_0, "INT32MIN-to-0"), + litest_named_i32(EXTREME_RANGE_NEGHALF_TO_POSHALF, "-INT32MAX/2-to-INT32MAX/2")) { + litest_add_parametrized_no_device(abs_device_extreme_axis_range, params); + } + litest_add_no_device(abs_device_negative_resolution); litest_add(device_udev_tag_wacom_tablet, LITEST_TABLET, LITEST_TOTEM);