Bug 2545949 (CVE-2026-93536) - CVE-2026-93536 xorg-x11-server: xorg-x11-server: Denial of Service via use-after-free in gesture event handling
Summary: CVE-2026-93536 xorg-x11-server: xorg-x11-server: Denial of Service via use-af...
Keywords:
Status: NEW
Alias: CVE-2026-93536
Deadline: 2026-10-07
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
low
low
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-10-05 15:45 UTC by OSIDB Bzimport
Modified: 2026-10-09 10:54 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-10-05 15:45:17 UTC
GestureBuildSprite() copies raw WindowPtr values into the gesture sprite
trace via CopySprite() without any reference counting or lifetime
management. WindowGone() only repairs touch sprite traces when a window
is destroyed -- gesture sprite traces are never checked or cleaned. This
leaves stale WindowPtr references in gesture sprites.

When DeliverOneGestureEvent() later dereferences the stale WindowPtr via
DeepestSpriteWin(&gi->sprite)->drawable.id, it produces a use-after-free.
The freed 4 bytes (drawable.id) can be read back, potentially leaking
information about the contents of the freed memory region.

Fixed in: xorg-server-21.1.25 and xwayland-24.1.14

* Patch
From a4dd330114c58f760c141c84c7c01cc693181acf Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer>
Date: Thu, 3 Sep 2026 14:57:31 +1000
Subject: [PATCH xserver] Xi: clean up gesture sprite traces in WindowGone

Gesture sprite traces which have the same 'fixed sprite trace
for the duration of the gesture' logic as touch sprite traces.
Both copy WindowPtrs into the trace without refcounting them.

If a device supports gestures but does not have a TouchClass,
the cleanup skipped over the gesture sprite cleanup, leaving
dangling pointers in place if a sprite trace is destroyed
during an active gesture (i.e. window is removed).

DeliverOneGestureEvent() then dereferences the stale pointer via
DeepestSpriteWin(&gi->sprite)->drawable.id, causing a use-after-free.

This vulnerability was discovered by:
  4nibhal working with TrendAI Zero Day Initiative

ZDI-CAN-32753

CVE-2026-93536

Assisted-by: Claude:claude-opus-4-6
#+begin_src diff
---
 Xi/exevents.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 2ee4fbc53c27..f17e519c2b09 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -2869,34 +2869,44 @@ InputClientGone(WindowPtr pWin, XID id)
             return Success;
         }
         prev = other;
     }
     FatalError("client not on device event list");
 }
 
 /**
- * Search for window in each touch trace for each device. Remove the window
- * and all its subwindows from the trace when found. The initial window
- * order is preserved.
+ * Search for window in each touch and gesture trace for each device.
+ * Truncate the trace when the window is found.
  ,*/
 void
 WindowGone(WindowPtr win)
 {
     DeviceIntPtr dev;
 
     for (dev = inputInfo.devices; dev; dev = dev->next) {
         TouchClassPtr t = dev->touch;
         int i;
 
-        if (!t)
-            continue;
+        if (t) {
+            for (i = 0; i < t->num_touches; i++) {
+                SpritePtr sprite = &t->touches[i].sprite;
+                int j;
 
-        for (i = 0; i < t->num_touches; i++) {
-            SpritePtr sprite = &t->touches[i].sprite;
+                for (j = 0; j < sprite->spriteTraceGood; j++) {
+                    if (sprite->spriteTrace[j] == win) {
+                        sprite->spriteTraceGood = j;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (dev->gesture) {
+            SpritePtr sprite = &dev->gesture->gesture.sprite;
             int j;
 
             for (j = 0; j < sprite->spriteTraceGood; j++) {
                 if (sprite->spriteTrace[j] == win) {
                     sprite->spriteTraceGood = j;
                     break;
                 }
             }
-- 
2.55.0
#+end_src


Note You need to log in before you can comment on or make changes to this bug.