Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libXi for openSUSE:Factory checked 
in at 2026-09-28 10:34:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libXi (Old)
 and      /work/SRC/openSUSE:Factory/.libXi.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libXi"

Mon Sep 28 10:34:54 2026 rev:24 rq:1379999 version:1.8.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/libXi/libXi.changes      2026-05-20 
15:23:40.740059879 +0200
+++ /work/SRC/openSUSE:Factory/.libXi.new.383539/libXi.changes  2026-09-28 
10:34:57.798076500 +0200
@@ -1,0 +2,19 @@
+Wed Sep 23 16:16:29 UTC 2026 - Stefan Dirsch <[email protected]>
+
+- 
0001-boo1281605_CVE-2026-93541_XQueryDeviceState-check-ValuatorClass-num_valuators-.patch
+  * Out-of-bounds read in libXi's XQueryDeviceState() (boo#1281605, 
CVE-2026-93541)
+- 
0002-boo1281606_CVE-2026-93542_size_classes-copy_classes-bound-XI2-class-lengths-to.patch
+  * Out-of-bounds read in libXi's XI2 class parsing via size_classes() and 
copy_classes()
+    (boo#1281606, CVE-2026-93542)
+- 
0003-boo1281608_CVE-2026-93543_size_classes-copy_classes-enforce-XI2-per-type-class.patch
+  * Out-of-bounds read in libXi's XI2 class parser (boo#1281608, 
CVE-2026-93543)
+- 
0004-boo1281609_CVE-2026-93544_XIQueryDevice-keep-padded-name-and-class-bytes-withi.patch
+  * Out-of-bounds read in libXi's XI2 XIQueryDevice reply parsing 
(boo#1281609, CVE-2026-93544)
+- 
0005-boo1281612_CVE-2026-93545_XListInputDevices-validate-device-name-lengths-again.patch
+  * Out-of-bounds read in libXi's XListInputDevices() (boo#1281612, 
CVE-2026-93545)
+- 
0006-boo1281615_CVE-2026-94281_XListInputDevices-validate-class-lengths-cumulativel.patch
+  * Out-of-bounds read in libXi's XListInputDevices() class parsing 
(boo#1281615, CVE-2026-94281)
+- 
0007-boo1281651_CVE-2026-94282_wireToEnterLeave-validate-buttons_len-against-the-re.patch
+  * Out-of-bounds read in libXi's XI2 enter/leave/focus cookie conversio 
(boo#1281651, CVE-2026-94282)
+
+-------------------------------------------------------------------

New:
----
  
0001-boo1281605_CVE-2026-93541_XQueryDeviceState-check-ValuatorClass-num_valuators-.patch
  
0002-boo1281606_CVE-2026-93542_size_classes-copy_classes-bound-XI2-class-lengths-to.patch
  
0003-boo1281608_CVE-2026-93543_size_classes-copy_classes-enforce-XI2-per-type-class.patch
  
0004-boo1281609_CVE-2026-93544_XIQueryDevice-keep-padded-name-and-class-bytes-withi.patch
  
0005-boo1281612_CVE-2026-93545_XListInputDevices-validate-device-name-lengths-again.patch
  
0006-boo1281615_CVE-2026-94281_XListInputDevices-validate-class-lengths-cumulativel.patch
  
0007-boo1281651_CVE-2026-94282_wireToEnterLeave-validate-buttons_len-against-the-re.patch

----------(New B)----------
  New:
- 
0001-boo1281605_CVE-2026-93541_XQueryDeviceState-check-ValuatorClass-num_valuators-.patch
  * Out-of-bounds read in libXi's XQueryDeviceState() (boo#1281605, 
CVE-2026-93541)
  New:  * Out-of-bounds read in libXi's XQueryDeviceState() (boo#1281605, 
CVE-2026-93541)
- 
0002-boo1281606_CVE-2026-93542_size_classes-copy_classes-bound-XI2-class-lengths-to.patch
  * Out-of-bounds read in libXi's XI2 class parsing via size_classes() and 
copy_classes()
  New:    (boo#1281606, CVE-2026-93542)
- 
0003-boo1281608_CVE-2026-93543_size_classes-copy_classes-enforce-XI2-per-type-class.patch
  * Out-of-bounds read in libXi's XI2 class parser (boo#1281608, CVE-2026-93543)
  New:  * Out-of-bounds read in libXi's XI2 class parser (boo#1281608, 
CVE-2026-93543)
- 
0004-boo1281609_CVE-2026-93544_XIQueryDevice-keep-padded-name-and-class-bytes-withi.patch
  * Out-of-bounds read in libXi's XI2 XIQueryDevice reply parsing (boo#1281609, 
CVE-2026-93544)
  New:  * Out-of-bounds read in libXi's XI2 XIQueryDevice reply parsing 
(boo#1281609, CVE-2026-93544)
- 
0005-boo1281612_CVE-2026-93545_XListInputDevices-validate-device-name-lengths-again.patch
  * Out-of-bounds read in libXi's XListInputDevices() (boo#1281612, 
CVE-2026-93545)
  New:  * Out-of-bounds read in libXi's XListInputDevices() (boo#1281612, 
CVE-2026-93545)
- 
0006-boo1281615_CVE-2026-94281_XListInputDevices-validate-class-lengths-cumulativel.patch
  * Out-of-bounds read in libXi's XListInputDevices() class parsing 
(boo#1281615, CVE-2026-94281)
  New:  * Out-of-bounds read in libXi's XListInputDevices() class parsing 
(boo#1281615, CVE-2026-94281)
- 
0007-boo1281651_CVE-2026-94282_wireToEnterLeave-validate-buttons_len-against-the-re.patch
  * Out-of-bounds read in libXi's XI2 enter/leave/focus cookie conversio 
(boo#1281651, CVE-2026-94282)
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libXi.spec ++++++
--- /var/tmp/diff_new_pack.foCBgo/_old  2026-09-28 10:34:58.343099335 +0200
+++ /var/tmp/diff_new_pack.foCBgo/_new  2026-09-28 10:34:58.344099377 +0200
@@ -29,6 +29,13 @@
 #Git-Web:      http://cgit.freedesktop.org/xorg/lib/libXi/
 Source:         
http://xorg.freedesktop.org/releases/individual/lib/%{name}-%{version}.tar.xz
 Source1:        baselibs.conf
+Patch1:         
0001-boo1281605_CVE-2026-93541_XQueryDeviceState-check-ValuatorClass-num_valuators-.patch
+Patch2:         
0002-boo1281606_CVE-2026-93542_size_classes-copy_classes-bound-XI2-class-lengths-to.patch
+Patch3:         
0003-boo1281608_CVE-2026-93543_size_classes-copy_classes-enforce-XI2-per-type-class.patch
+Patch4:         
0004-boo1281609_CVE-2026-93544_XIQueryDevice-keep-padded-name-and-class-bytes-withi.patch
+Patch5:         
0005-boo1281612_CVE-2026-93545_XListInputDevices-validate-device-name-lengths-again.patch
+Patch6:         
0006-boo1281615_CVE-2026-94281_XListInputDevices-validate-class-lengths-cumulativel.patch
+Patch7:         
0007-boo1281651_CVE-2026-94282_wireToEnterLeave-validate-buttons_len-against-the-re.patch
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 #git#BuildRequires:    autoconf >= 2.60, automake, libtool
 BuildRequires:  fdupes
@@ -66,7 +73,7 @@
 in %lname.
 
 %prep
-%setup -q
+%autosetup -p1
 
 %build
 %configure --docdir=%_docdir/%name --disable-static

++++++ 
0001-boo1281605_CVE-2026-93541_XQueryDeviceState-check-ValuatorClass-num_valuators-.patch
 ++++++
>From 7b6fffd13fd3914e0b39f3a4f131913da7f066e7 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 31 Jul 2026 15:05:27 +0200
Subject: [PATCH 1/7] XQueryDeviceState: check ValuatorClass num_valuators
 against class length

The first pass only verified that the xValuatorState header fit in the
reply. num_valuators was then used to size the destination and to copy
valuator words from (v + 1) without ensuring those words fit in
any->length.

A forged QueryDeviceState reply with length equal to the size of
xValuatorState and a large num_valuators could therefore read past
the allocated reply buffer (denial of service).

Reject ValuatorClass entries whose valuator array does not fit in the
declared class length before sizing or copying.

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-13253

CVE-2026-93541

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XQueryDv.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/XQueryDv.c b/src/XQueryDv.c
index 7ee2272..d8f29b1 100644
--- a/src/XQueryDv.c
+++ b/src/XQueryDv.c
@@ -116,10 +116,25 @@ XQueryDeviceState(
            case ValuatorClass:
            {
                xValuatorState *v = (xValuatorState *) any;
+               size_t nvals;
+               size_t needed;
+
+               if (any->length < sizeof(xValuatorState))
+                   goto out;
+
                if ((char *)any + sizeof(xValuatorState) > end)
                    goto out;
-               size += (sizeof(XValuatorState) +
-                        (v->num_valuators * sizeof(int)));
+
+               nvals = v->num_valuators;
+               /* valuators follow the header; must fit in class length */
+               if (nvals > (any->length - sizeof(xValuatorState)) / 
sizeof(CARD32))
+                   goto out;
+
+               needed = sizeof(xValuatorState) + nvals * sizeof(CARD32);
+               if ((char *)any + needed > end)
+                   goto out;
+
+               size += sizeof(XValuatorState) + nvals * sizeof(int);
            }
                break;
            }
@@ -165,18 +180,17 @@ XQueryDeviceState(
                xValuatorState *v = (xValuatorState *) any;
                XValuatorState *V = (XValuatorState *) Any;
                CARD32 *valuators = (CARD32 *) (v + 1);
+               size_t nvals = v->num_valuators;
 
                V->class = v->class;
-               V->length = sizeof(XValuatorState) +
-                           v->num_valuators * sizeof(int);
+               V->length = sizeof(XValuatorState) + nvals * sizeof(int);
                V->num_valuators = v->num_valuators;
                V->mode = v->mode;
                Any = (XInputClass *) (V + 1);
                V->valuators = (int *)Any;
-               for (j = 0; j < (int)V->num_valuators; j++)
+               for (j = 0; j < (int)nvals; j++)
                    *(V->valuators + j) = *valuators++;
-               Any = (XInputClass *) ((char *)Any +
-                                      V->num_valuators * sizeof(int));
+               Any = (XInputClass *) ((char *)Any + nvals * sizeof(int));
            }
                break;
            }
-- 
2.51.0


++++++ 
0002-boo1281606_CVE-2026-93542_size_classes-copy_classes-bound-XI2-class-lengths-to.patch
 ++++++
>From f499944ad595b9bd7e7571c810842244caf150aa Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 31 Jul 2026 16:04:12 +0200
Subject: [PATCH 2/7] size_classes/copy_classes: bound XI2 class lengths to the
 received buffer

Both size_classes() and copy_classes() walked server-provided class
records using any_wire->length without checking that each class fits
in the reply or event buffer. XIQueryDevice() had the reply end
pointer available but did not pass it down; the same helpers are
reused for XI_DeviceChanged conversion.

A forged XIQueryDevice reply or truncated DeviceChanged event with an
overlong class length could therefore read past the allocated buffer
and crash the client.

Pass an end pointer into size_classes and copy_classes, reject zero
or overlong wire lengths before advancing, and propagate parse
failures to the callers.

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-13600

CVE-2026-93542

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XExtInt.c       | 50 +++++++++++++++++++++++++++++++++++++--------
 src/XIQueryDevice.c | 24 +++++++++++++++++-----
 2 files changed, 61 insertions(+), 13 deletions(-)

diff --git a/src/XExtInt.c b/src/XExtInt.c
index cdf5579..6b60792 100644
--- a/src/XExtInt.c
+++ b/src/XExtInt.c
@@ -73,8 +73,8 @@ SOFTWARE.
 #define DONT_ENQUEUE   False
 #define FP1616toDBL(x) ((x) * 1.0 / (1 << 16))
 
-int copy_classes(XIDeviceInfo *to, xXIAnyInfo* from, int *nclasses);
-int size_classes(xXIAnyInfo* from, int nclasses);
+int copy_classes(XIDeviceInfo *to, xXIAnyInfo* from, int *nclasses, char *end);
+int size_classes(xXIAnyInfo* from, int nclasses, char *end);
 
 static XExtensionInfo *xinput_info;
 static const char *xinput_extension_name = INAME;
@@ -1681,8 +1681,30 @@ wireToDeviceEvent(xXIDeviceEvent *in, 
XGenericEventCookie* cookie)
     return 1;
 }
 
+static int
+validate_class_header(char *ptr, char *end, xXIAnyInfo **any_out)
+{
+    xXIAnyInfo *any;
+    size_t remaining;
+
+    if (ptr + sizeof(xXIAnyInfo) > end)
+        return -1;
+
+    any = (xXIAnyInfo *) ptr;
+    if (any->length == 0)
+        return -1;
+
+    remaining = (size_t) (end - ptr);
+    if ((size_t) any->length > remaining / 4)
+        return -1;
+
+    *any_out = any;
+
+    return 0;
+}
+
 _X_HIDDEN int
-size_classes(xXIAnyInfo* from, int nclasses)
+size_classes(xXIAnyInfo* from, int nclasses, char *end)
 {
     int len, i;
     xXIAnyInfo *any_wire;
@@ -1694,7 +1716,10 @@ size_classes(xXIAnyInfo* from, int nclasses)
     for (i = 0; i < nclasses; i++)
     {
         int l = 0;
-        any_wire = (xXIAnyInfo*)ptr_wire;
+
+        if (validate_class_header(ptr_wire, end, &any_wire) < 0)
+            return -1;
+
         switch(any_wire->type)
         {
             case XIButtonClass:
@@ -1735,7 +1760,7 @@ size_classes(xXIAnyInfo* from, int nclasses)
  *             |______________________^
  */
 _X_HIDDEN int
-copy_classes(XIDeviceInfo* to, xXIAnyInfo* from, int *nclasses)
+copy_classes(XIDeviceInfo* to, xXIAnyInfo* from, int *nclasses, char *end)
 {
     XIAnyClassInfo *any_lib;
     xXIAnyInfo *any_wire;
@@ -1757,7 +1782,9 @@ copy_classes(XIDeviceInfo* to, xXIAnyInfo* from, int 
*nclasses)
     for (i = 0; i < *nclasses; i++)
     {
         any_lib = (XIAnyClassInfo*)ptr_lib;
-        any_wire = (xXIAnyInfo*)ptr_wire;
+
+        if (validate_class_header(ptr_wire, end, &any_wire) < 0)
+            return -1;
 
         switch(any_wire->type)
         {
@@ -1913,8 +1940,11 @@ wireToDeviceChangedEvent(xXIDeviceChangedEvent *in, 
XGenericEventCookie *cookie)
     XIDeviceInfo info;
     int len;
     int nclasses = in->num_classes;
+    char *end = (char *)in + sizeof(xEvent) + in->length * 4;
 
-    len = size_classes((xXIAnyInfo*)&in[1], in->num_classes);
+    len = size_classes((xXIAnyInfo*)&in[1], in->num_classes, end);
+    if (len < 0)
+        return 0;
 
     cookie->data = out = malloc(sizeof(XIDeviceChangedEvent) + len);
     if (!out)
@@ -1935,7 +1965,11 @@ wireToDeviceChangedEvent(xXIDeviceChangedEvent *in, 
XGenericEventCookie *cookie)
 
     info.classes = out->classes;
 
-    copy_classes(&info, (xXIAnyInfo*)&in[1], &nclasses);
+    if (copy_classes(&info, (xXIAnyInfo*)&in[1], &nclasses, end) < 0) {
+        free(out);
+        cookie->data = NULL;
+        return 0;
+    }
     out->num_classes = nclasses;
 
     return 1;
diff --git a/src/XIQueryDevice.c b/src/XIQueryDevice.c
index c89c4bc..c0f2ce1 100644
--- a/src/XIQueryDevice.c
+++ b/src/XIQueryDevice.c
@@ -34,8 +34,9 @@
 #include <X11/extensions/extutil.h>
 #include "XIint.h"
 
-extern int copy_classes(XIDeviceInfo* to, xXIAnyInfo* from, int *nclasses);
-extern int size_classes(xXIAnyInfo* from, int nclasses);
+extern int copy_classes(XIDeviceInfo* to, xXIAnyInfo* from, int *nclasses,
+                        char *end);
+extern int size_classes(xXIAnyInfo* from, int nclasses, char *end);
 
 XIDeviceInfo*
 XIQueryDevice(Display *dpy, int deviceid, int *ndevices_return)
@@ -88,7 +89,8 @@ XIQueryDevice(Display *dpy, int deviceid, int 
*ndevices_return)
     for (i = 0; i < reply.num_devices; i++)
     {
         int             nclasses;
-        size_t          sz;
+        int             sz;
+        int             wire_len;
         XIDeviceInfo    *lib = &info[i];
         xXIDeviceInfo   *wire = (xXIDeviceInfo*)ptr;
 
@@ -113,14 +115,26 @@ XIQueryDevice(Display *dpy, int deviceid, int 
*ndevices_return)
         lib->name[wire->name_len] = '\0';
         ptr += ((wire->name_len + 3)/4) * 4;
 
-        sz = size_classes((xXIAnyInfo*)ptr, nclasses);
+        sz = size_classes((xXIAnyInfo*)ptr, nclasses, end);
+        if (sz < 0)
+        {
+            Xfree(lib->name);
+            goto error_loop;
+        }
         lib->classes = Xmalloc(sz);
         if (lib->classes == NULL)
         {
             Xfree(lib->name);
             goto error_loop;
         }
-        ptr += copy_classes(lib, (xXIAnyInfo*)ptr, &nclasses);
+        wire_len = copy_classes(lib, (xXIAnyInfo*)ptr, &nclasses, end);
+        if (wire_len < 0)
+        {
+            Xfree(lib->name);
+            Xfree(lib->classes);
+            goto error_loop;
+        }
+        ptr += wire_len;
         /* We skip over unused classes */
         lib->num_classes = nclasses;
     }
-- 
2.51.0


++++++ 
0003-boo1281608_CVE-2026-93543_size_classes-copy_classes-enforce-XI2-per-type-class.patch
 ++++++
>From e2089ab748828273f916bbffd4e65b506aa50fdc Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 31 Jul 2026 16:20:56 +0200
Subject: [PATCH 3/7] size_classes/copy_classes: enforce XI2 per-type class
 payload sizes

Bounding any_wire->length against the reply/event end pointer is not
enough: a class length that fits in the buffer can still be shorter
than the type-specific wire struct, and button/key counts can claim
more bytes than that class contains. copy_classes() then reads past
the payload when converting XI_DeviceChanged events (and when parsing
XIQueryDevice replies).

Validate per-type minimum sizes and that button/key payloads fit in
the declared class length, and reject DeviceChanged allocations that
would overflow size_t.

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-13717

CVE-2026-93543

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XExtInt.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 75 insertions(+), 2 deletions(-)

diff --git a/src/XExtInt.c b/src/XExtInt.c
index 6b60792..2a6e7ac 100644
--- a/src/XExtInt.c
+++ b/src/XExtInt.c
@@ -1703,6 +1703,73 @@ validate_class_header(char *ptr, char *end, xXIAnyInfo 
**any_out)
     return 0;
 }
 
+/*
+ * Reject class records whose type-specific header or payload cannot fit in
+ * the declared wire length (length is in 4-byte units).
+ */
+static int
+validate_class_payload(xXIAnyInfo *any)
+{
+    size_t cls_len = (size_t) any->length * 4;
+
+    switch (any->type) {
+    case XIButtonClass:
+        {
+            xXIButtonInfo *b = (xXIButtonInfo *) any;
+            size_t mask_size, labels_size, need;
+
+            if (cls_len < sizeof(xXIButtonInfo))
+                return -1;
+
+            mask_size = ((size_t) b->num_buttons + 7) / 8;
+            mask_size = (mask_size + 3) / 4 * 4;
+            if (b->num_buttons >
+                (SIZE_MAX - sizeof(xXIButtonInfo) - mask_size) / 4)
+                return -1;
+
+            labels_size = (size_t) b->num_buttons * 4;
+            need = sizeof(xXIButtonInfo) + mask_size + labels_size;
+            if (need > cls_len)
+                return -1;
+
+            break;
+        }
+    case XIKeyClass:
+        {
+            xXIKeyInfo *k = (xXIKeyInfo *) any;
+
+            if (cls_len < sizeof(xXIKeyInfo))
+                return -1;
+
+            /* copy_classes reads num_keycodes bytes from the wire */
+            if ((size_t) k->num_keycodes > cls_len - sizeof(xXIKeyInfo))
+                return -1;
+
+            break;
+        }
+    case XIValuatorClass:
+        if (cls_len < sizeof(xXIValuatorInfo))
+            return -1;
+        break;
+    case XIScrollClass:
+        if (cls_len < sizeof(xXIScrollInfo))
+            return -1;
+        break;
+    case XITouchClass:
+        if (cls_len < sizeof(xXITouchInfo))
+            return -1;
+        break;
+    case XIGestureClass:
+        if (cls_len < sizeof(xXIGestureInfo))
+            return -1;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
 _X_HIDDEN int
 size_classes(xXIAnyInfo* from, int nclasses, char *end)
 {
@@ -1720,6 +1787,9 @@ size_classes(xXIAnyInfo* from, int nclasses, char *end)
         if (validate_class_header(ptr_wire, end, &any_wire) < 0)
             return -1;
 
+        if (validate_class_payload(any_wire) < 0)
+            return -1;
+
         switch(any_wire->type)
         {
             case XIButtonClass:
@@ -1786,6 +1856,9 @@ copy_classes(XIDeviceInfo* to, xXIAnyInfo* from, int 
*nclasses, char *end)
         if (validate_class_header(ptr_wire, end, &any_wire) < 0)
             return -1;
 
+        if (validate_class_payload(any_wire) < 0)
+            return -1;
+
         switch(any_wire->type)
         {
             case XIButtonClass:
@@ -1940,10 +2013,10 @@ wireToDeviceChangedEvent(xXIDeviceChangedEvent *in, 
XGenericEventCookie *cookie)
     XIDeviceInfo info;
     int len;
     int nclasses = in->num_classes;
-    char *end = (char *)in + sizeof(xEvent) + in->length * 4;
+    char *end = (char *)in + sizeof(xXIDeviceChangedEvent) + in->length * 4;
 
     len = size_classes((xXIAnyInfo*)&in[1], in->num_classes, end);
-    if (len < 0)
+    if (len < 0 || (size_t)len > SIZE_MAX - sizeof(XIDeviceChangedEvent))
         return 0;
 
     cookie->data = out = malloc(sizeof(XIDeviceChangedEvent) + len);
-- 
2.51.0


++++++ 
0004-boo1281609_CVE-2026-93544_XIQueryDevice-keep-padded-name-and-class-bytes-withi.patch
 ++++++
>From a88a341135b79f6ed450f481e4a5d6ba502382af Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 31 Jul 2026 16:27:02 +0200
Subject: [PATCH 4/7] XIQueryDevice: keep padded name and class bytes within
 the reply

Class parsing helpers already reject overlong or inconsistent XI2
class records against the reply end pointer. Also require the padded
device name and the bytes consumed by copy_classes() to stay inside
that bound before advancing to the next device.

A malformed XIQueryDevice reply with an inflated name_len (within the
raw length check but exceeding the 4-byte-padded name field) could
otherwise leave the parser misaligned for subsequent class data.

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-14043

CVE-2026-93544

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XIQueryDevice.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/XIQueryDevice.c b/src/XIQueryDevice.c
index c0f2ce1..0da746e 100644
--- a/src/XIQueryDevice.c
+++ b/src/XIQueryDevice.c
@@ -93,6 +93,7 @@ XIQueryDevice(Display *dpy, int deviceid, int 
*ndevices_return)
         int             wire_len;
         XIDeviceInfo    *lib = &info[i];
         xXIDeviceInfo   *wire = (xXIDeviceInfo*)ptr;
+        size_t          name_bytes;
 
         if (ptr + sizeof(xXIDeviceInfo) > end)
             goto error_loop;
@@ -104,16 +105,18 @@ XIQueryDevice(Display *dpy, int deviceid, int 
*ndevices_return)
         nclasses         = wire->num_classes;
 
         ptr += sizeof(xXIDeviceInfo);
+        name_bytes = ((size_t) wire->name_len + 3) / 4 * 4;
 
-        if (ptr + wire->name_len > end)
+        if (ptr + name_bytes > end)
             goto error_loop;
 
         lib->name = Xcalloc(wire->name_len + 1, 1);
         if (lib->name == NULL)
             goto error_loop;
+
         strncpy(lib->name, ptr, wire->name_len);
         lib->name[wire->name_len] = '\0';
-        ptr += ((wire->name_len + 3)/4) * 4;
+        ptr += name_bytes;
 
         sz = size_classes((xXIAnyInfo*)ptr, nclasses, end);
         if (sz < 0)
@@ -128,7 +131,7 @@ XIQueryDevice(Display *dpy, int deviceid, int 
*ndevices_return)
             goto error_loop;
         }
         wire_len = copy_classes(lib, (xXIAnyInfo*)ptr, &nclasses, end);
-        if (wire_len < 0)
+        if (wire_len < 0 || ptr + wire_len > end)
         {
             Xfree(lib->name);
             Xfree(lib->classes);
-- 
2.51.0


++++++ 
0005-boo1281612_CVE-2026-93545_XListInputDevices-validate-device-name-lengths-again.patch
 ++++++
>From 234ce17d95c42d75f7f7fdb2bf7a24875451bc0a Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 11 Sep 2026 16:37:48 +0200
Subject: [PATCH 5/7] XListInputDevices: validate device name lengths against
 reply buffer

The name-parsing loops trusted the server-supplied length byte without
checking that the advertised name actually fits within the received
reply buffer.

Additionally, the sizing loop used a wrong end pointer derived from
the already-incremented list pointer plus the full reply length,
which was too generous and could not catch overruns.

A malicious server could return a ListInputDevices reply with a name
length byte exceeding the remaining reply bytes, causing memcpy() to
read past the buffer and crash the client.

Check each name length against the actual reply buffer end (already
computed as "end") in both the sizing and copy loops before accessing
the name data.

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-14711

CVE-2026-93545

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XListDev.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/src/XListDev.c b/src/XListDev.c
index db71e0e..6f0ce7b 100644
--- a/src/XListDev.c
+++ b/src/XListDev.c
@@ -229,12 +229,16 @@ XListInputDevices(
             size += s;
        }
 
-       Nptr = ((unsigned char *)list) + rlen;
        for (i = 0, nptr = (unsigned char *)any; i < ndevices; i++) {
-           if (nptr >= Nptr)
+           size_t name_len;
+
+           if (nptr >= (unsigned char *)end)
+               goto out;
+           name_len = *nptr;
+           if (name_len > (size_t)((unsigned char *)end - nptr - 1))
                goto out;
-           size += *nptr + 1;
-           nptr += (*nptr + 1);
+           size += name_len + 1;
+           nptr += name_len + 1;
        }
 
        clist = (XDeviceInfoPtr) Xmalloc(size);
@@ -263,11 +267,18 @@ XListInputDevices(
        nptr = (unsigned char *)any;
        Nptr = (unsigned char *)Any;
        for (i = 0; i < ndevices; i++, clist++) {
+           size_t name_len;
+
+           if (nptr >= (unsigned char *)end)
+               goto out;
+           name_len = *nptr;
+           if (name_len > (size_t)((unsigned char *)end - nptr - 1))
+               goto out;
            clist->name = (char *)Nptr;
-           memcpy(Nptr, nptr + 1, *nptr);
-           Nptr += (*nptr);
+           memcpy(Nptr, nptr + 1, name_len);
+           Nptr += name_len;
            *Nptr++ = '\0';
-           nptr += (*nptr + 1);
+           nptr += name_len + 1;
        }
     }
 
-- 
2.51.0


++++++ 
0006-boo1281615_CVE-2026-94281_XListInputDevices-validate-class-lengths-cumulativel.patch
 ++++++
>From 605f419d013153bf9e026cd100752ffbe930f3c1 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 11 Sep 2026 16:45:23 +0200
Subject: [PATCH 6/7] XListInputDevices: validate class lengths cumulatively in
 SizeClassInfo

SizeClassInfo() checked each class record's length against the original
remaining buffer size, but never decremented it after advancing past
each record. A malicious server could supply class records whose
individual lengths each passed the check but whose cumulative size
exceeded the buffer, causing out-of-bounds reads in SizeClassInfo and
ParseClassInfo.

This is fixed by:

- Decrementing len after each class record in SizeClassInfo
- Validating minimum class-specific struct sizes before accessing fields
- Rejecting unknown class types
- Adding an end pointer and bounds checks to ParseClassInfo
- Checking ParseClassInfo return value at the call site

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-14718

CVE-2026-94281

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XListDev.c | 45 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 35 insertions(+), 10 deletions(-)

diff --git a/src/XListDev.c b/src/XListDev.c
index 6f0ce7b..7eb26d9 100644
--- a/src/XListDev.c
+++ b/src/XListDev.c
@@ -78,32 +78,47 @@ SizeClassInfo(xAnyClassPtr *any, size_t len, int 
num_classes, size_t *size)
 {
     int j;
     size_t sz = 0;
+    int clen;
 
     for (j = 0; j < num_classes; j++) {
+        if (len < sizeof(xAnyClassInfo))
+            return 1;
+        clen = (*any)->length;
+        if (clen < (int)sizeof(xAnyClassInfo) || (size_t)clen > len)
+            return 1;
+
         switch ((*any)->class) {
             case KeyClass:
+                if (clen < (int)sizeof(xKeyInfo))
+                    return 1;
                 sz += pad_to_xid(sizeof(XKeyInfo));
                 break;
             case ButtonClass:
+                if (clen < (int)sizeof(xButtonInfo))
+                    return 1;
                 sz += pad_to_xid(sizeof(XButtonInfo));
                 break;
             case ValuatorClass:
                 {
                     xValuatorInfoPtr v;
+                    size_t need;
 
-                    if (len < sizeof(v))
+                    if (clen < (int)sizeof(xValuatorInfo))
                         return 1;
                     v = (xValuatorInfoPtr) *any;
+                    need = sizeof(xValuatorInfo) +
+                        ((size_t)v->num_axes * sizeof(xAxisInfo));
+                    if (need > (size_t)clen)
+                        return 1;
                     sz += pad_to_xid(sizeof(XValuatorInfo) +
                         (v->num_axes * sizeof(XAxisInfo)));
                     break;
                 }
             default:
-                break;
+                return 1;
         }
-        if ((*any)->length > len)
-            return 1;
-        *any = (xAnyClassPtr) ((char *)(*any) + (*any)->length);
+        *any = (xAnyClassPtr) ((char *)(*any) + clen);
+        len -= (size_t)clen;
     }
 
     *size = sz;
@@ -111,12 +126,20 @@ SizeClassInfo(xAnyClassPtr *any, size_t len, int 
num_classes, size_t *size)
     return 0;
 }
 
-static void
-ParseClassInfo(xAnyClassPtr *any, XAnyClassPtr *Any, int num_classes)
+static int
+ParseClassInfo(xAnyClassPtr *any, XAnyClassPtr *Any, int num_classes,
+               char *end)
 {
     int j;
+    int clen;
 
     for (j = 0; j < num_classes; j++) {
+        if ((char *)(*any) + sizeof(xAnyClassInfo) > end)
+            return 1;
+        clen = (*any)->length;
+        if (clen < (int)sizeof(xAnyClassInfo) || (char *)(*any) + clen > end)
+            return 1;
+
         switch ((*any)->class) {
             case KeyClass:
                 {
@@ -165,11 +188,12 @@ ParseClassInfo(xAnyClassPtr *any, XAnyClassPtr *Any, int 
num_classes)
                     break;
                 }
             default:
-                break;
+                return 1;
         }
-        *any = (xAnyClassPtr) ((char *)(*any) + (*any)->length);
+        *any = (xAnyClassPtr) ((char *)(*any) + clen);
         *Any = (XAnyClassPtr) ((char *)(*Any) + (*Any)->length);
     }
+    return 0;
 }
 
 XDeviceInfo *
@@ -260,7 +284,8 @@ XListInputDevices(
            clist->num_classes = list->num_classes;
            clist->inputclassinfo = Any;
 
-            ParseClassInfo(&any, &Any, (int)list->num_classes);
+            if (ParseClassInfo(&any, &Any, (int)list->num_classes, end))
+                goto out;
        }
 
        clist = sclist;
-- 
2.51.0


++++++ 
0007-boo1281651_CVE-2026-94282_wireToEnterLeave-validate-buttons_len-against-the-re.patch
 ++++++
>From cecf160e9731fe01f3632f875f29ffcb598b052a Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <[email protected]>
Date: Fri, 11 Sep 2026 16:15:21 +0200
Subject: [PATCH 7/7] wireToEnterLeave: validate buttons_len against the
 received event size

wireToEnterLeave() trusts the wire-supplied buttons_len field and
copies buttons_len * 4 bytes from the event payload without checking
that many bytes were actually received.

A malicious X server can send an XI2 Enter, Leave, FocusIn or FocusOut
event with a small ge.length but an oversized buttons_len, causing
memcpy() to read past the end of the event buffer.

Pass the total event size into wireToEnterLeave() and reject events
where buttons_len * 4 exceeds the payload following the fixed-size
xXIEnterEvent header.

Found by AISLE in partnership with Red Hat

AISLE-Report-PSIRTSUPT-15083

CVE-2026-94282

Reported-by: Aisle Research
Signed-off-by: Olivier Fourdan <[email protected]>
Assisted-by: AI
---
 src/XExtInt.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/src/XExtInt.c b/src/XExtInt.c
index 2a6e7ac..9959efb 100644
--- a/src/XExtInt.c
+++ b/src/XExtInt.c
@@ -118,7 +118,8 @@ wireToHierarchyChangedEvent(xXIHierarchyEvent *in, 
XGenericEventCookie *cookie);
 static int
 wireToRawEvent(XExtDisplayInfo *info, xXIRawEvent *in, XGenericEventCookie 
*cookie);
 static int
-wireToEnterLeave(xXIEnterEvent *in, XGenericEventCookie *cookie);
+wireToEnterLeave(xXIEnterEvent *in, size_t event_bytes,
+                 XGenericEventCookie *cookie);
 static int
 wireToPropertyEvent(xXIPropertyEvent *in, XGenericEventCookie *cookie);
 static int
@@ -1042,7 +1043,9 @@ XInputWireToCookie(
         case XI_FocusIn:
         case XI_FocusOut:
             *cookie = *(XGenericEventCookie*)save;
-            if (!wireToEnterLeave((xXIEnterEvent*)event, cookie))
+            if (!wireToEnterLeave((xXIEnterEvent*)event,
+                                  sizeof(xEvent) + ((size_t)ge->length * 4),
+                                  cookie))
             {
                 printf("XInputWireToCookie: CONVERSION FAILURE!  evtype=%d\n",
                         ge->evtype);
@@ -2146,12 +2149,20 @@ wireToRawEvent(XExtDisplayInfo *info, xXIRawEvent *in, 
XGenericEventCookie *cook
    [event][modifiers][group][button]
  */
 static int
-wireToEnterLeave(xXIEnterEvent *in, XGenericEventCookie *cookie)
+wireToEnterLeave(xXIEnterEvent *in, size_t event_bytes,
+                 XGenericEventCookie *cookie)
 {
-    int len;
+    size_t mask_len, len;
     XIEnterEvent *out;
 
-    len = sizeof(XIEnterEvent) + in->buttons_len * 4;
+    mask_len = (size_t)in->buttons_len * 4;
+
+    if (event_bytes < sizeof(*in) || mask_len > event_bytes - sizeof(*in))
+        return 0;
+    if (mask_len > SIZE_MAX - sizeof(XIEnterEvent))
+        return 0;
+
+    len = sizeof(XIEnterEvent) + mask_len;
 
     cookie->data = out = malloc(len);
     if (!out)
@@ -2189,8 +2200,8 @@ wireToEnterLeave(xXIEnterEvent *in, XGenericEventCookie 
*cookie)
     out->group.latched = in->group.latched_group;
     out->group.effective = in->group.effective_group;
 
-    out->buttons.mask_len = in->buttons_len * 4;
-    memcpy(out->buttons.mask, &in[1], out->buttons.mask_len);
+    out->buttons.mask_len = (int) mask_len;
+    memcpy(out->buttons.mask, &in[1], mask_len);
 
     return 1;
 }
-- 
2.51.0

Reply via email to