Commit 72bac84d authored by Peter Hutterer's avatar Peter Hutterer

If the parent libinput_device is unavailable, create a new one

The parent device ref's the libinput device during pre_init and unref's it
during DEVICE_INIT, so the copy is lost. During DEVICE_ON, the libinput device
is re-added and ref'd, this one stays around now. But the takeaway is: unless
the device is enabled, no libinput device reference is available.

If a device is a mixed pointer + keyboard device, a subdevice is created
during a WorkProc. The subdevice relied on the parent's libinput_device being
available and didn't even check for it. This WorkProc usually runs after
the parent's DEVICE_ON, so in most cases all is well.

But when running without logind and the server is vt-switched away, the parent
device only runs PreInit and DEVICE_INIT but never DEVICE_ON, causing the
subdevice to burn, crash, and generally fail horribly when it dereferences the
parent's libinput device.

Fix this because we have global warming already and don't need to burn more
things and also because it's considered bad user experience to have the
server crash. The simple fix is to check the parent device first and if it is
unavailable, create a new one because it will end up disabled as well anyway,
so the ref goes away as well. The use-case where the parent somehow gets
disabled but the subdevice doesn't is a bit too niche to worry about.

This doesn't happen with logind because in that case we don't get a usable fd
while VT-switched away, so we can't even run PreInit and never get this far
(see the paused fd handling in the xfree86 code for that). It can be
reproduced by setting AutoEnableDevices off, but why would you do that,
seriously.

https://bugs.freedesktop.org/show_bug.cgi?id=97117Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
parent 0b073d90
......@@ -2850,7 +2850,7 @@ xf86libinput_pre_init(InputDriverPtr drv,
struct xf86libinput *driver_data = NULL;
struct xf86libinput_device *shared_device = NULL;
struct libinput *libinput = NULL;
struct libinput_device *device;
struct libinput_device *device = NULL;
char *path = NULL;
bool is_subdevice;
......@@ -2885,7 +2885,28 @@ xf86libinput_pre_init(InputDriverPtr drv,
}
is_subdevice = xf86libinput_is_subdevice(pInfo);
if (!is_subdevice) {
if (is_subdevice) {
InputInfoPtr parent;
struct xf86libinput *parent_driver_data;
parent = xf86libinput_get_parent(pInfo);
if (!parent) {
xf86IDrvMsg(pInfo, X_ERROR, "Failed to find parent device\n");
goto fail;
}
parent_driver_data = parent->private;
if (!parent_driver_data) /* parent already removed again */
goto fail;
xf86IDrvMsg(pInfo, X_INFO, "is a virtual subdevice\n");
shared_device = xf86libinput_shared_ref(parent_driver_data->shared_device);
device = shared_device->device;
if (!device)
xf86IDrvMsg(pInfo, X_ERROR, "Parent device not available\n");
}
if (!device) {
device = libinput_path_add_device(libinput, path);
if (!device) {
xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a device for %s\n", path);
......@@ -2903,23 +2924,6 @@ xf86libinput_pre_init(InputDriverPtr drv,
libinput_device_unref(device);
goto fail;
}
} else {
InputInfoPtr parent;
struct xf86libinput *parent_driver_data;
parent = xf86libinput_get_parent(pInfo);
if (!parent) {
xf86IDrvMsg(pInfo, X_ERROR, "Failed to find parent device\n");
goto fail;
}
parent_driver_data = parent->private;
if (!parent_driver_data) /* parent already removed again */
goto fail;
xf86IDrvMsg(pInfo, X_INFO, "is a virtual subdevice\n");
shared_device = xf86libinput_shared_ref(parent_driver_data->shared_device);
device = shared_device->device;
}
pInfo->private = driver_data;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment