Xi: do not keep linked list pointer during recursion

The `DisableDevice()` function is called whenever an enabled device
is disabled and it moves the device from the `inputInfo.devices` linked
list to the `inputInfo.off_devices` linked list.

However, its link/unlink operation has an issue during the recursive
call to `DisableDevice()` due to the `prev` pointer pointing to a
removed device.

This issue leads to a length mismatch between the total number of
devices and the number of device in the list, leading to a heap
overflow and, possibly, to local privilege escalation.

Simplify the code that checked whether the device passed to
`DisableDevice()` was in `inputInfo.devices` or not and find the
previous device after the recursion.

CVE-2024-21886, ZDI-CAN-22840

This vulnerability was discovered by:
Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
This commit is contained in:
José Expósito 2023-12-22 18:28:31 +01:00 committed by José Expósito
parent 4a5e9b1895
commit bc1fdbe465

View File

@ -453,14 +453,20 @@ DisableDevice(DeviceIntPtr dev, BOOL sendevent)
{
DeviceIntPtr *prev, other;
BOOL enabled;
BOOL dev_in_devices_list = FALSE;
int flags[MAXDEVICES] = { 0 };
if (!dev->enabled)
return TRUE;
for (prev = &inputInfo.devices;
*prev && (*prev != dev); prev = &(*prev)->next);
if (*prev != dev)
for (other = inputInfo.devices; other; other = other->next) {
if (other == dev) {
dev_in_devices_list = TRUE;
break;
}
}
if (!dev_in_devices_list)
return FALSE;
TouchEndPhysicallyActiveTouches(dev);
@ -511,6 +517,9 @@ DisableDevice(DeviceIntPtr dev, BOOL sendevent)
LeaveWindow(dev);
SetFocusOut(dev);
for (prev = &inputInfo.devices;
*prev && (*prev != dev); prev = &(*prev)->next);
*prev = dev->next;
dev->next = inputInfo.off_devices;
inputInfo.off_devices = dev;