diff options
-rw-r--r-- | src/evdev.c | 15 | ||||
-rw-r--r-- | test/misc.c | 83 |
2 files changed, 98 insertions, 0 deletions
diff --git a/src/evdev.c b/src/evdev.c index 9fecdc4c..37080725 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -2202,6 +2202,17 @@ evdev_set_device_group(struct evdev_device *device, return 0; } +static inline void +evdev_drain_fd(int fd) +{ + struct input_event ev[24]; + size_t sz = sizeof ev; + + while (read(fd, &ev, sz) == (int)sz) { + /* discard all pending events */ + } +} + struct evdev_device * evdev_device_create(struct libinput_seat *seat, struct udev_device *udev_device) @@ -2235,6 +2246,8 @@ evdev_device_create(struct libinput_seat *seat, libinput_device_init(&device->base, seat); libinput_seat_ref(seat); + evdev_drain_fd(fd); + rc = libevdev_new_from_fd(fd, &device->evdev); if (rc != 0) goto err; @@ -2682,6 +2695,8 @@ evdev_device_resume(struct evdev_device *device) return -ENODEV; } + evdev_drain_fd(fd); + device->fd = fd; if (evdev_need_mtdev(device)) { diff --git a/test/misc.c b/test/misc.c index 89edb145..b962cc5a 100644 --- a/test/misc.c +++ b/test/misc.c @@ -693,6 +693,87 @@ START_TEST(time_conversion) } END_TEST +static int open_restricted_leak(const char *path, int flags, void *data) +{ + return *(int*)data; +} + +static void close_restricted_leak(int fd, void *data) +{ + /* noop */ +} + +const struct libinput_interface leak_interface = { + .open_restricted = open_restricted_leak, + .close_restricted = close_restricted_leak, +}; + +static void +simple_log_handler(struct libinput *libinput, + enum libinput_log_priority priority, + const char *format, + va_list args) +{ + vfprintf(stderr, format, args); +} + +START_TEST(fd_no_event_leak) +{ + struct libevdev_uinput *uinput; + struct libinput *li; + struct libinput_device *device; + int fd = -1; + const char *path; + struct libinput_event *event; + + uinput = create_simple_test_device("litest test device", + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_LEFT, + -1, -1); + path = libevdev_uinput_get_devnode(uinput); + + fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC); + ck_assert_int_gt(fd, -1); + + li = libinput_path_create_context(&leak_interface, &fd); + libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG); + libinput_log_set_handler(li, simple_log_handler); + + /* Add the device, trigger an event, then remove it again. + * Without it, we get a SYN_DROPPED immediately and no events. + */ + device = libinput_path_add_device(li, path); + libevdev_uinput_write_event(uinput, EV_REL, REL_X, 1); + libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); + libinput_path_remove_device(device); + libinput_dispatch(li); + litest_drain_events(li); + + /* Device is removed, but fd is still open. Queue an event, add a + * new device with the same fd, the queued event must be discarded + * by libinput */ + libevdev_uinput_write_event(uinput, EV_REL, REL_Y, 1); + libevdev_uinput_write_event(uinput, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + + libinput_path_add_device(li, path); + libinput_dispatch(li); + event = libinput_get_event(li); + ck_assert_int_eq(libinput_event_get_type(event), + LIBINPUT_EVENT_DEVICE_ADDED); + libinput_event_destroy(event); + + litest_assert_empty_queue(li); + + close(fd); + libinput_unref(li); + libevdev_uinput_destroy(uinput); +} +END_TEST + void litest_setup_tests(void) { @@ -714,4 +795,6 @@ litest_setup_tests(void) litest_add_no_device("misc:parser", trackpoint_accel_parser); litest_add_no_device("misc:parser", dimension_prop_parser); litest_add_no_device("misc:time", time_conversion); + + litest_add_no_device("misc:fd", fd_no_event_leak); } |