2d42f4
From 4c7a16b089b721b9c07f4ed593deba4f22158dbf Mon Sep 17 00:00:00 2001
2d42f4
From: Peter Hutterer <peter.hutterer@who-t.net>
2d42f4
Date: Mon, 18 Dec 2023 12:26:20 +1000
2d42f4
Subject: [PATCH xserver 2/2] dix: fix DeviceStateNotify event calculation
2d42f4
2d42f4
The previous code only made sense if one considers buttons and keys to
2d42f4
be mutually exclusive on a device. That is not necessarily true, causing
2d42f4
a number of issues.
2d42f4
2d42f4
This function allocates and fills in the number of xEvents we need to
2d42f4
send the device state down the wire.  This is split across multiple
2d42f4
32-byte devices including one deviceStateNotify event and optional
2d42f4
deviceKeyStateNotify, deviceButtonStateNotify and (possibly multiple)
2d42f4
deviceValuator events.
2d42f4
2d42f4
The previous behavior would instead compose a sequence
2d42f4
of [state, buttonstate, state, keystate, valuator...]. This is not
2d42f4
protocol correct, and on top of that made the code extremely convoluted.
2d42f4
2d42f4
Fix this by streamlining: add both button and key into the deviceStateNotify
2d42f4
and then append the key state and button state, followed by the
2d42f4
valuators. Finally, the deviceValuator events contain up to 6 valuators
2d42f4
per event but we only ever sent through 3 at a time. Let's double that
2d42f4
troughput.
2d42f4
2d42f4
CVE-2024-0229, ZDI-CAN-22678
2d42f4
2d42f4
This vulnerability was discovered by:
2d42f4
Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
2d42f4
---
2d42f4
 dix/enterleave.c | 119 ++++++++++++++++++++---------------------------
2d42f4
 1 file changed, 51 insertions(+), 68 deletions(-)
2d42f4
2d42f4
diff --git a/dix/enterleave.c b/dix/enterleave.c
2d42f4
index 17964b00a4..7b7ba1098b 100644
2d42f4
--- a/dix/enterleave.c
2d42f4
+++ b/dix/enterleave.c
2d42f4
@@ -615,9 +615,15 @@ FixDeviceValuator(DeviceIntPtr dev, deviceValuator * ev, ValuatorClassPtr v,
2d42f4
 
2d42f4
     ev->type = DeviceValuator;
2d42f4
     ev->deviceid = dev->id;
2d42f4
-    ev->num_valuators = nval < 3 ? nval : 3;
2d42f4
+    ev->num_valuators = nval < 6 ? nval : 6;
2d42f4
     ev->first_valuator = first;
2d42f4
     switch (ev->num_valuators) {
2d42f4
+    case 6:
2d42f4
+        ev->valuator5 = v->axisVal[first + 5];
2d42f4
+    case 5:
2d42f4
+        ev->valuator4 = v->axisVal[first + 4];
2d42f4
+    case 4:
2d42f4
+        ev->valuator3 = v->axisVal[first + 3];
2d42f4
     case 3:
2d42f4
         ev->valuator2 = v->axisVal[first + 2];
2d42f4
     case 2:
2d42f4
@@ -626,7 +632,6 @@ FixDeviceValuator(DeviceIntPtr dev, deviceValuator * ev, ValuatorClassPtr v,
2d42f4
         ev->valuator0 = v->axisVal[first];
2d42f4
         break;
2d42f4
     }
2d42f4
-    first += ev->num_valuators;
2d42f4
 }
2d42f4
 
2d42f4
 static void
2d42f4
@@ -646,7 +651,7 @@ FixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
2d42f4
         ev->num_buttons = b->numButtons;
2d42f4
         memcpy((char *) ev->buttons, (char *) b->down, 4);
2d42f4
     }
2d42f4
-    else if (k) {
2d42f4
+    if (k) {
2d42f4
         ev->classes_reported |= (1 << KeyClass);
2d42f4
         ev->num_keys = k->xkbInfo->desc->max_key_code -
2d42f4
             k->xkbInfo->desc->min_key_code;
2d42f4
@@ -670,15 +675,26 @@ FixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
2d42f4
     }
2d42f4
 }
2d42f4
 
2d42f4
-
2d42f4
+/**
2d42f4
+ * The device state notify event is split across multiple 32-byte events.
2d42f4
+ * The first one contains the first 32 button state bits, the first 32
2d42f4
+ * key state bits, and the first 3 valuator values.
2d42f4
+ *
2d42f4
+ * If a device has more than that, the server sends out:
2d42f4
+ * - one deviceButtonStateNotify for buttons 32 and above
2d42f4
+ * - one deviceKeyStateNotify for keys 32 and above
2d42f4
+ * - one deviceValuator event per 6 valuators above valuator 4
2d42f4
+ *
2d42f4
+ * All events but the last one have the deviceid binary ORed with MORE_EVENTS,
2d42f4
+ */
2d42f4
 static void
2d42f4
 DeliverStateNotifyEvent(DeviceIntPtr dev, WindowPtr win)
2d42f4
 {
2d42f4
+    /* deviceStateNotify, deviceKeyStateNotify, deviceButtonStateNotify
2d42f4
+     * and one deviceValuator for each 6 valuators */
2d42f4
+    deviceStateNotify sev[3 + (MAX_VALUATORS + 6)/6];
2d42f4
     int evcount = 1;
2d42f4
-    deviceStateNotify sev[6 + (MAX_VALUATORS + 2)/3];
2d42f4
-    deviceStateNotify *ev;
2d42f4
-    deviceKeyStateNotify *kev;
2d42f4
-    deviceButtonStateNotify *bev;
2d42f4
+    deviceStateNotify *ev = sev;
2d42f4
 
2d42f4
     KeyClassPtr k;
2d42f4
     ButtonClassPtr b;
2d42f4
@@ -691,82 +707,49 @@ DeliverStateNotifyEvent(DeviceIntPtr dev, WindowPtr win)
2d42f4
 
2d42f4
     if ((b = dev->button) != NULL) {
2d42f4
         nbuttons = b->numButtons;
2d42f4
-        if (nbuttons > 32)
2d42f4
+        if (nbuttons > 32) /* first 32 are encoded in deviceStateNotify */
2d42f4
             evcount++;
2d42f4
     }
2d42f4
     if ((k = dev->key) != NULL) {
2d42f4
         nkeys = k->xkbInfo->desc->max_key_code - k->xkbInfo->desc->min_key_code;
2d42f4
-        if (nkeys > 32)
2d42f4
+        if (nkeys > 32) /* first 32 are encoded in deviceStateNotify */
2d42f4
             evcount++;
2d42f4
-        if (nbuttons > 0) {
2d42f4
-            evcount++;
2d42f4
-        }
2d42f4
     }
2d42f4
     if ((v = dev->valuator) != NULL) {
2d42f4
         nval = v->numAxes;
2d42f4
-
2d42f4
-        if (nval > 3)
2d42f4
-            evcount++;
2d42f4
-        if (nval > 6) {
2d42f4
-            if (!(k && b))
2d42f4
-                evcount++;
2d42f4
-            if (nval > 9)
2d42f4
-                evcount += ((nval - 7) / 3);
2d42f4
-        }
2d42f4
+        /* first three are encoded in deviceStateNotify, then
2d42f4
+         * it's 6 per deviceValuator event */
2d42f4
+        evcount += ((nval - 3) + 6)/6;
2d42f4
     }
2d42f4
 
2d42f4
-    ev = sev;
2d42f4
-    FixDeviceStateNotify(dev, ev, NULL, NULL, NULL, first);
2d42f4
+    BUG_RETURN(evcount <= ARRAY_SIZE(sev));
2d42f4
 
2d42f4
-    if (b != NULL) {
2d42f4
-        FixDeviceStateNotify(dev, ev++, NULL, b, v, first);
2d42f4
-        first += 3;
2d42f4
-        nval -= 3;
2d42f4
-        if (nbuttons > 32) {
2d42f4
-            (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
-            bev = (deviceButtonStateNotify *) ev++;
2d42f4
-            bev->type = DeviceButtonStateNotify;
2d42f4
-            bev->deviceid = dev->id;
2d42f4
-            memcpy((char *) &bev->buttons[4], (char *) &b->down[4],
2d42f4
-                   DOWN_LENGTH - 4);
2d42f4
-        }
2d42f4
-        if (nval > 0) {
2d42f4
-            (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
-            FixDeviceValuator(dev, (deviceValuator *) ev++, v, first);
2d42f4
-            first += 3;
2d42f4
-            nval -= 3;
2d42f4
-        }
2d42f4
+    FixDeviceStateNotify(dev, ev, k, b, v, first);
2d42f4
+
2d42f4
+    if (b != NULL && nbuttons > 32) {
2d42f4
+        deviceButtonStateNotify *bev = (deviceButtonStateNotify *) ++ev;
2d42f4
+        (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
+        bev->type = DeviceButtonStateNotify;
2d42f4
+        bev->deviceid = dev->id;
2d42f4
+        memcpy((char *) &bev->buttons[4], (char *) &b->down[4],
2d42f4
+               DOWN_LENGTH - 4);
2d42f4
     }
2d42f4
 
2d42f4
-    if (k != NULL) {
2d42f4
-        FixDeviceStateNotify(dev, ev++, k, NULL, v, first);
2d42f4
-        first += 3;
2d42f4
-        nval -= 3;
2d42f4
-        if (nkeys > 32) {
2d42f4
-            (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
-            kev = (deviceKeyStateNotify *) ev++;
2d42f4
-            kev->type = DeviceKeyStateNotify;
2d42f4
-            kev->deviceid = dev->id;
2d42f4
-            memmove((char *) &kev->keys[0], (char *) &k->down[4], 28);
2d42f4
-        }
2d42f4
-        if (nval > 0) {
2d42f4
-            (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
-            FixDeviceValuator(dev, (deviceValuator *) ev++, v, first);
2d42f4
-            first += 3;
2d42f4
-            nval -= 3;
2d42f4
-        }
2d42f4
+    if (k != NULL && nkeys > 32) {
2d42f4
+        deviceKeyStateNotify *kev = (deviceKeyStateNotify *) ++ev;
2d42f4
+        (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
+        kev->type = DeviceKeyStateNotify;
2d42f4
+        kev->deviceid = dev->id;
2d42f4
+        memmove((char *) &kev->keys[0], (char *) &k->down[4], 28);
2d42f4
     }
2d42f4
 
2d42f4
+    first = 3;
2d42f4
+    nval -= 3;
2d42f4
     while (nval > 0) {
2d42f4
-        FixDeviceStateNotify(dev, ev++, NULL, NULL, v, first);
2d42f4
-        first += 3;
2d42f4
-        nval -= 3;
2d42f4
-        if (nval > 0) {
2d42f4
-            (ev - 1)->deviceid |= MORE_EVENTS;
2d42f4
-            FixDeviceValuator(dev, (deviceValuator *) ev++, v, first);
2d42f4
-            first += 3;
2d42f4
-            nval -= 3;
2d42f4
-        }
2d42f4
+        ev->deviceid |= MORE_EVENTS;
2d42f4
+        FixDeviceValuator(dev, (deviceValuator *) ++ev, v, first);
2d42f4
+        first += 6;
2d42f4
+        nval -= 6;
2d42f4
     }
2d42f4
 
2d42f4
     DeliverEventsToWindow(dev, win, (xEvent *) sev, evcount,
2d42f4
-- 
2d42f4
2.43.0
2d42f4