From bd134231e282d9eb126b6fdaa40bb383180fa72b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 5 Jul 2022 11:11:06 +1000 Subject: [PATCH xserver 3/3] xkb: add request length validation for XkbSetGeometry No validation of the various fields on that report were done, so a malicious client could send a short request that claims it had N sections, or rows, or keys, and the server would process the request for N sections, running out of bounds of the actual request data. Fix this by adding size checks to ensure our data is valid. ZDI-CAN 16062, CVE-2022-2319. This vulnerability was discovered by: Jan-Niklas Sohn working with Trend Micro Zero Day Initiative Signed-off-by: Peter Hutterer (cherry picked from commit 6907b6ea2b4ce949cb07271f5b678d5966d9df42) --- xkb/xkb.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/xkb/xkb.c b/xkb/xkb.c index 36464a770..27d19793e 100644 --- a/xkb/xkb.c +++ b/xkb/xkb.c @@ -5160,7 +5160,7 @@ _GetCountedString(char **wire_inout, ClientPtr client, char **str) } static Status -_CheckSetDoodad(char **wire_inout, +_CheckSetDoodad(char **wire_inout, xkbSetGeometryReq *req, XkbGeometryPtr geom, XkbSectionPtr section, ClientPtr client) { char *wire; @@ -5171,6 +5171,9 @@ _CheckSetDoodad(char **wire_inout, Status status; dWire = (xkbDoodadWireDesc *) (*wire_inout); + if (!_XkbCheckRequestBounds(client, req, dWire, dWire + 1)) + return BadLength; + any = dWire->any; wire = (char *) &dWire[1]; if (client->swapped) { @@ -5273,7 +5276,7 @@ _CheckSetDoodad(char **wire_inout, } static Status -_CheckSetOverlay(char **wire_inout, +_CheckSetOverlay(char **wire_inout, xkbSetGeometryReq *req, XkbGeometryPtr geom, XkbSectionPtr section, ClientPtr client) { register int r; @@ -5284,6 +5287,9 @@ _CheckSetOverlay(char **wire_inout, wire = *wire_inout; olWire = (xkbOverlayWireDesc *) wire; + if (!_XkbCheckRequestBounds(client, req, olWire, olWire + 1)) + return BadLength; + if (client->swapped) { swapl(&olWire->name); } @@ -5295,6 +5301,9 @@ _CheckSetOverlay(char **wire_inout, xkbOverlayKeyWireDesc *kWire; XkbOverlayRowPtr row; + if (!_XkbCheckRequestBounds(client, req, rWire, rWire + 1)) + return BadLength; + if (rWire->rowUnder > section->num_rows) { client->errorValue = _XkbErrCode4(0x20, r, section->num_rows, rWire->rowUnder); @@ -5303,6 +5312,9 @@ _CheckSetOverlay(char **wire_inout, row = XkbAddGeomOverlayRow(ol, rWire->rowUnder, rWire->nKeys); kWire = (xkbOverlayKeyWireDesc *) &rWire[1]; for (k = 0; k < rWire->nKeys; k++, kWire++) { + if (!_XkbCheckRequestBounds(client, req, kWire, kWire + 1)) + return BadLength; + if (XkbAddGeomOverlayKey(ol, row, (char *) kWire->over, (char *) kWire->under) == NULL) { @@ -5336,6 +5348,9 @@ _CheckSetSections(XkbGeometryPtr geom, register int r; xkbRowWireDesc *rWire; + if (!_XkbCheckRequestBounds(client, req, sWire, sWire + 1)) + return BadLength; + if (client->swapped) { swapl(&sWire->name); swaps(&sWire->top); @@ -5361,6 +5376,9 @@ _CheckSetSections(XkbGeometryPtr geom, XkbRowPtr row; xkbKeyWireDesc *kWire; + if (!_XkbCheckRequestBounds(client, req, rWire, rWire + 1)) + return BadLength; + if (client->swapped) { swaps(&rWire->top); swaps(&rWire->left); @@ -5375,6 +5393,9 @@ _CheckSetSections(XkbGeometryPtr geom, for (k = 0; k < rWire->nKeys; k++, kWire++) { XkbKeyPtr key; + if (!_XkbCheckRequestBounds(client, req, kWire, kWire + 1)) + return BadLength; + key = XkbAddGeomKey(row); if (!key) return BadAlloc; @@ -5400,7 +5421,7 @@ _CheckSetSections(XkbGeometryPtr geom, register int d; for (d = 0; d < sWire->nDoodads; d++) { - status = _CheckSetDoodad(&wire, geom, section, client); + status = _CheckSetDoodad(&wire, req, geom, section, client); if (status != Success) return status; } @@ -5409,7 +5430,7 @@ _CheckSetSections(XkbGeometryPtr geom, register int o; for (o = 0; o < sWire->nOverlays; o++) { - status = _CheckSetOverlay(&wire, geom, section, client); + status = _CheckSetOverlay(&wire, req, geom, section, client); if (status != Success) return status; } @@ -5443,6 +5464,9 @@ _CheckSetShapes(XkbGeometryPtr geom, xkbOutlineWireDesc *olWire; XkbOutlinePtr ol; + if (!_XkbCheckRequestBounds(client, req, shapeWire, shapeWire + 1)) + return BadLength; + shape = XkbAddGeomShape(geom, shapeWire->name, shapeWire->nOutlines); if (!shape) @@ -5453,12 +5477,18 @@ _CheckSetShapes(XkbGeometryPtr geom, XkbPointPtr pt; xkbPointWireDesc *ptWire; + if (!_XkbCheckRequestBounds(client, req, olWire, olWire + 1)) + return BadLength; + ol = XkbAddGeomOutline(shape, olWire->nPoints); if (!ol) return BadAlloc; ol->corner_radius = olWire->cornerRadius; ptWire = (xkbPointWireDesc *) &olWire[1]; for (p = 0, pt = ol->points; p < olWire->nPoints; p++, pt++, ptWire++) { + if (!_XkbCheckRequestBounds(client, req, ptWire, ptWire + 1)) + return BadLength; + pt->x = ptWire->x; pt->y = ptWire->y; if (client->swapped) { @@ -5564,12 +5594,15 @@ _CheckSetGeom(XkbGeometryPtr geom, xkbSetGeometryReq * req, ClientPtr client) return status; for (i = 0; i < req->nDoodads; i++) { - status = _CheckSetDoodad(&wire, geom, NULL, client); + status = _CheckSetDoodad(&wire, req, geom, NULL, client); if (status != Success) return status; } for (i = 0; i < req->nKeyAliases; i++) { + if (!_XkbCheckRequestBounds(client, req, wire, wire + XkbKeyNameLength)) + return BadLength; + if (XkbAddGeomKeyAlias(geom, &wire[XkbKeyNameLength], wire) == NULL) return BadAlloc; wire += 2 * XkbKeyNameLength; -- 2.36.1