Bug 2545860 (CVE-2026-93518) - CVE-2026-93518 xorg-x11-server: XKB ResizeKeyType Numeric Truncation
Summary: CVE-2026-93518 xorg-x11-server: XKB ResizeKeyType Numeric Truncation
Keywords:
Status: NEW
Alias: CVE-2026-93518
Deadline: 2026-10-07
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-10-05 10:06 UTC by OSIDB Bzimport
Modified: 2026-10-07 16:04 UTC (History)
3 users (show)

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


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-10-05 10:06:20 UTC
In XkbResizeKeyType(), the size_syms variable is declared as unsigned
     short. The expression (nTotal * 15) / 10 can exceed 65535 for large
     nTotal values, and the result is truncated when assigned to the
     unsigned short variable. This causes an undersized allocation, and
     subsequent writes overflow the heap buffer.

     An authenticated X client can trigger this by sending XKB requests that
     cause a key type resize with a sufficiently large nTotal value.

     The heap buffer overflow resulting from the truncated allocation can lead
     to arbitrary code execution or denial of service.

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

Fields in XkbClientMapRec and XkbServerMapRec that denote size/num
and are unsigned short can result in truncations. For example
when XkbResizeKeyType() computes the new size_syms value as
(nTotal * 15) / 10, the result may be implicitly truncated to 16
bits on assignment. This causes an undersized calloc allocation, and the
subsequent copy loop writes based on the actual (untruncated) key count,
resulting in a heap buffer overflow. A similar truncation may happen
in XkbResizeKeySyms() and XkbResizeKeyActions().

Widen the fields from unsigned short to unsigned int. These are internal
server structures, not wire protocol types.

---
 include/xkbstr.h | 8 ++++----
 xkb/XKBMAlloc.c  | 3 ++-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/include/xkbstr.h b/include/xkbstr.h
index 749f1f26e500..45a15f37632a 100644
--- a/include/xkbstr.h
+++ b/include/xkbstr.h
@@ -307,18 +307,18 @@ typedef struct _XkbControls {
 } XkbControlsRec, *XkbControlsPtr;
 
 #define	XkbAX_AnyFeedback(c)	((c)->enabled_ctrls&XkbAccessXFeedbackMask)
 #define	XkbAX_NeedOption(c,w)	((c)->ax_options&(w))
 #define	XkbAX_NeedFeedback(c, w) (XkbAX_AnyFeedback((c)) && \
                                   XkbAX_NeedOption((c), (w)))
 
 typedef struct _XkbServerMapRec {
-    unsigned short num_acts;
-    unsigned short size_acts;
+    unsigned int num_acts;
+    unsigned int size_acts;
     XkbAction *acts;
 
     XkbBehavior *behaviors;
     unsigned short *key_acts;
 #if defined(__cplusplus) || defined(c_plusplus)
     /* explicit is a C++ reserved word */
     unsigned char *c_explicit;
 #else
@@ -341,18 +341,18 @@ typedef struct _XkbSymMapRec {
     unsigned short offset;
 } XkbSymMapRec, *XkbSymMapPtr;
 
 typedef struct _XkbClientMapRec {
     unsigned char size_types;
     unsigned char num_types;
     XkbKeyTypePtr types;
 
-    unsigned short size_syms;
-    unsigned short num_syms;
+    unsigned int size_syms;
+    unsigned int num_syms;
     KeySym *syms;
     XkbSymMapPtr key_sym_map;
 
     unsigned char *modmap;
 } XkbClientMapRec, *XkbClientMapPtr;
 
 #define	XkbCMKeyGroupInfo(m, k) ((m)->key_sym_map[(k)].group_info)
 #define	XkbCMKeyNumGroups(m, k) (XkbNumGroups((m)->key_sym_map[(k)].group_info))
diff --git a/xkb/XKBMAlloc.c b/xkb/XKBMAlloc.c
index 78e597f3d93e..8f8ac28fadb9 100644
--- a/xkb/XKBMAlloc.c
+++ b/xkb/XKBMAlloc.c
@@ -380,17 +380,18 @@ XkbResizeKeyType(XkbDescPtr xkb,
                 nTotal += XkbKeyNumSyms(xkb, i);
             else {
                 nTotal += XkbKeyNumGroups(xkb, i) * new_num_lvls;
                 nResize++;
             }
         }
         if (nResize > 0) {
             int nextMatch;
-
+            if (nTotal > INT_MAX / 15)
+                return BadAlloc;
             xkb->map->size_syms = (nTotal * 15) / 10;
             newSyms = calloc(xkb->map->size_syms, sizeof(KeySym));
             if (newSyms == NULL)
                 return BadAlloc;
             nextMatch = 0;
             nSyms = 1;
             for (i = xkb->min_key_code; i <= xkb->max_key_code; i++) {
                 if (matchingKeys[nextMatch] == i) {
-- 
2.55.0


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