timer: always restart the timer loop when we called one of them
authorPeter Hutterer <peter.hutterer@who-t.net>
Mon, 17 Jul 2017 02:30:25 +0000 (12:30 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Wed, 6 Sep 2017 09:38:49 +0000 (19:38 +1000)
If a timer_func causes the removal or addition of a different timer, our tmp
pointer from the list_for_each_safe may not be valid anymore.

This was triggered by having the debounce code trigger a middle button state
change, which caused that timer to be cancelled.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
(cherry picked from commit 6d0edf9d07379082d9a2c66c71e929ad510b29d2)

src/timer.c

index 7a0a8ebf8704bebfaf5bfacb288154d95ac43efc..d7e04baeb65de323554538f4836a56102132a5fd 100644 (file)
@@ -116,7 +116,7 @@ static void
 libinput_timer_handler(void *data)
 {
        struct libinput *libinput = data;
-       struct libinput_timer *timer, *tmp;
+       struct libinput_timer *timer;
        uint64_t now;
        uint64_t discard;
        int r;
@@ -132,7 +132,8 @@ libinput_timer_handler(void *data)
        if (now == 0)
                return;
 
-       list_for_each_safe(timer, tmp, &libinput->timer.list, link) {
+restart:
+       list_for_each(timer, &libinput->timer.list, link) {
                if (timer->expire == 0)
                        continue;
 
@@ -141,6 +142,16 @@ libinput_timer_handler(void *data)
                           as timer_func may re-arm it */
                        libinput_timer_cancel(timer);
                        timer->timer_func(now, timer->timer_func_data);
+
+                       /*
+                        * Restart the loop. We can't use
+                        * list_for_each_safe() here because that only
+                        * allows removing one (our) timer per timer_func.
+                        * But the timer func may trigger another unrelated
+                        * timer to be cancelled and removed, causing a
+                        * segfault.
+                        */
+                       goto restart;
                }
        }
 }