xkb: Add additional bound checking in CheckKeyTypes()

The function CheckKeyTypes() will loop over the client's request but
won't perform any additional bound checking to ensure that the data
read remains within the request bounds.

As a result, a specifically crafted request may cause CheckKeyTypes() to
read past the request data, as reported by valgrind:

  == Invalid read of size 2
  ==    at 0x5A3D1D: CheckKeyTypes (xkb.c:1694)
  ==    by 0x5A6A9C: _XkbSetMapChecks (xkb.c:2515)
  ==    by 0x5A759E: ProcXkbSetMap (xkb.c:2736)
  ==    by 0x5BF832: SProcXkbSetMap (xkbSwap.c:245)
  ==    by 0x5C05ED: SProcXkbDispatch (xkbSwap.c:501)
  ==    by 0x4A20DF: Dispatch (dispatch.c:551)
  ==    by 0x4B03B4: dix_main (main.c:277)
  ==    by 0x428941: main (stubmain.c:34)
  ==  Address is 30 bytes after a block of size 28,672 in arena "client"
  ==
  == Invalid read of size 2
  ==    at 0x5A3AB6: CheckKeyTypes (xkb.c:1669)
  ==    by 0x5A6A9C: _XkbSetMapChecks (xkb.c:2515)
  ==    by 0x5A759E: ProcXkbSetMap (xkb.c:2736)
  ==    by 0x5BF832: SProcXkbSetMap (xkbSwap.c:245)
  ==    by 0x5C05ED: SProcXkbDispatch (xkbSwap.c:501)
  ==    by 0x4A20DF: Dispatch (dispatch.c:551)
  ==    by 0x4B03B4: dix_main (main.c:277)
  ==    by 0x428941: main (stubmain.c:34)
  ==  Address is 2 bytes after a block of size 28,672 alloc'd
  ==    at 0x4848897: realloc (vg_replace_malloc.c:1804)
  ==    by 0x5E357A: ReadRequestFromClient (io.c:336)
  ==    by 0x4A1FAB: Dispatch (dispatch.c:519)
  ==    by 0x4B03B4: dix_main (main.c:277)
  ==    by 0x428941: main (stubmain.c:34)
  ==
  == Invalid write of size 2
  ==    at 0x5A3AD7: CheckKeyTypes (xkb.c:1669)
  ==    by 0x5A6A9C: _XkbSetMapChecks (xkb.c:2515)
  ==    by 0x5A759E: ProcXkbSetMap (xkb.c:2736)
  ==    by 0x5BF832: SProcXkbSetMap (xkbSwap.c:245)
  ==    by 0x5C05ED: SProcXkbDispatch (xkbSwap.c:501)
  ==    by 0x4A20DF: Dispatch (dispatch.c:551)
  ==    by 0x4B03B4: dix_main (main.c:277)
  ==    by 0x428941: main (stubmain.c:34)
  ==  Address is 2 bytes after a block of size 28,672 alloc'd
  ==    at 0x4848897: realloc (vg_replace_malloc.c:1804)
  ==    by 0x5E357A: ReadRequestFromClient (io.c:336)
  ==    by 0x4A1FAB: Dispatch (dispatch.c:519)
  ==    by 0x4B03B4: dix_main (main.c:277)
  ==    by 0x428941: main (stubmain.c:34)
  ==

To avoid that issue, add additional bounds checking within the loops by
calling _XkbCheckRequestBounds() and report an error if we are to read
past the client's request.

CVE-2026-34003, ZDI-CAN-28736

This vulnerability was discovered by:
Jan-Niklas Sohn working with TrendAI Zero Day Initiative

Signed-off-by: Olivier Fourdan <ofourdan@redhat.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
(cherry picked from commit b85b00dd7b)

Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2178>
This commit is contained in:
Olivier Fourdan 2026-02-23 15:52:49 +01:00
parent 5328a544ba
commit 905194d1fa

View file

@ -1639,6 +1639,10 @@ CheckKeyTypes(ClientPtr client,
for (i = 0; i < req->nTypes; i++) {
unsigned width;
if (!_XkbCheckRequestBounds(client, req, wire, wire + 1)) {
*nMapsRtrn = _XkbErrCode3(0x0b, req->nTypes, i);
return 0;
}
if (client->swapped && doswap) {
swaps(&wire->virtualMods);
}
@ -1664,7 +1668,18 @@ CheckKeyTypes(ClientPtr client,
xkbModsWireDesc *preWire;
mapWire = (xkbKTSetMapEntryWireDesc *) &wire[1];
if (!_XkbCheckRequestBounds(client, req, mapWire,
&mapWire[wire->nMapEntries])) {
*nMapsRtrn = _XkbErrCode3(0x0c, i, wire->nMapEntries);
return 0;
}
preWire = (xkbModsWireDesc *) &mapWire[wire->nMapEntries];
if (wire->preserve &&
!_XkbCheckRequestBounds(client, req, preWire,
&preWire[wire->nMapEntries])) {
*nMapsRtrn = _XkbErrCode3(0x0d, i, wire->nMapEntries);
return 0;
}
for (n = 0; n < wire->nMapEntries; n++) {
if (client->swapped && doswap) {
swaps(&mapWire[n].virtualMods);