Version in base suite: 21.1.7-3+deb12u11 Base version: xorg-server_21.1.7-3+deb12u11 Target version: xorg-server_21.1.7-3+deb12u12 Base file: /srv/ftp-master.debian.org/ftp/pool/main/x/xorg-server/xorg-server_21.1.7-3+deb12u11.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/x/xorg-server/xorg-server_21.1.7-3+deb12u12.dsc debian/patches/CVE-2026-33999/0001-xkb-fix-buffer-re-use-in-_XkbSetCompatMap.patch | 44 ++ debian/patches/CVE-2026-34000/0001-xkb-Fix-bounds-check-in-_CheckSetGeom.patch | 67 +++ debian/patches/CVE-2026-34001/0001-miext-sync-Fix-use-after-free-in-miSyncTriggerFence.patch | 100 ++++ debian/patches/CVE-2026-34002/0001-xkb-Fix-out-of-bounds-read-in-CheckModifierMap.patch | 88 ++++ debian/patches/CVE-2026-34003/0001-xkb-Add-additional-bound-checking-in-CheckKeyTypes.patch | 109 +++++ debian/patches/CVE-2026-34003/0002-xkb-Add-more-_XkbCheckRequestBounds.patch | 218 ++++++++++ xorg-server-21.1.7/debian/changelog | 12 xorg-server-21.1.7/debian/patches/series | 6 8 files changed, 644 insertions(+) diff -u xorg-server-21.1.7/debian/changelog xorg-server-21.1.7/debian/changelog --- xorg-server-21.1.7/debian/changelog +++ xorg-server-21.1.7/debian/changelog @@ -1,3 +1,15 @@ +xorg-server (2:21.1.7-3+deb12u12) bookworm; urgency=medium + + * Non-maintainer upload. + * xkb: fix buffer re-use in _XkbSetCompatMap (CVE-2026-33999) + * xkb: Fix bounds check in _CheckSetGeom() (CVE-2026-34000) + * miext/sync: Fix use-after-free in miSyncTriggerFence() (CVE-2026-34001) + * xkb: Fix out-of-bounds read in CheckModifierMap() (CVE-2026-34002) + * xkb: Add additional bound checking in CheckKeyTypes() (CVE-2026-34003) + * xkb: Add more _XkbCheckRequestBounds() (CVE-2026-34003) + + -- Salvatore Bonaccorso Sat, 11 Apr 2026 02:48:00 +0200 + xorg-server (2:21.1.7-3+deb12u11) bookworm-security; urgency=high * Non-maintainer upload by the Security Team. diff -u xorg-server-21.1.7/debian/patches/series xorg-server-21.1.7/debian/patches/series --- xorg-server-21.1.7/debian/patches/series +++ xorg-server-21.1.7/debian/patches/series @@ -49,3 +49,9 @@ CVE-2025-62230/0001-xkb-Make-the-RT_XKBCLIENT-resource-private.patch CVE-2025-62230/0002-xkb-Free-the-XKB-resource-when-freeing-XkbInterest.patch CVE-2025-62231/0001-xkb-Prevent-overflow-in-XkbSetCompatMap.patch +CVE-2026-33999/0001-xkb-fix-buffer-re-use-in-_XkbSetCompatMap.patch +CVE-2026-34000/0001-xkb-Fix-bounds-check-in-_CheckSetGeom.patch +CVE-2026-34001/0001-miext-sync-Fix-use-after-free-in-miSyncTriggerFence.patch +CVE-2026-34002/0001-xkb-Fix-out-of-bounds-read-in-CheckModifierMap.patch +CVE-2026-34003/0001-xkb-Add-additional-bound-checking-in-CheckKeyTypes.patch +CVE-2026-34003/0002-xkb-Add-more-_XkbCheckRequestBounds.patch only in patch2: unchanged: --- xorg-server-21.1.7.orig/debian/patches/CVE-2026-33999/0001-xkb-fix-buffer-re-use-in-_XkbSetCompatMap.patch +++ xorg-server-21.1.7/debian/patches/CVE-2026-33999/0001-xkb-fix-buffer-re-use-in-_XkbSetCompatMap.patch @@ -0,0 +1,44 @@ +From d4a18e229af79917d09b2a24f38df95374c536e2 Mon Sep 17 00:00:00 2001 +From: Peter Harris +Date: Thu, 15 Jan 2026 15:54:09 -0500 +Subject: [PATCH xserver] xkb: fix buffer re-use in _XkbSetCompatMap + +If the "compat" buffer has previously been truncated, there will be +unused space in the buffer. The code uses this space, but does not +update the number of valid entries in the buffer. + +In the best case, this leads to the new compat entries being ignored. In the +worst case, if there are any "skipped" compat entries, the number of +valid entries will be corrupted, potentially leading to a buffer read +overrun when processing a future request. + +Set the number of used "compat" entries when re-using previously +allocated space in the buffer. + +CVE-2026-33999, ZDI-CAN-28593 + +This vulnerability was discovered by: +Jan-Niklas Sohn working with TrendAI Zero Day Initiative + +Signed-off-by: Peter Harris +Acked-by: Olivier Fourdan +--- + xkb/xkb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index c4ed3fb1e..d31c64113 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -3008,7 +3008,7 @@ _XkbSetCompatMap(ClientPtr client, DeviceIntPtr dev, + return BadAlloc; + } + } +- else if (req->truncateSI) { ++ else if (req->truncateSI || req->firstSI + req->nSI > compat->num_si) { + compat->num_si = req->firstSI + req->nSI; + } + sym = &compat->sym_interpret[req->firstSI]; +-- +2.53.0 + only in patch2: unchanged: --- xorg-server-21.1.7.orig/debian/patches/CVE-2026-34000/0001-xkb-Fix-bounds-check-in-_CheckSetGeom.patch +++ xorg-server-21.1.7/debian/patches/CVE-2026-34000/0001-xkb-Fix-bounds-check-in-_CheckSetGeom.patch @@ -0,0 +1,67 @@ +From b81454f29d7e408d682ef1371ae1df63871cd7ed Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Wed, 18 Feb 2026 16:03:11 +0100 +Subject: [PATCH xserver] xkb: Fix bounds check in _CheckSetGeom() + +As reported by valgrind: + + == Conditional jump or move depends on uninitialised value(s) + == at 0x5CBE66: SrvXkbAddGeomKeyAlias (XKBGAlloc.c:585) + == by 0x5AC7D5: _CheckSetGeom (xkb.c:5607) + == by 0x5AC952: _XkbSetGeometry (xkb.c:5643) + == by 0x5ACB58: ProcXkbSetGeometry (xkb.c:5684) + == by 0x5B0DAC: ProcXkbDispatch (xkb.c:7070) + == by 0x4A28C5: Dispatch (dispatch.c:553) + == by 0x4B0B24: dix_main (main.c:274) + == by 0x42915E: main (stubmain.c:34) + == Uninitialised value was created by a heap allocation + == at 0x4840B26: malloc (vg_replace_malloc.c:447) + == by 0x5E13B0: AllocateInputBuffer (io.c:981) + == by 0x5E05CD: InsertFakeRequest (io.c:516) + == by 0x4AA860: NextAvailableClient (dispatch.c:3629) + == by 0x5DE0D7: AllocNewConnection (connection.c:628) + == by 0x5DE2C6: EstablishNewConnections (connection.c:692) + == by 0x5DE600: HandleNotifyFd (connection.c:809) + == by 0x5E2598: ospoll_wait (ospoll.c:660) + == by 0x5DA00C: WaitForSomething (WaitFor.c:208) + == by 0x4A26E5: Dispatch (dispatch.c:493) + == by 0x4B0B24: dix_main (main.c:274) + == by 0x42915E: main (stubmain.c:34) + +Each key alias entry contains two key names (the alias and the real key +name), each of size XkbKeyNameLength. + +The current bounds check only validates the first name, allowing +XkbAddGeomKeyAlias to potentially read uninitialized memory when +accessing the second name at &wire[XkbKeyNameLength]. + +To fix this, change the value to check to use 2 * XkbKeyNameLength to +validate the bounds. + +CVE-2026-34000, ZDI-CAN-28679 + +This vulnerability was discovered by: +Jan-Niklas Sohn working with TrendAI Zero Day Initiative + +Signed-off-by: Olivier Fourdan +Acked-by: Peter Hutterer +--- + xkb/xkb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index d31c64113..d2b15b485 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -5613,7 +5613,7 @@ _CheckSetGeom(XkbGeometryPtr geom, xkbSetGeometryReq * req, ClientPtr client) + } + + for (i = 0; i < req->nKeyAliases; i++) { +- if (!_XkbCheckRequestBounds(client, req, wire, wire + XkbKeyNameLength)) ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 2 * XkbKeyNameLength)) + return BadLength; + + if (XkbAddGeomKeyAlias(geom, &wire[XkbKeyNameLength], wire) == NULL) +-- +2.53.0 + only in patch2: unchanged: --- xorg-server-21.1.7.orig/debian/patches/CVE-2026-34001/0001-miext-sync-Fix-use-after-free-in-miSyncTriggerFence.patch +++ xorg-server-21.1.7/debian/patches/CVE-2026-34001/0001-miext-sync-Fix-use-after-free-in-miSyncTriggerFence.patch @@ -0,0 +1,100 @@ +From e684fb34a858191769ff1c064bd4ab40f6ff7189 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Wed, 18 Feb 2026 16:23:23 +0100 +Subject: [PATCH xserver] miext/sync: Fix use-after-free in + miSyncTriggerFence() + +As reported by valgrind: + + == Invalid read of size 8 + == at 0x568C14: miSyncTriggerFence (misync.c:140) + == by 0x540688: ProcSyncTriggerFence (sync.c:1957) + == by 0x540CCC: ProcSyncDispatch (sync.c:2152) + == by 0x4A28C5: Dispatch (dispatch.c:553) + == by 0x4B0B24: dix_main (main.c:274) + == by 0x42915E: main (stubmain.c:34) + == Address 0x17e35488 is 8 bytes inside a block of size 16 free'd + == at 0x4843E43: free (vg_replace_malloc.c:990) + == by 0x53D683: SyncDeleteTriggerFromSyncObject (sync.c:169) + == by 0x53F14D: FreeAwait (sync.c:1208) + == by 0x4DFB06: doFreeResource (resource.c:888) + == by 0x4DFC59: FreeResource (resource.c:918) + == by 0x53E349: SyncAwaitTriggerFired (sync.c:701) + == by 0x568C52: miSyncTriggerFence (misync.c:142) + == by 0x540688: ProcSyncTriggerFence (sync.c:1957) + == by 0x540CCC: ProcSyncDispatch (sync.c:2152) + == by 0x4A28C5: Dispatch (dispatch.c:553) + == by 0x4B0B24: dix_main (main.c:274) + == by 0x42915E: main (stubmain.c:34) + == Block was alloc'd at + == at 0x4840B26: malloc (vg_replace_malloc.c:447) + == by 0x5E50E1: XNFalloc (utils.c:1129) + == by 0x53D772: SyncAddTriggerToSyncObject (sync.c:206) + == by 0x53DCA8: SyncInitTrigger (sync.c:414) + == by 0x5409C7: ProcSyncAwaitFence (sync.c:2089) + == by 0x540D04: ProcSyncDispatch (sync.c:2160) + == by 0x4A28C5: Dispatch (dispatch.c:553) + == by 0x4B0B24: dix_main (main.c:274) + == by 0x42915E: main (stubmain.c:34) + +When walking the list of fences to trigger, miSyncTriggerFence() may +call TriggerFence() for the current trigger, which end up calling the +function SyncAwaitTriggerFired(). + +SyncAwaitTriggerFired() frees the entire await resource, which removes +all triggers from that await - including pNext which may be another +trigger from the same await attached to the same fence. + +On the next iteration, ptl = pNext points to freed memory... + +To avoid the issue, we need to restart the iteration from the beginning +of the list each time a trigger fires, since the callback can modify the +list. + +CVE-2026-34001, ZDI-CAN-28706 + +This vulnerability was discovered by: +Jan-Niklas Sohn working with TrendAI Zero Day Initiative + +Signed-off-by: Olivier Fourdan +Acked-by: Peter Hutterer +--- + miext/sync/misync.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/miext/sync/misync.c b/miext/sync/misync.c +index 0931803f6..9a6fbbd4a 100644 +--- a/miext/sync/misync.c ++++ b/miext/sync/misync.c +@@ -131,16 +131,22 @@ miSyncDestroyFence(SyncFence * pFence) + void + miSyncTriggerFence(SyncFence * pFence) + { +- SyncTriggerList *ptl, *pNext; ++ SyncTriggerList *ptl; ++ Bool triggered; + + pFence->funcs.SetTriggered(pFence); + + /* run through triggers to see if any fired */ +- for (ptl = pFence->sync.pTriglist; ptl; ptl = pNext) { +- pNext = ptl->next; +- if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, 0)) +- (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); +- } ++ do { ++ triggered = FALSE; ++ for (ptl = pFence->sync.pTriglist; ptl; ptl = ptl->next) { ++ if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, 0)) { ++ (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); ++ triggered = TRUE; ++ break; ++ } ++ } ++ } while (triggered); + } + + SyncScreenFuncsPtr +-- +2.53.0 + only in patch2: unchanged: --- xorg-server-21.1.7.orig/debian/patches/CVE-2026-34002/0001-xkb-Fix-out-of-bounds-read-in-CheckModifierMap.patch +++ xorg-server-21.1.7/debian/patches/CVE-2026-34002/0001-xkb-Fix-out-of-bounds-read-in-CheckModifierMap.patch @@ -0,0 +1,88 @@ +From 822657b357048ddedfb4e8313c7f1a69295240c5 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Wed, 18 Feb 2026 17:02:09 +0100 +Subject: [PATCH xserver] xkb: Fix out-of-bounds read in CheckModifierMap() + +As reported by valgrind: + + == Conditional jump or move depends on uninitialised value(s) + == at 0x547E5B: CheckModifierMap (xkb.c:1972) + == by 0x54A086: _XkbSetMapChecks (xkb.c:2574) + == by 0x54A845: ProcXkbSetMap (xkb.c:2741) + == by 0x556EF4: ProcXkbDispatch (xkb.c:7048) + == by 0x454A8C: Dispatch (dispatch.c:553) + == by 0x462CEB: dix_main (main.c:274) + == by 0x405EA7: main (stubmain.c:34) + == Uninitialised value was created by a heap allocation + == at 0x4840B26: malloc (vg_replace_malloc.c:447) + == by 0x592D5A: AllocateInputBuffer (io.c:981) + == by 0x591F77: InsertFakeRequest (io.c:516) + == by 0x45CA27: NextAvailableClient (dispatch.c:3629) + == by 0x58FA81: AllocNewConnection (connection.c:628) + == by 0x58FC70: EstablishNewConnections (connection.c:692) + == by 0x58FFAA: HandleNotifyFd (connection.c:809) + == by 0x593F42: ospoll_wait (ospoll.c:660) + == by 0x58B9B6: WaitForSomething (WaitFor.c:208) + == by 0x4548AC: Dispatch (dispatch.c:493) + == by 0x462CEB: dix_main (main.c:274) + == by 0x405EA7: main (stubmain.c:34) + +The issue is that the loop in CheckModifierMap() reads from wire without +verifying that the data is within the request bounds. + +The req->totalModMapKeys value could exceed the actual data provided, +causing reads of uninitialized memory. + +To fix that issue, we add a bounds check using _XkbCheckRequestBounds, +but for that, we need to also pass a ClientPtr parameter, which is not +a problem since CheckModifierMap() is a private, static function. + +CVE-2026-34002, ZDI-CAN-28737 + +This vulnerability was discovered by: +Jan-Niklas Sohn working with Trend Micro Zero Day Initiative + +Signed-off-by: Olivier Fourdan +Acked-by: Peter Hutterer +--- + xkb/xkb.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index d2b15b485..b03d6a264 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -1944,8 +1944,8 @@ CheckKeyExplicit(XkbDescPtr xkb, + } + + static int +-CheckModifierMap(XkbDescPtr xkb, xkbSetMapReq * req, CARD8 **wireRtrn, +- int *errRtrn) ++CheckModifierMap(ClientPtr client, XkbDescPtr xkb, xkbSetMapReq * req, ++ CARD8 **wireRtrn, int *errRtrn) + { + register CARD8 *wire = *wireRtrn; + CARD8 *start; +@@ -1969,6 +1969,10 @@ CheckModifierMap(XkbDescPtr xkb, xkbSetMapReq * req, CARD8 **wireRtrn, + } + start = wire; + for (i = 0; i < req->totalModMapKeys; i++, wire += 2) { ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 2)) { ++ *errRtrn = _XkbErrCode3(0x64, req->totalModMapKeys, i); ++ return 0; ++ } + if ((wire[0] < first) || (wire[0] > last)) { + *errRtrn = _XkbErrCode4(0x63, first, last, wire[0]); + return 0; +@@ -2571,7 +2575,7 @@ _XkbSetMapChecks(ClientPtr client, DeviceIntPtr dev, xkbSetMapReq * req, + return BadValue; + } + if ((req->present & XkbModifierMapMask) && +- (!CheckModifierMap(xkb, req, (CARD8 **) &values, &error))) { ++ (!CheckModifierMap(client, xkb, req, (CARD8 **) &values, &error))) { + client->errorValue = error; + return BadValue; + } +-- +2.53.0 + only in patch2: unchanged: --- xorg-server-21.1.7.orig/debian/patches/CVE-2026-34003/0001-xkb-Add-additional-bound-checking-in-CheckKeyTypes.patch +++ xorg-server-21.1.7/debian/patches/CVE-2026-34003/0001-xkb-Add-additional-bound-checking-in-CheckKeyTypes.patch @@ -0,0 +1,109 @@ +From d41409dfc193d1a2ca84f19ad59b2b84de68c3e0 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 23 Feb 2026 15:52:49 +0100 +Subject: [PATCH xserver 1/2] xkb: Add additional bound checking in + CheckKeyTypes() + +The function CheckKeyTypes() will loop over the client's request but +won't perform any additional bound checking to ensure that the data +read remains within the request bounds. + +As a result, a specifically crafted request may cause CheckKeyTypes() to +read past the request data, as reported by valgrind: + + == Invalid read of size 2 + == at 0x5A3D1D: CheckKeyTypes (xkb.c:1694) + == by 0x5A6A9C: _XkbSetMapChecks (xkb.c:2515) + == by 0x5A759E: ProcXkbSetMap (xkb.c:2736) + == by 0x5BF832: SProcXkbSetMap (xkbSwap.c:245) + == by 0x5C05ED: SProcXkbDispatch (xkbSwap.c:501) + == by 0x4A20DF: Dispatch (dispatch.c:551) + == by 0x4B03B4: dix_main (main.c:277) + == by 0x428941: main (stubmain.c:34) + == Address is 30 bytes after a block of size 28,672 in arena "client" + == + == Invalid read of size 2 + == at 0x5A3AB6: CheckKeyTypes (xkb.c:1669) + == by 0x5A6A9C: _XkbSetMapChecks (xkb.c:2515) + == by 0x5A759E: ProcXkbSetMap (xkb.c:2736) + == by 0x5BF832: SProcXkbSetMap (xkbSwap.c:245) + == by 0x5C05ED: SProcXkbDispatch (xkbSwap.c:501) + == by 0x4A20DF: Dispatch (dispatch.c:551) + == by 0x4B03B4: dix_main (main.c:277) + == by 0x428941: main (stubmain.c:34) + == Address is 2 bytes after a block of size 28,672 alloc'd + == at 0x4848897: realloc (vg_replace_malloc.c:1804) + == by 0x5E357A: ReadRequestFromClient (io.c:336) + == by 0x4A1FAB: Dispatch (dispatch.c:519) + == by 0x4B03B4: dix_main (main.c:277) + == by 0x428941: main (stubmain.c:34) + == + == Invalid write of size 2 + == at 0x5A3AD7: CheckKeyTypes (xkb.c:1669) + == by 0x5A6A9C: _XkbSetMapChecks (xkb.c:2515) + == by 0x5A759E: ProcXkbSetMap (xkb.c:2736) + == by 0x5BF832: SProcXkbSetMap (xkbSwap.c:245) + == by 0x5C05ED: SProcXkbDispatch (xkbSwap.c:501) + == by 0x4A20DF: Dispatch (dispatch.c:551) + == by 0x4B03B4: dix_main (main.c:277) + == by 0x428941: main (stubmain.c:34) + == Address is 2 bytes after a block of size 28,672 alloc'd + == at 0x4848897: realloc (vg_replace_malloc.c:1804) + == by 0x5E357A: ReadRequestFromClient (io.c:336) + == by 0x4A1FAB: Dispatch (dispatch.c:519) + == by 0x4B03B4: dix_main (main.c:277) + == by 0x428941: main (stubmain.c:34) + == + +To avoid that issue, add additional bounds checking within the loops by +calling _XkbCheckRequestBounds() and report an error if we are to read +past the client's request. + +CVE-2026-34003, ZDI-CAN-28736 + +This vulnerability was discovered by: +Jan-Niklas Sohn working with TrendAI Zero Day Initiative + +Signed-off-by: Olivier Fourdan +Acked-by: Peter Hutterer +--- + xkb/xkb.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index b03d6a264..a90845230 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -1643,6 +1643,10 @@ CheckKeyTypes(ClientPtr client, + for (i = 0; i < req->nTypes; i++) { + unsigned width; + ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 1)) { ++ *nMapsRtrn = _XkbErrCode3(0x0b, req->nTypes, i); ++ return 0; ++ } + if (client->swapped && doswap) { + swaps(&wire->virtualMods); + } +@@ -1668,7 +1672,18 @@ CheckKeyTypes(ClientPtr client, + xkbModsWireDesc *preWire; + + mapWire = (xkbKTSetMapEntryWireDesc *) &wire[1]; ++ if (!_XkbCheckRequestBounds(client, req, mapWire, ++ &mapWire[wire->nMapEntries])) { ++ *nMapsRtrn = _XkbErrCode3(0x0c, i, wire->nMapEntries); ++ return 0; ++ } + preWire = (xkbModsWireDesc *) &mapWire[wire->nMapEntries]; ++ if (wire->preserve && ++ !_XkbCheckRequestBounds(client, req, preWire, ++ &preWire[wire->nMapEntries])) { ++ *nMapsRtrn = _XkbErrCode3(0x0d, i, wire->nMapEntries); ++ return 0; ++ } + for (n = 0; n < wire->nMapEntries; n++) { + if (client->swapped && doswap) { + swaps(&mapWire[n].virtualMods); +-- +2.53.0 + only in patch2: unchanged: --- xorg-server-21.1.7.orig/debian/patches/CVE-2026-34003/0002-xkb-Add-more-_XkbCheckRequestBounds.patch +++ xorg-server-21.1.7/debian/patches/CVE-2026-34003/0002-xkb-Add-more-_XkbCheckRequestBounds.patch @@ -0,0 +1,218 @@ +From a44b3d0bc1b80beeac50f5b52fb3f4cc07e77f64 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 2 Mar 2026 14:09:57 +0100 +Subject: [PATCH xserver 2/2] xkb: Add more _XkbCheckRequestBounds() + +Similar to the recent fixes, add more _XkbCheckRequestBounds() to the +functions that loop over the request data, i.e.: + + * CheckKeySyms() + * CheckKeyActions() + * CheckKeyBehaviors() + * CheckVirtualMods() + * CheckKeyExplicit() + * CheckVirtualModMap() + * _XkbSetMapChecks() + +All these are static functions so we can add the client to the parameters +without breaking any API. + +See also: +CVE-2026-34003, ZDI-CAN-28736, CVE-2026-34002, ZDI-CAN-28737 + +v2: Check for "nSyms != 0" in CheckKeySyms() to avoid false positives. + +Signed-off-by: Olivier Fourdan +Acked-by: Peter Hutterer +--- + xkb/xkb.c | 69 ++++++++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 55 insertions(+), 14 deletions(-) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index a90845230..652996946 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -1756,6 +1756,11 @@ CheckKeySyms(ClientPtr client, + KeySym *pSyms; + register unsigned nG; + ++ /* Check we received enough data to read the next xkbSymMapWireDesc */ ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 1)) { ++ *errorRtrn = _XkbErrCode3(0x18, i + req->firstKeySym, i); ++ return 0; ++ } + if (client->swapped && doswap) { + swaps(&wire->nSyms); + } +@@ -1794,6 +1799,12 @@ CheckKeySyms(ClientPtr client, + return 0; + } + pSyms = (KeySym *) &wire[1]; ++ if (wire->nSyms != 0) { ++ if (!_XkbCheckRequestBounds(client, req, pSyms, &pSyms[wire->nSyms])) { ++ *errorRtrn = _XkbErrCode3(0x19, i + req->firstKeySym, wire->nSyms); ++ return 0; ++ } ++ } + wire = (xkbSymMapWireDesc *) &pSyms[wire->nSyms]; + } + +@@ -1817,11 +1828,12 @@ CheckKeySyms(ClientPtr client, + } + + static int +-CheckKeyActions(XkbDescPtr xkb, +- xkbSetMapReq * req, +- int nTypes, +- CARD8 *mapWidths, +- CARD16 *symsPerKey, CARD8 **wireRtrn, int *nActsRtrn) ++CheckKeyActions(ClientPtr client, ++ XkbDescPtr xkb, ++ xkbSetMapReq * req, ++ int nTypes, ++ CARD8 *mapWidths, ++ CARD16 *symsPerKey, CARD8 **wireRtrn, int *nActsRtrn) + { + int nActs; + CARD8 *wire = *wireRtrn; +@@ -1832,6 +1844,11 @@ CheckKeyActions(XkbDescPtr xkb, + CHK_REQ_KEY_RANGE2(0x21, req->firstKeyAct, req->nKeyActs, req, (*nActsRtrn), + 0); + for (nActs = i = 0; i < req->nKeyActs; i++) { ++ /* Check we received enough data to read the next byte on the wire */ ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 1)) { ++ *nActsRtrn = _XkbErrCode3(0x24, i + req->firstKeyAct, i); ++ return 0; ++ } + if (wire[0] != 0) { + if (wire[0] == symsPerKey[i + req->firstKeyAct]) + nActs += wire[0]; +@@ -1850,7 +1867,8 @@ CheckKeyActions(XkbDescPtr xkb, + } + + static int +-CheckKeyBehaviors(XkbDescPtr xkb, ++CheckKeyBehaviors(ClientPtr client, ++ XkbDescPtr xkb, + xkbSetMapReq * req, + xkbBehaviorWireDesc ** wireRtrn, int *errorRtrn) + { +@@ -1876,6 +1894,11 @@ CheckKeyBehaviors(XkbDescPtr xkb, + } + + for (i = 0; i < req->totalKeyBehaviors; i++, wire++) { ++ /* Check we received enough data to read the next behavior */ ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 1)) { ++ *errorRtrn = _XkbErrCode3(0x36, first, i); ++ return 0; ++ } + if ((wire->key < first) || (wire->key > last)) { + *errorRtrn = _XkbErrCode4(0x33, first, last, wire->key); + return 0; +@@ -1901,7 +1924,8 @@ CheckKeyBehaviors(XkbDescPtr xkb, + } + + static int +-CheckVirtualMods(XkbDescRec * xkb, ++CheckVirtualMods(ClientPtr client, ++ XkbDescRec * xkb, + xkbSetMapReq * req, CARD8 **wireRtrn, int *errorRtrn) + { + register CARD8 *wire = *wireRtrn; +@@ -1913,12 +1937,18 @@ CheckVirtualMods(XkbDescRec * xkb, + if (req->virtualMods & bit) + nMods++; + } ++ /* Check we received enough data for the number of virtual mods expected */ ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + XkbPaddedSize(nMods))) { ++ *errorRtrn = _XkbErrCode3(0x37, nMods, i); ++ return 0; ++ } + *wireRtrn = (wire + XkbPaddedSize(nMods)); + return 1; + } + + static int +-CheckKeyExplicit(XkbDescPtr xkb, ++CheckKeyExplicit(ClientPtr client, ++ XkbDescPtr xkb, + xkbSetMapReq * req, CARD8 **wireRtrn, int *errorRtrn) + { + register CARD8 *wire = *wireRtrn; +@@ -1944,6 +1974,11 @@ CheckKeyExplicit(XkbDescPtr xkb, + } + start = wire; + for (i = 0; i < req->totalKeyExplicit; i++, wire += 2) { ++ /* Check we received enough data to read the next two bytes */ ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 2)) { ++ *errorRtrn = _XkbErrCode4(0x54, first, last, i); ++ return 0; ++ } + if ((wire[0] < first) || (wire[0] > last)) { + *errorRtrn = _XkbErrCode4(0x53, first, last, wire[0]); + return 0; +@@ -1999,7 +2034,8 @@ CheckModifierMap(ClientPtr client, XkbDescPtr xkb, xkbSetMapReq * req, + } + + static int +-CheckVirtualModMap(XkbDescPtr xkb, ++CheckVirtualModMap(ClientPtr client, ++ XkbDescPtr xkb, + xkbSetMapReq * req, + xkbVModMapWireDesc ** wireRtrn, int *errRtrn) + { +@@ -2023,6 +2059,11 @@ CheckVirtualModMap(XkbDescPtr xkb, + return 0; + } + for (i = 0; i < req->totalVModMapKeys; i++, wire++) { ++ /* Check we received enough data to read the next virtual mod map key */ ++ if (!_XkbCheckRequestBounds(client, req, wire, wire + 1)) { ++ *errRtrn = _XkbErrCode3(0x74, first, i); ++ return 0; ++ } + if ((wire->key < first) || (wire->key > last)) { + *errRtrn = _XkbErrCode4(0x73, first, last, wire->key); + return 0; +@@ -2566,7 +2607,7 @@ _XkbSetMapChecks(ClientPtr client, DeviceIntPtr dev, xkbSetMapReq * req, + } + + if ((req->present & XkbKeyActionsMask) && +- (!CheckKeyActions(xkb, req, nTypes, mapWidths, symsPerKey, ++ (!CheckKeyActions(client, xkb, req, nTypes, mapWidths, symsPerKey, + (CARD8 **) &values, &nActions))) { + client->errorValue = nActions; + return BadValue; +@@ -2574,18 +2615,18 @@ _XkbSetMapChecks(ClientPtr client, DeviceIntPtr dev, xkbSetMapReq * req, + + if ((req->present & XkbKeyBehaviorsMask) && + (!CheckKeyBehaviors +- (xkb, req, (xkbBehaviorWireDesc **) &values, &error))) { ++ (client, xkb, req, (xkbBehaviorWireDesc **) &values, &error))) { + client->errorValue = error; + return BadValue; + } + + if ((req->present & XkbVirtualModsMask) && +- (!CheckVirtualMods(xkb, req, (CARD8 **) &values, &error))) { ++ (!CheckVirtualMods(client, xkb, req, (CARD8 **) &values, &error))) { + client->errorValue = error; + return BadValue; + } + if ((req->present & XkbExplicitComponentsMask) && +- (!CheckKeyExplicit(xkb, req, (CARD8 **) &values, &error))) { ++ (!CheckKeyExplicit(client, xkb, req, (CARD8 **) &values, &error))) { + client->errorValue = error; + return BadValue; + } +@@ -2596,7 +2637,7 @@ _XkbSetMapChecks(ClientPtr client, DeviceIntPtr dev, xkbSetMapReq * req, + } + if ((req->present & XkbVirtualModMapMask) && + (!CheckVirtualModMap +- (xkb, req, (xkbVModMapWireDesc **) &values, &error))) { ++ (client, xkb, req, (xkbVModMapWireDesc **) &values, &error))) { + client->errorValue = error; + return BadValue; + } +-- +2.53.0 +