A flaw was found in xorg-x11-server. In the X Keyboard Extension (XKB), key name memory is allocated with an insufficient buffer size compared to the maximum supported range. An authenticated local client can exploit this flaw by sending requests that modify the keycode range, triggering a heap-based buffer overflow. This vulnerability can lead to arbitrary code execution or cause a Denial of Service (DoS) by crashing the X server.
This is caused by an incomplete fix regression from commit a3171732d.
XkbAllocNames() still uses max_key_code+1 for the names->keys allocation
size, while memset in the ChangeKeycodeRange path uses MAP_LENGTH which
can be larger. This causes a heap out-of-bounds write when the keycode
range is changed to a value where MAP_LENGTH exceeds max_key_code+1.
An authenticated X client can trigger this by sending XKB requests that
change the keycode range.
The heap out-of-bounds write can lead to arbitrary code execution or denial
of service.
Fixed in: xorg-server-21.1.25 and xwayland-24.1.14
Fix: https://gitlab.freedesktop.org/xorg/xserver/-/commit/TBD
Found by: Anonymous working with TrendAI Zero Day Initiative.
(ZDI-CAN-31941)
* Patch
From 58e2874f69ef83323cdd18a4004535e06a8947bb Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer>
Date: Wed, 26 Aug 2026 17:04:03 +1000
Subject: [PATCH xserver] xkb: allocate names->keys to MAP_LENGTH in
XkbAllocNames
Commit a3171732d ("xkb: Always use MAP_LENGTH keymap size") converted
most XKB allocation functions to use MAP_LENGTH (256) instead of
max_key_code + 1. It also removed the per-array reallocarray() calls in
XkbChangeKeycodeRange(), replacing them with a memset up to MAP_LENGTH.
However, XkbAllocNames() was missed and still allocates names->keys to
max_key_code + 1.
When a keycodes component with max_key_code < 255 is loaded (e.g.
sun(type6) with max_key_code=132) and then a SetMap request extends
maxKeyCode to 255, XkbChangeKeycodeRange() memsets names->keys from
max_key_code to MAP_LENGTH-1, writing past the undersized allocation.
Fix by allocating names->keys to MAP_LENGTH, consistent with all other
keymap arrays after a3171732d.
This vulnerability was discovered by:
Anonymous working with TrendAI Zero Day Initiative
ZDI-CAN-31941
CVE-2026-93520
Fixes: a3171732da50 ("xkb: Always use MAP_LENGTH keymap size")
Assisted-by: Claude:claude-opus-4-6
#+begin_src diff
---
xkb/XKBAlloc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/xkb/XKBAlloc.c b/xkb/XKBAlloc.c
index dced42eebd2c..314a858ddb09 100644
--- a/xkb/XKBAlloc.c
+++ b/xkb/XKBAlloc.c
@@ -143,17 +143,17 @@ XkbAllocNames(XkbDescPtr xkb, unsigned which, int nTotalRG, int nTotalAliases)
}
}
}
if ((which & XkbKeyNamesMask) && (names->keys == NULL)) {
if ((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))
return BadValue;
- names->keys = calloc((xkb->max_key_code + 1), sizeof(XkbKeyNameRec));
+ names->keys = calloc(MAP_LENGTH, sizeof(XkbKeyNameRec));
if (names->keys == NULL)
return BadAlloc;
}
if ((which & XkbKeyAliasesMask) && (nTotalAliases > 0)) {
if (names->key_aliases == NULL) {
names->key_aliases = calloc(nTotalAliases, sizeof(XkbKeyAliasRec));
}
else if (nTotalAliases > names->num_key_aliases) {
--
2.55.0
#+end_src
This is caused by an incomplete fix regression from commit a3171732d. XkbAllocNames() still uses max_key_code+1 for the names->keys allocation size, while memset in the ChangeKeycodeRange path uses MAP_LENGTH which can be larger. This causes a heap out-of-bounds write when the keycode range is changed to a value where MAP_LENGTH exceeds max_key_code+1. An authenticated X client can trigger this by sending XKB requests that change the keycode range. The heap out-of-bounds write can lead to arbitrary code execution or denial of service. Fixed in: xorg-server-21.1.25 and xwayland-24.1.14 Fix: https://gitlab.freedesktop.org/xorg/xserver/-/commit/TBD Found by: Anonymous working with TrendAI Zero Day Initiative. (ZDI-CAN-31941) * Patch From 58e2874f69ef83323cdd18a4004535e06a8947bb Mon Sep 17 00:00:00 2001 From: Peter Hutterer <peter.hutterer> Date: Wed, 26 Aug 2026 17:04:03 +1000 Subject: [PATCH xserver] xkb: allocate names->keys to MAP_LENGTH in XkbAllocNames Commit a3171732d ("xkb: Always use MAP_LENGTH keymap size") converted most XKB allocation functions to use MAP_LENGTH (256) instead of max_key_code + 1. It also removed the per-array reallocarray() calls in XkbChangeKeycodeRange(), replacing them with a memset up to MAP_LENGTH. However, XkbAllocNames() was missed and still allocates names->keys to max_key_code + 1. When a keycodes component with max_key_code < 255 is loaded (e.g. sun(type6) with max_key_code=132) and then a SetMap request extends maxKeyCode to 255, XkbChangeKeycodeRange() memsets names->keys from max_key_code to MAP_LENGTH-1, writing past the undersized allocation. Fix by allocating names->keys to MAP_LENGTH, consistent with all other keymap arrays after a3171732d. This vulnerability was discovered by: Anonymous working with TrendAI Zero Day Initiative ZDI-CAN-31941 CVE-2026-93520 Fixes: a3171732da50 ("xkb: Always use MAP_LENGTH keymap size") Assisted-by: Claude:claude-opus-4-6 #+begin_src diff --- xkb/XKBAlloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xkb/XKBAlloc.c b/xkb/XKBAlloc.c index dced42eebd2c..314a858ddb09 100644 --- a/xkb/XKBAlloc.c +++ b/xkb/XKBAlloc.c @@ -143,17 +143,17 @@ XkbAllocNames(XkbDescPtr xkb, unsigned which, int nTotalRG, int nTotalAliases) } } } if ((which & XkbKeyNamesMask) && (names->keys == NULL)) { if ((!XkbIsLegalKeycode(xkb->min_key_code)) || (!XkbIsLegalKeycode(xkb->max_key_code)) || (xkb->max_key_code < xkb->min_key_code)) return BadValue; - names->keys = calloc((xkb->max_key_code + 1), sizeof(XkbKeyNameRec)); + names->keys = calloc(MAP_LENGTH, sizeof(XkbKeyNameRec)); if (names->keys == NULL) return BadAlloc; } if ((which & XkbKeyAliasesMask) && (nTotalAliases > 0)) { if (names->key_aliases == NULL) { names->key_aliases = calloc(nTotalAliases, sizeof(XkbKeyAliasRec)); } else if (nTotalAliases > names->num_key_aliases) { -- 2.55.0 #+end_src