xserver: Branch 'master' - 13 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Feb 25 15:32:16 UTC 2025


 Xext/sync.c           |   92 ++++++++++++++++++++++++++++----------------------
 Xi/xibarriers.c       |   27 ++++++++++++--
 composite/compalloc.c |   16 +++++---
 dix/devices.c         |   18 +++++++++
 dix/dispatch.c        |    4 ++
 dix/main.c            |    4 ++
 xkb/XKBMisc.c         |    1 
 xkb/xkb.c             |    8 ++--
 xkb/xkbtext.c         |   16 ++++----
 9 files changed, 126 insertions(+), 60 deletions(-)

New commits:
commit c285798984c6bb99e454a33772cde23d394d3dcd
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Jan 20 17:10:31 2025 +0100

    sync: Apply changes last in SyncChangeAlarmAttributes()
    
    SyncChangeAlarmAttributes() would apply the various changes while
    checking for errors.
    
    If one of the changes triggers an error, the changes for the trigger,
    counter or delta value would remain, possibly leading to inconsistent
    changes.
    
    Postpone the actual changes until we're sure nothing else can go wrong.
    
    Related to CVE-2025-26601, ZDI-CAN-25870
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/Xext/sync.c b/Xext/sync.c
index 65f2d4378..cab73be92 100644
--- a/Xext/sync.c
+++ b/Xext/sync.c
@@ -830,8 +830,14 @@ SyncChangeAlarmAttributes(ClientPtr client, SyncAlarm * pAlarm, Mask mask,
     int status;
     XSyncCounter counter;
     Mask origmask = mask;
+    SyncTrigger trigger;
+    Bool select_events_changed = FALSE;
+    Bool select_events_value = FALSE;
+    int64_t delta;
 
-    counter = pAlarm->trigger.pSync ? pAlarm->trigger.pSync->id : None;
+    trigger = pAlarm->trigger;
+    delta = pAlarm->delta;
+    counter = trigger.pSync ? trigger.pSync->id : None;
 
     while (mask) {
         int index2 = lowbit(mask);
@@ -847,24 +853,24 @@ SyncChangeAlarmAttributes(ClientPtr client, SyncAlarm * pAlarm, Mask mask,
         case XSyncCAValueType:
             mask &= ~XSyncCAValueType;
             /* sanity check in SyncInitTrigger */
-            pAlarm->trigger.value_type = *values++;
+            trigger.value_type = *values++;
             break;
 
         case XSyncCAValue:
             mask &= ~XSyncCAValue;
-            pAlarm->trigger.wait_value = ((int64_t)values[0] << 32) | values[1];
+            trigger.wait_value = ((int64_t)values[0] << 32) | values[1];
             values += 2;
             break;
 
         case XSyncCATestType:
             mask &= ~XSyncCATestType;
             /* sanity check in SyncInitTrigger */
-            pAlarm->trigger.test_type = *values++;
+            trigger.test_type = *values++;
             break;
 
         case XSyncCADelta:
             mask &= ~XSyncCADelta;
-            pAlarm->delta = ((int64_t)values[0] << 32) | values[1];
+            delta = ((int64_t)values[0] << 32) | values[1];
             values += 2;
             break;
 
@@ -874,10 +880,8 @@ SyncChangeAlarmAttributes(ClientPtr client, SyncAlarm * pAlarm, Mask mask,
                 client->errorValue = *values;
                 return BadValue;
             }
-            status = SyncEventSelectForAlarm(pAlarm, client,
-                                             (Bool) (*values++));
-            if (status != Success)
-                return status;
+            select_events_value = (Bool) (*values++);
+            select_events_changed = TRUE;
             break;
 
         default:
@@ -886,25 +890,33 @@ SyncChangeAlarmAttributes(ClientPtr client, SyncAlarm * pAlarm, Mask mask,
         }
     }
 
+    if (select_events_changed) {
+        status = SyncEventSelectForAlarm(pAlarm, client, select_events_value);
+        if (status != Success)
+            return status;
+    }
+
     /* "If the test-type is PositiveComparison or PositiveTransition
      *  and delta is less than zero, or if the test-type is
      *  NegativeComparison or NegativeTransition and delta is
      *  greater than zero, a Match error is generated."
      */
     if (origmask & (XSyncCADelta | XSyncCATestType)) {
-        if ((((pAlarm->trigger.test_type == XSyncPositiveComparison) ||
-              (pAlarm->trigger.test_type == XSyncPositiveTransition))
-             && pAlarm->delta < 0)
+        if ((((trigger.test_type == XSyncPositiveComparison) ||
+              (trigger.test_type == XSyncPositiveTransition))
+             && delta < 0)
             ||
-            (((pAlarm->trigger.test_type == XSyncNegativeComparison) ||
-              (pAlarm->trigger.test_type == XSyncNegativeTransition))
-             && pAlarm->delta > 0)
+            (((trigger.test_type == XSyncNegativeComparison) ||
+              (trigger.test_type == XSyncNegativeTransition))
+             && delta > 0)
             ) {
             return BadMatch;
         }
     }
 
     /* postpone this until now, when we're sure nothing else can go wrong */
+    pAlarm->delta = delta;
+    pAlarm->trigger = trigger;
     if ((status = SyncInitTrigger(client, &pAlarm->trigger, counter, RTCounter,
                                   origmask & XSyncCAAllTrigger)) != Success)
         return status;
commit 8cbc90c8817306af75a60f494ec9dbb1061e50db
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Jan 20 17:06:07 2025 +0100

    sync: Do not fail SyncAddTriggerToSyncObject()
    
    We do not want to return a failure at the very last step in
    SyncInitTrigger() after having all changes applied.
    
    SyncAddTriggerToSyncObject() must not fail on memory allocation, if the
    allocation of the SyncTriggerList fails, trigger a FatalError() instead.
    
    Related to CVE-2025-26601, ZDI-CAN-25870
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/Xext/sync.c b/Xext/sync.c
index 10302160f..65f2d4378 100644
--- a/Xext/sync.c
+++ b/Xext/sync.c
@@ -201,8 +201,8 @@ SyncAddTriggerToSyncObject(SyncTrigger * pTrigger)
             return Success;
     }
 
-    if (!(pCur = malloc(sizeof(SyncTriggerList))))
-        return BadAlloc;
+    /* Failure is not an option, it's succeed or burst! */
+    pCur = XNFalloc(sizeof(SyncTriggerList));
 
     pCur->pTrigger = pTrigger;
     pCur->next = pTrigger->pSync->pTriglist;
@@ -439,8 +439,7 @@ SyncInitTrigger(ClientPtr client, SyncTrigger * pTrigger, XID syncObject,
      *  a new counter on a trigger
      */
     if (newSyncObject) {
-        if ((rc = SyncAddTriggerToSyncObject(pTrigger)) != Success)
-            return rc;
+        SyncAddTriggerToSyncObject(pTrigger);
     }
     else if (pCounter && IsSystemCounter(pCounter)) {
         SyncComputeBracketValues(pCounter);
commit f52cea2f93a0c891494eb3334894442a92368030
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Jan 20 16:54:30 2025 +0100

    sync: Check values before applying changes
    
    In SyncInitTrigger(), we would set the CheckTrigger function before
    validating the counter value.
    
    As a result, if the counter value overflowed, we would leave the
    function SyncInitTrigger() with the CheckTrigger applied but without
    updating the trigger object.
    
    To avoid that issue, move the portion of code checking for the trigger
    check value before updating the CheckTrigger function.
    
    Related to CVE-2025-26601, ZDI-CAN-25870
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/Xext/sync.c b/Xext/sync.c
index 585cfa6f6..10302160f 100644
--- a/Xext/sync.c
+++ b/Xext/sync.c
@@ -381,6 +381,24 @@ SyncInitTrigger(ClientPtr client, SyncTrigger * pTrigger, XID syncObject,
         }
     }
 
+    if (changes & (XSyncCAValueType | XSyncCAValue)) {
+        if (pTrigger->value_type == XSyncAbsolute)
+            pTrigger->test_value = pTrigger->wait_value;
+        else {                  /* relative */
+            Bool overflow;
+
+            if (pCounter == NULL)
+                return BadMatch;
+
+            overflow = checked_int64_add(&pTrigger->test_value,
+                                         pCounter->value, pTrigger->wait_value);
+            if (overflow) {
+                client->errorValue = pTrigger->wait_value >> 32;
+                return BadValue;
+            }
+        }
+    }
+
     if (changes & XSyncCATestType) {
 
         if (pSync && SYNC_FENCE == pSync->type) {
@@ -409,24 +427,6 @@ SyncInitTrigger(ClientPtr client, SyncTrigger * pTrigger, XID syncObject,
         }
     }
 
-    if (changes & (XSyncCAValueType | XSyncCAValue)) {
-        if (pTrigger->value_type == XSyncAbsolute)
-            pTrigger->test_value = pTrigger->wait_value;
-        else {                  /* relative */
-            Bool overflow;
-
-            if (pCounter == NULL)
-                return BadMatch;
-
-            overflow = checked_int64_add(&pTrigger->test_value,
-                                         pCounter->value, pTrigger->wait_value);
-            if (overflow) {
-                client->errorValue = pTrigger->wait_value >> 32;
-                return BadValue;
-            }
-        }
-    }
-
     if (changes & XSyncCACounter) {
         if (pSync != pTrigger->pSync) { /* new counter for trigger */
             SyncDeleteTriggerFromSyncObject(pTrigger);
commit 16a1242d0ffc7f45ed3c595ee7564b5c04287e0b
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Jan 20 16:52:01 2025 +0100

    sync: Do not let sync objects uninitialized
    
    When changing an alarm, the change mask values are evaluated one after
    the other, changing the trigger values as requested and eventually,
    SyncInitTrigger() is called.
    
    SyncInitTrigger() will evaluate the XSyncCACounter first and may free
    the existing sync object.
    
    Other changes are then evaluated and may trigger an error and an early
    return, not adding the new sync object.
    
    This can be used to cause a use after free when the alarm eventually
    triggers.
    
    To avoid the issue, delete the existing sync object as late as possible
    only once we are sure that no further error will cause an early exit.
    
    CVE-2025-26601, ZDI-CAN-25870
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/Xext/sync.c b/Xext/sync.c
index ee0010e65..585cfa6f6 100644
--- a/Xext/sync.c
+++ b/Xext/sync.c
@@ -360,11 +360,6 @@ SyncInitTrigger(ClientPtr client, SyncTrigger * pTrigger, XID syncObject,
             client->errorValue = syncObject;
             return rc;
         }
-        if (pSync != pTrigger->pSync) { /* new counter for trigger */
-            SyncDeleteTriggerFromSyncObject(pTrigger);
-            pTrigger->pSync = pSync;
-            newSyncObject = TRUE;
-        }
     }
 
     /* if system counter, ask it what the current value is */
@@ -432,6 +427,14 @@ SyncInitTrigger(ClientPtr client, SyncTrigger * pTrigger, XID syncObject,
         }
     }
 
+    if (changes & XSyncCACounter) {
+        if (pSync != pTrigger->pSync) { /* new counter for trigger */
+            SyncDeleteTriggerFromSyncObject(pTrigger);
+            pTrigger->pSync = pSync;
+            newSyncObject = TRUE;
+        }
+    }
+
     /*  we wait until we're sure there are no errors before registering
      *  a new counter on a trigger
      */
commit 6e0f332ba4c8b8c9a9945dc9d7989bfe06f80e14
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Dec 16 16:18:04 2024 +0100

    dix: Dequeue pending events on frozen device on removal
    
    When a device is removed while still frozen, the events queued for that
    device remain while the device itself is freed.
    
    As a result, replaying the events will cause a use after free.
    
    To avoid the issue, make sure to dequeue and free any pending events on
    a frozen device when removed.
    
    CVE-2025-26600, ZDI-CAN-25871
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/dix/devices.c b/dix/devices.c
index 3074662a6..310364730 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -981,6 +981,23 @@ FreeAllDeviceClasses(ClassesPtr classes)
 
 }
 
+static void
+FreePendingFrozenDeviceEvents(DeviceIntPtr dev)
+{
+    QdEventPtr qe, tmp;
+
+    if (!dev->deviceGrab.sync.frozen)
+        return;
+
+    /* Dequeue any frozen pending events */
+    xorg_list_for_each_entry_safe(qe, tmp, &syncEvents.pending, next) {
+        if (qe->device == dev) {
+            xorg_list_del(&qe->next);
+            free(qe);
+        }
+    }
+}
+
 /**
  * Close down a device and free all resources.
  * Once closed down, the driver will probably not expect you that you'll ever
@@ -1044,6 +1061,7 @@ CloseDevice(DeviceIntPtr dev)
         valuator_mask_free(&dev->last.touches[j].valuators);
     free(dev->last.touches);
     dev->config_info = NULL;
+    FreePendingFrozenDeviceEvents(dev);
     dixFreePrivates(dev->devPrivates, PRIVATE_DEVICE);
     free(dev);
 }
commit b07192a8bedb90b039dc0f70ae69daf047ff9598
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Jan 13 16:09:43 2025 +0100

    composite: initialize border clip even when pixmap alloc fails
    
    If it fails to allocate the pixmap, the function compAllocPixmap() would
    return early and leave the borderClip region uninitialized, which may
    lead to the use of uninitialized value as reported by valgrind:
    
     Conditional jump or move depends on uninitialised value(s)
        at 0x4F9B33: compClipNotify (compwindow.c:317)
        by 0x484FC9: miComputeClips (mivaltree.c:476)
        by 0x48559A: miValidateTree (mivaltree.c:679)
        by 0x4F0685: MapWindow (window.c:2693)
        by 0x4A344A: ProcMapWindow (dispatch.c:922)
        by 0x4A25B5: Dispatch (dispatch.c:560)
        by 0x4B082A: dix_main (main.c:282)
        by 0x429233: main (stubmain.c:34)
      Uninitialised value was created by a heap allocation
        at 0x4841866: malloc (vg_replace_malloc.c:446)
        by 0x4F47BC: compRedirectWindow (compalloc.c:171)
        by 0x4FA8AD: compCreateWindow (compwindow.c:592)
        by 0x4EBB89: CreateWindow (window.c:925)
        by 0x4A2E6E: ProcCreateWindow (dispatch.c:768)
        by 0x4A25B5: Dispatch (dispatch.c:560)
        by 0x4B082A: dix_main (main.c:282)
        by 0x429233: main (stubmain.c:34)
    
     Conditional jump or move depends on uninitialised value(s)
        at 0x48EEDBC: pixman_region_translate (pixman-region.c:2233)
        by 0x4F9255: RegionTranslate (regionstr.h:312)
        by 0x4F9B7E: compClipNotify (compwindow.c:319)
        by 0x484FC9: miComputeClips (mivaltree.c:476)
        by 0x48559A: miValidateTree (mivaltree.c:679)
        by 0x4F0685: MapWindow (window.c:2693)
        by 0x4A344A: ProcMapWindow (dispatch.c:922)
        by 0x4A25B5: Dispatch (dispatch.c:560)
        by 0x4B082A: dix_main (main.c:282)
        by 0x429233: main (stubmain.c:34)
      Uninitialised value was created by a heap allocation
        at 0x4841866: malloc (vg_replace_malloc.c:446)
        by 0x4F47BC: compRedirectWindow (compalloc.c:171)
        by 0x4FA8AD: compCreateWindow (compwindow.c:592)
        by 0x4EBB89: CreateWindow (window.c:925)
        by 0x4A2E6E: ProcCreateWindow (dispatch.c:768)
        by 0x4A25B5: Dispatch (dispatch.c:560)
        by 0x4B082A: dix_main (main.c:282)
        by 0x429233: main (stubmain.c:34)
    
     Conditional jump or move depends on uninitialised value(s)
        at 0x48EEE33: UnknownInlinedFun (pixman-region.c:2241)
        by 0x48EEE33: pixman_region_translate (pixman-region.c:2225)
        by 0x4F9255: RegionTranslate (regionstr.h:312)
        by 0x4F9B7E: compClipNotify (compwindow.c:319)
        by 0x484FC9: miComputeClips (mivaltree.c:476)
        by 0x48559A: miValidateTree (mivaltree.c:679)
        by 0x4F0685: MapWindow (window.c:2693)
        by 0x4A344A: ProcMapWindow (dispatch.c:922)
        by 0x4A25B5: Dispatch (dispatch.c:560)
        by 0x4B082A: dix_main (main.c:282)
        by 0x429233: main (stubmain.c:34)
      Uninitialised value was created by a heap allocation
        at 0x4841866: malloc (vg_replace_malloc.c:446)
        by 0x4F47BC: compRedirectWindow (compalloc.c:171)
        by 0x4FA8AD: compCreateWindow (compwindow.c:592)
        by 0x4EBB89: CreateWindow (window.c:925)
        by 0x4A2E6E: ProcCreateWindow (dispatch.c:768)
        by 0x4A25B5: Dispatch (dispatch.c:560)
        by 0x4B082A: dix_main (main.c:282)
        by 0x429233: main (stubmain.c:34)
    
    Fix compAllocPixmap() to initialize the border clip even if the creation
    of the backing pixmap has failed, to avoid depending later on
    uninitialized border clip values.
    
    Related to CVE-2025-26599, ZDI-CAN-25851
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Acked-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/composite/compalloc.c b/composite/compalloc.c
index 7cf7351e0..4a1243170 100644
--- a/composite/compalloc.c
+++ b/composite/compalloc.c
@@ -605,9 +605,12 @@ compAllocPixmap(WindowPtr pWin)
     int h = pWin->drawable.height + (bw << 1);
     PixmapPtr pPixmap = compNewPixmap(pWin, x, y, w, h);
     CompWindowPtr cw = GetCompWindow(pWin);
+    Bool status;
 
-    if (!pPixmap)
-        return FALSE;
+    if (!pPixmap) {
+        status = FALSE;
+        goto out;
+    }
     if (cw->update == CompositeRedirectAutomatic)
         pWin->redirectDraw = RedirectDrawAutomatic;
     else
@@ -621,14 +624,16 @@ compAllocPixmap(WindowPtr pWin)
         DamageRegister(&pWin->drawable, cw->damage);
         cw->damageRegistered = TRUE;
     }
+    status = TRUE;
 
+out:
     /* Make sure our borderClip is up to date */
     RegionUninit(&cw->borderClip);
     RegionCopy(&cw->borderClip, &pWin->borderClip);
     cw->borderClipX = pWin->drawable.x;
     cw->borderClipY = pWin->drawable.y;
 
-    return TRUE;
+    return status;
 }
 
 void
commit c1ff84bef2569b4ba4be59323cf575d1798ba9be
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Tue Dec 17 15:19:45 2024 +0100

    composite: Handle failure to redirect in compRedirectWindow()
    
    The function compCheckRedirect() may fail if it cannot allocate the
    backing pixmap.
    
    In that case, compRedirectWindow() will return a BadAlloc error.
    
    However that failure code path will shortcut the validation of the
    window tree marked just before, which leaves the validate data partly
    initialized.
    
    That causes a use of uninitialized pointer later.
    
    The fix is to not shortcut the call to compHandleMarkedWindows() even in
    the case of compCheckRedirect() returning an error.
    
    CVE-2025-26599, ZDI-CAN-25851
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Acked-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/composite/compalloc.c b/composite/compalloc.c
index f7048a75b..7cf7351e0 100644
--- a/composite/compalloc.c
+++ b/composite/compalloc.c
@@ -138,6 +138,7 @@ compRedirectWindow(ClientPtr pClient, WindowPtr pWin, int update)
     CompScreenPtr cs = GetCompScreen(pWin->drawable.pScreen);
     WindowPtr pLayerWin;
     Bool anyMarked = FALSE;
+    int status = Success;
 
     if (pWin == cs->pOverlayWin) {
         return Success;
@@ -216,13 +217,13 @@ compRedirectWindow(ClientPtr pClient, WindowPtr pWin, int update)
 
     if (!compCheckRedirect(pWin)) {
         FreeResource(ccw->id, X11_RESTYPE_NONE);
-        return BadAlloc;
+        status = BadAlloc;
     }
 
     if (anyMarked)
         compHandleMarkedWindows(pWin, pLayerWin);
 
-    return Success;
+    return status;
 }
 
 void
commit bba9df1a9d57234c76c0b93f88dacb143d01bca2
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Dec 16 11:25:11 2024 +0100

    Xi: Fix barrier device search
    
    The function GetBarrierDevice() would search for the pointer device
    based on its device id and return the matching value, or supposedly NULL
    if no match was found.
    
    Unfortunately, as written, it would return the last element of the list
    if no matching device id was found which can lead to out of bounds
    memory access.
    
    Fix the search function to return NULL if not matching device is found,
    and adjust the callers to handle the case where the device cannot be
    found.
    
    CVE-2025-26598, ZDI-CAN-25740
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c
index 700b2b8c5..6761bcb49 100644
--- a/Xi/xibarriers.c
+++ b/Xi/xibarriers.c
@@ -132,14 +132,15 @@ static void FreePointerBarrierClient(struct PointerBarrierClient *c)
 
 static struct PointerBarrierDevice *GetBarrierDevice(struct PointerBarrierClient *c, int deviceid)
 {
-    struct PointerBarrierDevice *pbd = NULL;
+    struct PointerBarrierDevice *p, *pbd = NULL;
 
-    xorg_list_for_each_entry(pbd, &c->per_device, entry) {
-        if (pbd->deviceid == deviceid)
+    xorg_list_for_each_entry(p, &c->per_device, entry) {
+        if (p->deviceid == deviceid) {
+            pbd = p;
             break;
+        }
     }
 
-    BUG_WARN(!pbd);
     return pbd;
 }
 
@@ -340,6 +341,9 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev,
         double distance;
 
         pbd = GetBarrierDevice(c, dev->id);
+        if (!pbd)
+            continue;
+
         if (pbd->seen)
             continue;
 
@@ -448,6 +452,9 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
         nearest = &c->barrier;
 
         pbd = GetBarrierDevice(c, master->id);
+        if (!pbd)
+            continue;
+
         new_sequence = !pbd->hit;
 
         pbd->seen = TRUE;
@@ -488,6 +495,9 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen,
         int flags = 0;
 
         pbd = GetBarrierDevice(c, master->id);
+        if (!pbd)
+            continue;
+
         pbd->seen = FALSE;
         if (!pbd->hit)
             continue;
@@ -682,6 +692,9 @@ BarrierFreeBarrier(void *data, XID id)
             continue;
 
         pbd = GetBarrierDevice(c, dev->id);
+        if (!pbd)
+            continue;
+
         if (!pbd->hit)
             continue;
 
@@ -741,6 +754,8 @@ static void remove_master_func(void *res, XID id, void *devid)
     barrier = container_of(b, struct PointerBarrierClient, barrier);
 
     pbd = GetBarrierDevice(barrier, *deviceid);
+    if (!pbd)
+        return;
 
     if (pbd->hit) {
         BarrierEvent ev = {
@@ -905,6 +920,10 @@ ProcXIBarrierReleasePointer(ClientPtr client)
         barrier = container_of(b, struct PointerBarrierClient, barrier);
 
         pbd = GetBarrierDevice(barrier, dev->id);
+        if (!pbd) {
+            client->errorValue = dev->id;
+            return BadDevice;
+        }
 
         if (pbd->barrier_event_id == event_id)
             pbd->release_event_id = event_id;
commit 0e4ed94952b255c04fe910f6a1d9c852878dcd64
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu Nov 28 14:09:04 2024 +0100

    xkb: Fix buffer overflow in XkbChangeTypesOfKey()
    
    If XkbChangeTypesOfKey() is called with nGroups == 0, it will resize the
    key syms to 0 but leave the key actions unchanged.
    
    If later, the same function is called with a non-zero value for nGroups,
    this will cause a buffer overflow because the key actions are of the wrong
    size.
    
    To avoid the issue, make sure to resize both the key syms and key actions
    when nGroups is 0.
    
    CVE-2025-26597, ZDI-CAN-25683
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/xkb/XKBMisc.c b/xkb/XKBMisc.c
index abbfed90e..fd180fad2 100644
--- a/xkb/XKBMisc.c
+++ b/xkb/XKBMisc.c
@@ -553,6 +553,7 @@ XkbChangeTypesOfKey(XkbDescPtr xkb,
         i = XkbSetNumGroups(i, 0);
         xkb->map->key_sym_map[key].group_info = i;
         XkbResizeKeySyms(xkb, key, 0);
+        XkbResizeKeyActions(xkb, key, 0);
         return Success;
     }
 
commit 80d69f01423fc065c950e1ff4e8ddf9f675df773
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Thu Nov 28 11:49:34 2024 +0100

    xkb: Fix computation of XkbSizeKeySyms
    
    The computation of the length in XkbSizeKeySyms() differs from what is
    actually written in XkbWriteKeySyms(), leading to a heap overflow.
    
    Fix the calculation in XkbSizeKeySyms() to match what kbWriteKeySyms()
    does.
    
    CVE-2025-26596, ZDI-CAN-25543
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/xkb/xkb.c b/xkb/xkb.c
index 85659382d..744dba63d 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -1095,10 +1095,10 @@ XkbSizeKeySyms(XkbDescPtr xkb, xkbGetMapReply * rep)
     len = rep->nKeySyms * SIZEOF(xkbSymMapWireDesc);
     symMap = &xkb->map->key_sym_map[rep->firstKeySym];
     for (i = nSyms = 0; i < rep->nKeySyms; i++, symMap++) {
-        if (symMap->offset != 0) {
-            nSymsThisKey = XkbNumGroups(symMap->group_info) * symMap->width;
-            nSyms += nSymsThisKey;
-        }
+        nSymsThisKey = XkbNumGroups(symMap->group_info) * symMap->width;
+        if (nSymsThisKey == 0)
+            continue;
+        nSyms += nSymsThisKey;
     }
     len += nSyms * 4;
     rep->totalSyms = nSyms;
commit 11fcda8753e994e15eb915d28cf487660ec8e722
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Wed Nov 27 14:41:45 2024 +0100

    xkb: Fix buffer overflow in XkbVModMaskText()
    
    The code in XkbVModMaskText() allocates a fixed sized buffer on the
    stack and copies the virtual mod name.
    
    There's actually two issues in the code that can lead to a buffer
    overflow.
    
    First, the bound check mixes pointers and integers using misplaced
    parenthesis, defeating the bound check.
    
    But even though, if the check fails, the data is still copied, so the
    stack overflow will occur regardless.
    
    Change the logic to skip the copy entirely if the bound check fails.
    
    CVE-2025-26595, ZDI-CAN-25545
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/xkb/xkbtext.c b/xkb/xkbtext.c
index 018466420..93262528b 100644
--- a/xkb/xkbtext.c
+++ b/xkb/xkbtext.c
@@ -173,14 +173,14 @@ XkbVModMaskText(XkbDescPtr xkb,
                 len = strlen(tmp) + 1 + (str == buf ? 0 : 1);
                 if (format == XkbCFile)
                     len += 4;
-                if ((str - (buf + len)) <= VMOD_BUFFER_SIZE) {
-                    if (str != buf) {
-                        if (format == XkbCFile)
-                            *str++ = '|';
-                        else
-                            *str++ = '+';
-                        len--;
-                    }
+                if ((str - buf) + len > VMOD_BUFFER_SIZE)
+                    continue; /* Skip */
+                if (str != buf) {
+                    if (format == XkbCFile)
+                        *str++ = '|';
+                    else
+                        *str++ = '+';
+                    len--;
                 }
                 if (format == XkbCFile)
                     sprintf(str, "%sMask", tmp);
commit b0a09ba6020147961acc62d9c73d807b4cccd9f7
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Wed Dec 4 15:49:43 2024 +1000

    dix: keep a ref to the rootCursor
    
    CreateCursor returns a cursor with refcount 1 - that refcount is used by
    the resource system, any caller needs to call RefCursor to get their own
    reference. That happens correctly for normal cursors but for our
    rootCursor we keep a variable to the cursor despite not having a ref for
    ourselves.
    
    Fix this by reffing/unreffing the rootCursor to ensure our pointer is
    valid.
    
    Related to CVE-2025-26594, ZDI-CAN-25544
    
    Reviewed-by: Olivier Fourdan <ofourdan at redhat.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/dix/main.c b/dix/main.c
index aa7b020b2..0c57ba605 100644
--- a/dix/main.c
+++ b/dix/main.c
@@ -241,6 +241,8 @@ dix_main(int argc, char *argv[], char *envp[])
             FatalError("could not open default cursor font");
         }
 
+        rootCursor = RefCursor(rootCursor);
+
 #ifdef XINERAMA
         /*
          * Consolidate window and colourmap information for each screen
@@ -281,6 +283,8 @@ dix_main(int argc, char *argv[], char *envp[])
 
         Dispatch();
 
+        UnrefCursor(rootCursor);
+
         UndisplayDevices();
         DisableAllDevices();
 
commit 01642f263f12becf803b19be4db95a4a83f94acc
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Wed Nov 27 11:27:05 2024 +0100

    Cursor: Refuse to free the root cursor
    
    If a cursor reference count drops to 0, the cursor is freed.
    
    The root cursor however is referenced with a specific global variable,
    and when the root cursor is freed, the global variable may still point
    to freed memory.
    
    Make sure to prevent the rootCursor from being explicitly freed by a
    client.
    
    CVE-2025-26594, ZDI-CAN-25544
    
    This vulnerability was discovered by:
    Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
    
    v2: Explicitly forbid XFreeCursor() on the root cursor (Peter Hutterer
    <peter.hutterer at who-t.net>)
    v3: Return BadCursor instead of BadValue (Michel Dänzer
    <michel at daenzer.net>)
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Suggested-by: Peter Hutterer <peter.hutterer at who-t.net>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1828>

diff --git a/dix/dispatch.c b/dix/dispatch.c
index efb17c734..b3e5feacc 100644
--- a/dix/dispatch.c
+++ b/dix/dispatch.c
@@ -3123,6 +3123,10 @@ ProcFreeCursor(ClientPtr client)
     rc = dixLookupResourceByType((void **) &pCursor, stuff->id, X11_RESTYPE_CURSOR,
                                  client, DixDestroyAccess);
     if (rc == Success) {
+        if (pCursor == rootCursor) {
+            client->errorValue = stuff->id;
+            return BadCursor;
+        }
         FreeResource(stuff->id, X11_RESTYPE_NONE);
         return Success;
     }


More information about the xorg-commit mailing list