Fedora Account System
Red Hat Associate
Red Hat Customer
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