Bug 2545945 (CVE-2026-93524)

Summary: CVE-2026-93524 xorg-x11-server: XKB SetMap Key Width/Action Count Desync Out-Of-Bounds Read
Product: [Other] Security Response Reporter: OSIDB Bzimport <bzimport>
Component: vulnerabilityAssignee: Product Security <prodsec-ir-bot>
Status: NEW --- QA Contact:
Severity: low Docs Contact:
Priority: low    
Version: unspecifiedCC: rhel-process-autobot, security-response-team, watson-tool-maintainers
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: ---
Doc Text:
A flaw was found in xorg-x11-server. An authenticated local user can trigger an out-of-bounds heap memory read by sending specially crafted X Keyboard Extension (XKB) requests with inconsistent key range parameters. This flaw leads to information disclosure, allowing the user to read sensitive data from the server's heap memory.
Story Points: ---
Clone Of: Environment:
Last Closed: Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Deadline: 2026-10-07   

Description OSIDB Bzimport 2026-10-05 15:34:29 UTC
CheckKeySyms() in xkb/xkb.c populates a symsPerKey[] validation array with
new key widths for keys in the SetMap request range, then continues filling
entries for keys beyond the range. However, the second loop starts at index
i = nKeySyms (the first loop's counter) rather than i = firstKeySym +
nKeySyms. When firstKeySym is large and nKeySyms is small, the second loop
overwrites symsPerKey[target] with the old width from the existing map.

CheckKeyActions() then validates the wire action count against the stale old
width and accepts it. In _XkbSetMap(), SetKeySyms() widens the key (resizing
actions to nGroups * newWidth), but SetKeyActions() then resizes again to the
old count. A subsequent XkbGetMap request reads XkbKeyNumActions() entries
(derived from the new wider key_sym_map.width) from the shorter action
allocation, causing an out-of-bounds heap read. The overread bytes are
copied into the GetMap reply and sent back to the requesting client,
producing a bounded heap information disclosure.

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


* Patch
From 2d6e9cd5f0e79115e1c0573f9473a8e8fa7dd12a Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer>
Date: Thu, 3 Sep 2026 14:55:27 +1000
Subject: [PATCH xserver] xkb: fix CheckKeySyms overwriting request-range
 symsPerKey entries

CheckKeySyms() has two loops: the first processes keys in the SetMap
request range, the second fills in any missing keys from the existing
map.

The indexing between the two was inconsistent: the first loop
uses a zero-based index + req->firstKeySym, the second loop used it just
as zero-based index.

Example:
   req->firstKeySym = 50
   req->nKeySyms = 10

  i goes from 0 to 10,
  loop 1 checks [50, 60), changes the width but finishes with i at 10.
  loop 2 then runs over [10, 255(, copying the old keymap width

The second loop overwrites the new widths with the width of the
existing keymap. The subsequent SetKeySyms worked on the new width
and may write fewer actions than the map width.

A subsequent XkbGetMap request would then read XkbKeyNumActions()
entries (derived from the new wider key_sym_map.width) from the shorter
action allocation, causing an out-of-bounds heap read. The overread
bytes would be copied into the GetMap reply and sent back to the client,
producing a bounded heap information disclosure.

This vulnerability was discovered by:
  WONJOON HWANG (@joon1337) working with TrendAI Zero Day Initiative

ZDI-CAN-32408

CVE-2026-93524

Assisted-by: Claude:claude-opus-4-6
#+begin_src diff
---
 xkb/xkb.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xkb/xkb.c b/xkb/xkb.c
index cbbbc5055e3c..9ed01552e12b 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -1808,16 +1808,17 @@ CheckKeySyms(ClientPtr client,
             if (!_XkbCheckRequestBounds(client, req, pSyms, &pSyms[wire->nSyms])) {
                 ,*errorRtrn = _XkbErrCode3(0x19, i + req->firstKeySym, wire->nSyms);
                 return 0;
             }
         }
         wire = (xkbSymMapWireDesc *) &pSyms[wire->nSyms];
     }
 
+    i = req->firstKeySym + req->nKeySyms;
     map = &xkb->map->key_sym_map[i];
     for (; i <= (unsigned) xkb->max_key_code; i++, map++) {
         register int g, nG, w;
 
         nG = XkbKeyNumGroups(xkb, i);
         for (w = g = 0; g < nG; g++) {
             if (map->kt_index[g] >= (unsigned) nTypes) {
                 ,*errorRtrn = _XkbErrCode4(0x18, i, g, map->kt_index[g]);
-- 
2.55.0
#+end_src