evdev: reject devices with a min == max axis range

Except for a few axes where this may be correct, a min == max axis range
indicates a broken kernel driver. To avoid potential divisions by zero when
scaling this axis later, reject such a device outright.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
Peter Hutterer 2015-03-23 12:08:14 +10:00
parent ee376b0b56
commit 0e262e55bc
2 changed files with 116 additions and 0 deletions

View file

@ -1452,10 +1452,32 @@ evdev_fix_android_mt(struct evdev_device *device)
libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y));
}
static inline int
evdev_check_min_max(struct evdev_device *device, unsigned int code)
{
struct libevdev *evdev = device->evdev;
const struct input_absinfo *absinfo;
if (!libevdev_has_event_code(evdev, EV_ABS, code))
return 0;
absinfo = libevdev_get_abs_info(evdev, code);
if (absinfo->minimum == absinfo->maximum) {
log_bug_kernel(device->base.seat->libinput,
"Device '%s' has min == max on %s\n",
device->devname,
libevdev_event_code_get_name(EV_ABS, code));
return -1;
}
return 0;
}
static int
evdev_reject_device(struct evdev_device *device)
{
struct libevdev *evdev = device->evdev;
unsigned int code;
if (libevdev_has_event_code(evdev, EV_ABS, ABS_X) ^
libevdev_has_event_code(evdev, EV_ABS, ABS_Y))
@ -1465,6 +1487,18 @@ evdev_reject_device(struct evdev_device *device)
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
return -1;
for (code = 0; code < ABS_CNT; code++) {
switch (code) {
case ABS_MISC:
case ABS_MT_SLOT:
case ABS_MT_TOOL_TYPE:
break;
default:
if (evdev_check_min_max(device, code) == -1)
return -1;
}
}
return 0;
}

View file

@ -814,6 +814,86 @@ START_TEST(abs_mt_device_no_absx)
}
END_TEST
START_TEST(abs_device_no_range)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
int code;
/* set x/y so libinput doesn't just reject for missing axes */
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 0 },
{ ABS_Y, 0, 10, 0, 0, 0 },
{ -1, 0, 0, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
for (code = 0; code < ABS_MT_SLOT; code++) {
if (code == ABS_MISC)
continue;
absinfo[2].value = code;
uinput = litest_create_uinput_abs_device("test device", NULL,
absinfo,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
-1);
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device == NULL);
libevdev_uinput_destroy(uinput);
}
litest_restore_log_handler(li);
libinput_unref(li);
}
END_TEST
START_TEST(abs_mt_device_no_range)
{
struct libevdev_uinput *uinput;
struct libinput *li;
struct libinput_device *device;
int code;
/* set x/y so libinput doesn't just reject for missing axes */
struct input_absinfo absinfo[] = {
{ ABS_X, 0, 10, 0, 0, 0 },
{ ABS_Y, 0, 10, 0, 0, 0 },
{ ABS_MT_SLOT, 0, 10, 0, 0, 0 },
{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
{ ABS_MT_POSITION_X, 0, 10, 0, 0, 0 },
{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
{ -1, 0, 0, 0, 0, 0 },
{ -1, -1, -1, -1, -1, -1 }
};
li = litest_create_context();
litest_disable_log_handler(li);
for (code = ABS_MT_SLOT + 1; code < ABS_CNT; code++) {
if (code == ABS_MT_TOOL_TYPE ||
code == ABS_MT_TRACKING_ID) /* kernel overrides it */
continue;
absinfo[6].value = code;
uinput = litest_create_uinput_abs_device("test device", NULL,
absinfo,
EV_KEY, BTN_LEFT,
EV_KEY, BTN_RIGHT,
-1);
device = libinput_path_add_device(li,
libevdev_uinput_get_devnode(uinput));
ck_assert(device == NULL);
libevdev_uinput_destroy(uinput);
}
litest_restore_log_handler(li);
libinput_unref(li);
}
END_TEST
int main (int argc, char **argv)
{
litest_add("device:sendevents", device_sendevents_config, LITEST_ANY, LITEST_TOUCHPAD);
@ -846,6 +926,8 @@ int main (int argc, char **argv)
litest_add_no_device("device:invalid devices", abs_device_no_absy);
litest_add_no_device("device:invalid devices", abs_mt_device_no_absx);
litest_add_no_device("device:invalid devices", abs_mt_device_no_absy);
litest_add_no_device("device:invalid devices", abs_device_no_range);
litest_add_no_device("device:invalid devices", abs_mt_device_no_range);
return litest_run(argc, argv);
}