Fix an abort if the device speed is NaN
authorOlivier Fourdan <ofourdan@redhat.com>
Thu, 5 Feb 2015 13:33:31 +0000 (14:33 +0100)
committerPeter Hutterer <peter.hutterer@who-t.net>
Fri, 6 Feb 2015 00:26:04 +0000 (10:26 +1000)
When using libinput with xf86-input-libinput, the device speed is
represented as a float passed via X properties.

If a buggy client gives a broken value, the conversions that occur
can cause the value of speed to be NaN (not a number), aka infinity.

In C, any comparison with NaN always gives false, whatever the value.

So that test in libinput_device_config_accel_set_speed():

   (speed < 1.0 || speed > 1.0)

will necessarily return FALSE, defeating the test of range.

However, since since any comparison with NaN is false, the
opposite assert() in accelerator_set_speed():

   (speed >= 1.0 && speed <= 1.0)

will be false as well, thus triggering the abort() and the crash of
the entire X server along with it.

The solution is to use the same construct in both routines, so that
it fails gracefully in libinput_device_config_accel_set_speed().

Signed-off-by: Olivier Fourdan <ofourdan@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
src/libinput.c
test/pointer.c

index 7456b90..ba60c13 100644 (file)
@@ -1534,7 +1534,8 @@ LIBINPUT_EXPORT enum libinput_config_status
 libinput_device_config_accel_set_speed(struct libinput_device *device,
                                       double speed)
 {
-       if (speed < -1.0 || speed > 1.0)
+       /* Need the negation in case speed is NaN */
+       if (!(speed >= -1.0 && speed <= 1.0))
                return LIBINPUT_CONFIG_STATUS_INVALID;
 
        if (!libinput_device_config_accel_is_available(device))
index 9a1e9d6..2e52a20 100644 (file)
@@ -770,6 +770,23 @@ START_TEST(pointer_accel_defaults)
 }
 END_TEST
 
+START_TEST(pointer_accel_invalid)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput_device *device = dev->libinput_device;
+       enum libinput_config_status status;
+
+       ck_assert(libinput_device_config_accel_is_available(device));
+
+       status = libinput_device_config_accel_set_speed(device,
+                                                       NAN);
+       ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
+       status = libinput_device_config_accel_set_speed(device,
+                                                       INFINITY);
+       ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
+}
+END_TEST
+
 START_TEST(pointer_accel_defaults_absolute)
 {
        struct litest_device *dev = litest_current_device();
@@ -818,6 +835,7 @@ int main (int argc, char **argv) {
        litest_add("pointer:left-handed", pointer_left_handed_during_click_multiple_buttons, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
 
        litest_add("pointer:accel", pointer_accel_defaults, LITEST_RELATIVE, LITEST_ANY);
+       litest_add("pointer:accel", pointer_accel_invalid, LITEST_RELATIVE, LITEST_ANY);
        litest_add("pointer:accel", pointer_accel_defaults_absolute, LITEST_ABSOLUTE, LITEST_ANY);
 
        return litest_run(argc, argv);