diff --git a/0001-Cursor-Refuse-to-free-the-root-cursor.patch b/0001-Cursor-Refuse-to-free-the-root-cursor.patch new file mode 100644 index 0000000000000000000000000000000000000000..e8108b75db77a782b451db5d1a90cc5ec1bd47d2 --- /dev/null +++ b/0001-Cursor-Refuse-to-free-the-root-cursor.patch @@ -0,0 +1,55 @@ +From 63e23eb8088c9510151e27dbcf1e9a1c7f5c0183 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Wed, 27 Nov 2024 11:27:05 +0100 +Subject: [PATCH xserver 01/13] Cursor: Refuse to free the root cursor +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 +) +v3: Return BadCursor instead of BadValue (Michel Dänzer +) + +Signed-off-by: Olivier Fourdan +Suggested-by: Peter Hutterer +Reviewed-by: Peter Hutterer +(cherry picked from commit 01642f263f12becf803b19be4db95a4a83f94acc) + +Part-of: +--- + dix/dispatch.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/dix/dispatch.c b/dix/dispatch.c +index eaac39b7c..9e6a6cf51 100644 +--- a/dix/dispatch.c ++++ b/dix/dispatch.c +@@ -3102,6 +3102,10 @@ ProcFreeCursor(ClientPtr client) + rc = dixLookupResourceByType((void **) &pCursor, stuff->id, RT_CURSOR, + client, DixDestroyAccess); + if (rc == Success) { ++ if (pCursor == rootCursor) { ++ client->errorValue = stuff->id; ++ return BadCursor; ++ } + FreeResource(stuff->id, RT_NONE); + return Success; + } +-- +2.48.1 + diff --git a/0001-xkb-Fix-buffer-overflow-in-_XkbSetCompatMap.patch b/0001-xkb-Fix-buffer-overflow-in-_XkbSetCompatMap.patch new file mode 100644 index 0000000000000000000000000000000000000000..dc757364e12e24fc3dade7cf4b368d52bac1c340 --- /dev/null +++ b/0001-xkb-Fix-buffer-overflow-in-_XkbSetCompatMap.patch @@ -0,0 +1,57 @@ +From 26120df7aae6b5bf8086fb4d871d3b1a07ddacdb Mon Sep 17 00:00:00 2001 +From: Matthieu Herrb +Date: Thu, 10 Oct 2024 10:37:28 +0200 +Subject: [PATCH xserver] xkb: Fix buffer overflow in _XkbSetCompatMap() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The _XkbSetCompatMap() function attempts to resize the `sym_interpret` +buffer. + +However, It didn't update its size properly. It updated `num_si` only, +without updating `size_si`. + +This may lead to local privilege escalation if the server is run as root +or remote code execution (e.g. x11 over ssh). + +CVE-2024-9632, ZDI-CAN-24756 + +This vulnerability was discovered by: +Jan-Niklas Sohn working with Trend Micro Zero Day Initiative + +Reviewed-by: Peter Hutterer +Tested-by: Peter Hutterer +Reviewed-by: José Expósito +(cherry picked from commit 85b776571487f52e756f68a069c768757369bfe3) + +Part-of: +--- + xkb/xkb.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index 8d52e25df..8b63e34b5 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -2990,13 +2990,13 @@ _XkbSetCompatMap(ClientPtr client, DeviceIntPtr dev, + XkbSymInterpretPtr sym; + unsigned int skipped = 0; + +- if ((unsigned) (req->firstSI + req->nSI) > compat->num_si) { +- compat->num_si = req->firstSI + req->nSI; ++ if ((unsigned) (req->firstSI + req->nSI) > compat->size_si) { ++ compat->num_si = compat->size_si = req->firstSI + req->nSI; + compat->sym_interpret = reallocarray(compat->sym_interpret, +- compat->num_si, ++ compat->size_si, + sizeof(XkbSymInterpretRec)); + if (!compat->sym_interpret) { +- compat->num_si = 0; ++ compat->num_si = compat->size_si = 0; + return BadAlloc; + } + } +-- +2.47.0 + diff --git a/0002-dix-keep-a-ref-to-the-rootCursor.patch b/0002-dix-keep-a-ref-to-the-rootCursor.patch new file mode 100644 index 0000000000000000000000000000000000000000..ea2c3bbb5a16aa816225424b169399e6141bf8de --- /dev/null +++ b/0002-dix-keep-a-ref-to-the-rootCursor.patch @@ -0,0 +1,49 @@ +From a28f9bd06500db5594605cc042882a3527eb67a9 Mon Sep 17 00:00:00 2001 +From: Peter Hutterer +Date: Wed, 4 Dec 2024 15:49:43 +1000 +Subject: [PATCH xserver 02/13] 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 +(cherry picked from commit b0a09ba6020147961acc62d9c73d807b4cccd9f7) + +Part-of: +--- + dix/main.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/dix/main.c b/dix/main.c +index dd0cfba06..982234965 100644 +--- a/dix/main.c ++++ b/dix/main.c +@@ -230,6 +230,8 @@ dix_main(int argc, char *argv[], char *envp[]) + FatalError("could not open default cursor font"); + } + ++ rootCursor = RefCursor(rootCursor); ++ + #ifdef PANORAMIX + /* + * Consolidate window and colourmap information for each screen +@@ -270,6 +272,8 @@ dix_main(int argc, char *argv[], char *envp[]) + + Dispatch(); + ++ UnrefCursor(rootCursor); ++ + UndisplayDevices(); + DisableAllDevices(); + +-- +2.48.1 + diff --git a/0003-xkb-Fix-buffer-overflow-in-XkbVModMaskText.patch b/0003-xkb-Fix-buffer-overflow-in-XkbVModMaskText.patch new file mode 100644 index 0000000000000000000000000000000000000000..f8ed8c7ab36efd05285bfeca502f8315a6e284eb --- /dev/null +++ b/0003-xkb-Fix-buffer-overflow-in-XkbVModMaskText.patch @@ -0,0 +1,63 @@ +From 84adfa4caf2b62f391bb8911fcb43ab4287a34f9 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Wed, 27 Nov 2024 14:41:45 +0100 +Subject: [PATCH xserver 03/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit 11fcda8753e994e15eb915d28cf487660ec8e722) + +Part-of: +--- + xkb/xkbtext.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/xkb/xkbtext.c b/xkb/xkbtext.c +index fb17f513e..62fbf288a 100644 +--- a/xkb/xkbtext.c ++++ b/xkb/xkbtext.c +@@ -175,14 +175,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); +-- +2.48.1 + diff --git a/0004-xkb-Fix-computation-of-XkbSizeKeySyms.patch b/0004-xkb-Fix-computation-of-XkbSizeKeySyms.patch new file mode 100644 index 0000000000000000000000000000000000000000..d1e4a7445fb7957bf602f2afd836478ce8b9aeec --- /dev/null +++ b/0004-xkb-Fix-computation-of-XkbSizeKeySyms.patch @@ -0,0 +1,47 @@ +From 63f84d443aca15728c37d348c778b36d9041bd94 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Thu, 28 Nov 2024 11:49:34 +0100 +Subject: [PATCH xserver 04/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit 80d69f01423fc065c950e1ff4e8ddf9f675df773) + +Part-of: +--- + xkb/xkb.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xkb/xkb.c b/xkb/xkb.c +index f9e3acbdc..c9a3ee300 100644 +--- a/xkb/xkb.c ++++ b/xkb/xkb.c +@@ -1093,10 +1093,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; +-- +2.48.1 + diff --git a/0005-xkb-Fix-buffer-overflow-in-XkbChangeTypesOfKey.patch b/0005-xkb-Fix-buffer-overflow-in-XkbChangeTypesOfKey.patch new file mode 100644 index 0000000000000000000000000000000000000000..17186c685535481ef4e17a6070b1f255ae8a2dd4 --- /dev/null +++ b/0005-xkb-Fix-buffer-overflow-in-XkbChangeTypesOfKey.patch @@ -0,0 +1,45 @@ +From 05c8d1ccd26caa2c790f6819d58e1dc77dda98c1 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Thu, 28 Nov 2024 14:09:04 +0100 +Subject: [PATCH xserver 05/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit 0e4ed94952b255c04fe910f6a1d9c852878dcd64) + +Part-of: +--- + xkb/XKBMisc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/xkb/XKBMisc.c b/xkb/XKBMisc.c +index 2bad695e0..32429daf3 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; + } + +-- +2.48.1 + diff --git a/0006-Xi-Fix-barrier-device-search.patch b/0006-Xi-Fix-barrier-device-search.patch new file mode 100644 index 0000000000000000000000000000000000000000..63ddd68af752b8b31d73e7edf2ff3268b585d81b --- /dev/null +++ b/0006-Xi-Fix-barrier-device-search.patch @@ -0,0 +1,118 @@ +From e70ec8fd0f4a94a9132caaff2beadf411964ab06 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 16 Dec 2024 11:25:11 +0100 +Subject: [PATCH xserver 06/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit bba9df1a9d57234c76c0b93f88dacb143d01bca2) + +Part-of: +--- + Xi/xibarriers.c | 27 +++++++++++++++++++++++---- + 1 file changed, 23 insertions(+), 4 deletions(-) + +diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c +index 1926762ad..cb336f22b 100644 +--- a/Xi/xibarriers.c ++++ b/Xi/xibarriers.c +@@ -129,14 +129,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; + } + +@@ -337,6 +338,9 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev, + double distance; + + pbd = GetBarrierDevice(c, dev->id); ++ if (!pbd) ++ continue; ++ + if (pbd->seen) + continue; + +@@ -445,6 +449,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; +@@ -485,6 +492,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; +@@ -679,6 +689,9 @@ BarrierFreeBarrier(void *data, XID id) + continue; + + pbd = GetBarrierDevice(c, dev->id); ++ if (!pbd) ++ continue; ++ + if (!pbd->hit) + continue; + +@@ -738,6 +751,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 = { +@@ -903,6 +918,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; +-- +2.48.1 + diff --git a/0007-composite-Handle-failure-to-redirect-in-compRedirect.patch b/0007-composite-Handle-failure-to-redirect-in-compRedirect.patch new file mode 100644 index 0000000000000000000000000000000000000000..cf6b48330b2f040b476ed449f3d38f907c5c1e09 --- /dev/null +++ b/0007-composite-Handle-failure-to-redirect-in-compRedirect.patch @@ -0,0 +1,65 @@ +From d57ddec12566e81c93205ef7bec90f203a5b213f Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 17 Dec 2024 15:19:45 +0100 +Subject: [PATCH xserver 07/13] 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 +Acked-by: Peter Hutterer +(cherry picked from commit c1ff84bef2569b4ba4be59323cf575d1798ba9be) + +Part-of: +--- + composite/compalloc.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/composite/compalloc.c b/composite/compalloc.c +index acd215002..83894aa1f 100644 +--- a/composite/compalloc.c ++++ b/composite/compalloc.c +@@ -140,6 +140,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; +@@ -218,13 +219,13 @@ compRedirectWindow(ClientPtr pClient, WindowPtr pWin, int update) + + if (!compCheckRedirect(pWin)) { + FreeResource(ccw->id, RT_NONE); +- return BadAlloc; ++ status = BadAlloc; + } + + if (anyMarked) + compHandleMarkedWindows(pWin, pLayerWin); + +- return Success; ++ return status; + } + + void +-- +2.48.1 + diff --git a/0008-composite-initialize-border-clip-even-when-pixmap-al.patch b/0008-composite-initialize-border-clip-even-when-pixmap-al.patch new file mode 100644 index 0000000000000000000000000000000000000000..a0aac324fb870997cbac3354a9275580f5abf5ba --- /dev/null +++ b/0008-composite-initialize-border-clip-even-when-pixmap-al.patch @@ -0,0 +1,127 @@ +From 573d3e49a3da1305f228bb381014934b35d618c7 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 13 Jan 2025 16:09:43 +0100 +Subject: [PATCH xserver 08/13] 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 +Acked-by: Peter Hutterer +(cherry picked from commit b07192a8bedb90b039dc0f70ae69daf047ff9598) + +Part-of: +--- + composite/compalloc.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/composite/compalloc.c b/composite/compalloc.c +index 83894aa1f..ba3168480 100644 +--- a/composite/compalloc.c ++++ b/composite/compalloc.c +@@ -606,9 +606,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 +@@ -622,14 +625,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 +-- +2.48.1 + diff --git a/0009-dix-Dequeue-pending-events-on-frozen-device-on-remov.patch b/0009-dix-Dequeue-pending-events-on-frozen-device-on-remov.patch new file mode 100644 index 0000000000000000000000000000000000000000..872e7725ad0319ca00752daae4464a8e0e199bd8 --- /dev/null +++ b/0009-dix-Dequeue-pending-events-on-frozen-device-on-remov.patch @@ -0,0 +1,67 @@ +From fb8a3c1389a012f9a99fe4be021a22ab22bebc47 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 16 Dec 2024 16:18:04 +0100 +Subject: [PATCH xserver 09/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit 6e0f332ba4c8b8c9a9945dc9d7989bfe06f80e14) + +Part-of: +--- + dix/devices.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/dix/devices.c b/dix/devices.c +index 95190b5b7..440a00b58 100644 +--- a/dix/devices.c ++++ b/dix/devices.c +@@ -969,6 +969,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 +@@ -1033,6 +1050,7 @@ CloseDevice(DeviceIntPtr dev) + free(dev->last.touches[j].valuators); + free(dev->last.touches); + dev->config_info = NULL; ++ FreePendingFrozenDeviceEvents(dev); + dixFreePrivates(dev->devPrivates, PRIVATE_DEVICE); + free(dev); + } +-- +2.48.1 + diff --git a/0010-sync-Do-not-let-sync-objects-uninitialized.patch b/0010-sync-Do-not-let-sync-objects-uninitialized.patch new file mode 100644 index 0000000000000000000000000000000000000000..af8155fc45f32f3273d86f383820b09f26f05a36 --- /dev/null +++ b/0010-sync-Do-not-let-sync-objects-uninitialized.patch @@ -0,0 +1,69 @@ +From 08b4c950282d86df3c09813e36b7a8768a24c213 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 20 Jan 2025 16:52:01 +0100 +Subject: [PATCH xserver 10/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit 16a1242d0ffc7f45ed3c595ee7564b5c04287e0b) + +Part-of: +--- + Xext/sync.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/Xext/sync.c b/Xext/sync.c +index fd2ceb042..e55295904 100644 +--- a/Xext/sync.c ++++ b/Xext/sync.c +@@ -329,11 +329,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 */ +@@ -401,6 +396,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 + */ +-- +2.48.1 + diff --git a/0011-sync-Check-values-before-applying-changes.patch b/0011-sync-Check-values-before-applying-changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..48bc5c727505ac5dc28659db32f4b56c52282d1e --- /dev/null +++ b/0011-sync-Check-values-before-applying-changes.patch @@ -0,0 +1,83 @@ +From 1722d5d08408b5d7b98a3bc7363494f9aa4afc3b Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 20 Jan 2025 16:54:30 +0100 +Subject: [PATCH xserver 11/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit f52cea2f93a0c891494eb3334894442a92368030) + +Part-of: +--- + Xext/sync.c | 36 ++++++++++++++++++------------------ + 1 file changed, 18 insertions(+), 18 deletions(-) + +diff --git a/Xext/sync.c b/Xext/sync.c +index e55295904..66a52283d 100644 +--- a/Xext/sync.c ++++ b/Xext/sync.c +@@ -350,6 +350,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) { +@@ -378,24 +396,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); +-- +2.48.1 + diff --git a/0012-sync-Do-not-fail-SyncAddTriggerToSyncObject.patch b/0012-sync-Do-not-fail-SyncAddTriggerToSyncObject.patch new file mode 100644 index 0000000000000000000000000000000000000000..42966d728dc4d0f079cc2431f948e4fa63f64f28 --- /dev/null +++ b/0012-sync-Do-not-fail-SyncAddTriggerToSyncObject.patch @@ -0,0 +1,50 @@ +From b7965e00bb96304f49eac09dad1f672d8ef2e167 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 20 Jan 2025 17:06:07 +0100 +Subject: [PATCH xserver 12/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit 8cbc90c8817306af75a60f494ec9dbb1061e50db) + +Part-of: +--- + Xext/sync.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/Xext/sync.c b/Xext/sync.c +index 66a52283d..8def4adbf 100644 +--- a/Xext/sync.c ++++ b/Xext/sync.c +@@ -199,8 +199,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; +@@ -408,8 +408,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); +-- +2.48.1 + diff --git a/0013-sync-Apply-changes-last-in-SyncChangeAlarmAttributes.patch b/0013-sync-Apply-changes-last-in-SyncChangeAlarmAttributes.patch new file mode 100644 index 0000000000000000000000000000000000000000..193f5c2abeb3d7b603e50669f421912eeb0b8f56 --- /dev/null +++ b/0013-sync-Apply-changes-last-in-SyncChangeAlarmAttributes.patch @@ -0,0 +1,131 @@ +From cea49024d71b530b5a3717d528d4b4ee0f203625 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 20 Jan 2025 17:10:31 +0100 +Subject: [PATCH xserver 13/13] 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 +Reviewed-by: Peter Hutterer +(cherry picked from commit c285798984c6bb99e454a33772cde23d394d3dcd) + +Part-of: +--- + Xext/sync.c | 42 +++++++++++++++++++++++++++--------------- + 1 file changed, 27 insertions(+), 15 deletions(-) + +diff --git a/Xext/sync.c b/Xext/sync.c +index 8def4adbf..e2f2c2774 100644 +--- a/Xext/sync.c ++++ b/Xext/sync.c +@@ -799,8 +799,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); +@@ -816,24 +822,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; + +@@ -843,10 +849,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: +@@ -855,25 +859,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; +-- +2.48.1 + diff --git a/xorg-x11-server-Xwayland.spec b/xorg-x11-server-Xwayland.spec index 30908e1b390744fa91483f4b8070768173d6c7c1..3fe21219c094065eea6fbeb1f5c87cd828d1d4db 100644 --- a/xorg-x11-server-Xwayland.spec +++ b/xorg-x11-server-Xwayland.spec @@ -9,7 +9,7 @@ Summary: Xwayland Name: xorg-x11-server-Xwayland Version: 23.2.7 -Release: 1%{?gitdate:.%{gitdate}git%{shortcommit}}%{?dist} +Release: 3%{?gitdate:.%{gitdate}git%{shortcommit}}%{?dist} URL: http://www.x.org %if 0%{?gitdate} @@ -18,6 +18,30 @@ Source0: https://gitlab.freedesktop.org/xorg/%{pkgname}/-/archive/%{commit}/%{ Source0: https://www.x.org/pub/individual/xserver/%{pkgname}-%{version}.tar.xz %endif +# Fix for CVE-2024-9632 +Patch1: 0001-xkb-Fix-buffer-overflow-in-_XkbSetCompatMap.patch +# CVE-2025-26594: Use-after-free of the root cursor +Patch2: 0001-Cursor-Refuse-to-free-the-root-cursor.patch +Patch3: 0002-dix-keep-a-ref-to-the-rootCursor.patch +# CVE-2025-26595: Buffer overflow in XkbVModMaskText() +Patch4: 0003-xkb-Fix-buffer-overflow-in-XkbVModMaskText.patch +# CVE-2025-26596: Heap overflow in XkbWriteKeySyms() +Patch5: 0004-xkb-Fix-computation-of-XkbSizeKeySyms.patch +# CVE-2025-26597: Buffer overflow in XkbChangeTypesOfKey() +Patch6: 0005-xkb-Fix-buffer-overflow-in-XkbChangeTypesOfKey.patch +# CVE-2025-26598: Out-of-bounds write in CreatePointerBarrierClient() +Patch7: 0006-Xi-Fix-barrier-device-search.patch +# CVE-2025-26599: Use of uninitialized pointer in compRedirectWindow() +Patch8: 0007-composite-Handle-failure-to-redirect-in-compRedirect.patch +Patch9: 0008-composite-initialize-border-clip-even-when-pixmap-al.patch +# CVE-2025-26600: Use-after-free in PlayReleasedEvents() +Patch10: 0009-dix-Dequeue-pending-events-on-frozen-device-on-remov.patch +# CVE-2025-26601: Use-after-free in SyncInitTrigger() +Patch11: 0010-sync-Do-not-let-sync-objects-uninitialized.patch +Patch12: 0011-sync-Check-values-before-applying-changes.patch +Patch13: 0012-sync-Do-not-fail-SyncAddTriggerToSyncObject.patch +Patch14: 0013-sync-Apply-changes-last-in-SyncChangeAlarmAttributes.patch + License: MIT Requires: xorg-x11-server-common @@ -135,6 +159,15 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libdir}/pkgconfig/xwayland.pc %changelog +* Wed Feb 26 2025 Olivier Fourdan - 23.2.7-3 +- CVE fix for: CVE-2025-26594 (RHEL-80204), CVE-2025-26595 (RHEL-80187), + CVE-2025-26596 (RHEL-80190), CVE-2025-26597 (RHEL-80193), + CVE-2025-26598 (RHEL-80195), CVE-2025-26599 (RHEL-80202), + CVE-2025-26600 (RHEL-80203), CVE-2025-26601 (RHEL-80207) + +* Wed Oct 30 2024 Olivier Fourdan - 23.2.7-2 +- Fix for CVE-2024-9632 - (RHEL-61997) + * Thu May 16 2024 Olivier Fourdan - 23.2.7-1 - xwayland 23.2.7 - (RHEL-29912)