Blame SOURCES/0002-Check-SetMap-request-length-carefully.patch

4bbf3f
From 06d1a032ee491547f7037c3ff042065dc2aeaa99 Mon Sep 17 00:00:00 2001
4bbf3f
From: Matthieu Herrb <matthieu@herrb.eu>
4bbf3f
Date: Thu, 12 Nov 2020 19:15:07 +0100
4bbf3f
Subject: [PATCH xserver 2/2] Check SetMap request length carefully.
4bbf3f
4bbf3f
Avoid out of bounds memory accesses on too short request.
4bbf3f
4bbf3f
ZDI-CAN 11572 /  CVE-2020-14360
4bbf3f
4bbf3f
This vulnerability was discovered by:
4bbf3f
Jan-Niklas Sohn working with Trend Micro Zero Day Initiative
4bbf3f
4bbf3f
Signed-off-by: Matthieu Herrb <matthieu@herrb.eu>
4bbf3f
(cherry picked from commit 446ff2d3177087b8173fa779fa5b77a2a128988b)
4bbf3f
---
4bbf3f
 xkb/xkb.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
4bbf3f
 1 file changed, 92 insertions(+)
4bbf3f
4bbf3f
diff --git a/xkb/xkb.c b/xkb/xkb.c
4bbf3f
index f162e8d83..68c59df02 100644
4bbf3f
--- a/xkb/xkb.c
4bbf3f
+++ b/xkb/xkb.c
4bbf3f
@@ -2382,6 +2382,93 @@ SetVirtualModMap(XkbSrvInfoPtr xkbi,
4bbf3f
     return (char *) wire;
4bbf3f
 }
4bbf3f
 
4bbf3f
+#define _add_check_len(new) \
4bbf3f
+    if (len > UINT32_MAX - (new) || len > req_len - (new)) goto bad; \
4bbf3f
+    else len += new
4bbf3f
+
4bbf3f
+/**
4bbf3f
+ * Check the length of the SetMap request
4bbf3f
+ */
4bbf3f
+static int
4bbf3f
+_XkbSetMapCheckLength(xkbSetMapReq *req)
4bbf3f
+{
4bbf3f
+    size_t len = sz_xkbSetMapReq, req_len = req->length << 2;
4bbf3f
+    xkbKeyTypeWireDesc *keytype;
4bbf3f
+    xkbSymMapWireDesc *symmap;
4bbf3f
+    BOOL preserve;
4bbf3f
+    int i, map_count, nSyms;
4bbf3f
+
4bbf3f
+    if (req_len < len)
4bbf3f
+        goto bad;
4bbf3f
+    /* types */
4bbf3f
+    if (req->present & XkbKeyTypesMask) {
4bbf3f
+        keytype = (xkbKeyTypeWireDesc *)(req + 1);
4bbf3f
+        for (i = 0; i < req->nTypes; i++) {
4bbf3f
+            _add_check_len(XkbPaddedSize(sz_xkbKeyTypeWireDesc));
4bbf3f
+            if (req->flags & XkbSetMapResizeTypes) {
4bbf3f
+                _add_check_len(keytype->nMapEntries
4bbf3f
+                               * sz_xkbKTSetMapEntryWireDesc);
4bbf3f
+                preserve = keytype->preserve;
4bbf3f
+                map_count = keytype->nMapEntries;
4bbf3f
+                if (preserve) {
4bbf3f
+                    _add_check_len(map_count * sz_xkbModsWireDesc);
4bbf3f
+                }
4bbf3f
+                keytype += 1;
4bbf3f
+                keytype = (xkbKeyTypeWireDesc *)
4bbf3f
+                          ((xkbKTSetMapEntryWireDesc *)keytype + map_count);
4bbf3f
+                if (preserve)
4bbf3f
+                    keytype = (xkbKeyTypeWireDesc *)
4bbf3f
+                              ((xkbModsWireDesc *)keytype + map_count);
4bbf3f
+            }
4bbf3f
+        }
4bbf3f
+    }
4bbf3f
+    /* syms */
4bbf3f
+    if (req->present & XkbKeySymsMask) {
4bbf3f
+        symmap = (xkbSymMapWireDesc *)((char *)req + len);
4bbf3f
+        for (i = 0; i < req->nKeySyms; i++) {
4bbf3f
+            _add_check_len(sz_xkbSymMapWireDesc);
4bbf3f
+            nSyms = symmap->nSyms;
4bbf3f
+            _add_check_len(nSyms*sizeof(CARD32));
4bbf3f
+            symmap += 1;
4bbf3f
+            symmap = (xkbSymMapWireDesc *)((CARD32 *)symmap + nSyms);
4bbf3f
+        }
4bbf3f
+    }
4bbf3f
+    /* actions */
4bbf3f
+    if (req->present & XkbKeyActionsMask) {
4bbf3f
+        _add_check_len(req->totalActs * sz_xkbActionWireDesc 
4bbf3f
+                       + XkbPaddedSize(req->nKeyActs));
4bbf3f
+    }
4bbf3f
+    /* behaviours */
4bbf3f
+    if (req->present & XkbKeyBehaviorsMask) {
4bbf3f
+        _add_check_len(req->totalKeyBehaviors * sz_xkbBehaviorWireDesc);
4bbf3f
+    }
4bbf3f
+    /* vmods */
4bbf3f
+    if (req->present & XkbVirtualModsMask) {
4bbf3f
+        _add_check_len(XkbPaddedSize(Ones(req->virtualMods)));
4bbf3f
+    }
4bbf3f
+    /* explicit */
4bbf3f
+    if (req->present & XkbExplicitComponentsMask) {
4bbf3f
+        /* two bytes per non-zero explicit componen */
4bbf3f
+        _add_check_len(XkbPaddedSize(req->totalKeyExplicit * sizeof(CARD16)));
4bbf3f
+    }
4bbf3f
+    /* modmap */
4bbf3f
+    if (req->present & XkbModifierMapMask) {
4bbf3f
+         /* two bytes per non-zero modmap component */
4bbf3f
+        _add_check_len(XkbPaddedSize(req->totalModMapKeys * sizeof(CARD16)));
4bbf3f
+    }
4bbf3f
+    /* vmodmap */
4bbf3f
+    if (req->present & XkbVirtualModMapMask) {
4bbf3f
+        _add_check_len(req->totalVModMapKeys * sz_xkbVModMapWireDesc);
4bbf3f
+    }
4bbf3f
+    if (len == req_len)
4bbf3f
+        return Success;
4bbf3f
+bad:
4bbf3f
+    ErrorF("[xkb] BOGUS LENGTH in SetMap: expected %ld got %ld\n",
4bbf3f
+           len, req_len);
4bbf3f
+    return BadLength;
4bbf3f
+}
4bbf3f
+
4bbf3f
+
4bbf3f
 /**
4bbf3f
  * Check if the given request can be applied to the given device but don't
4bbf3f
  * actually do anything..
4bbf3f
@@ -2639,6 +2726,11 @@ ProcXkbSetMap(ClientPtr client)
4bbf3f
     CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixManageAccess);
4bbf3f
     CHK_MASK_LEGAL(0x01, stuff->present, XkbAllMapComponentsMask);
4bbf3f
 
4bbf3f
+    /* first verify the request length carefully */
4bbf3f
+    rc = _XkbSetMapCheckLength(stuff);
4bbf3f
+    if (rc != Success)
4bbf3f
+        return rc;
4bbf3f
+
4bbf3f
     tmp = (char *) &stuff[1];
4bbf3f
 
4bbf3f
     /* Check if we can to the SetMap on the requested device. If this
4bbf3f
-- 
4bbf3f
2.28.0
4bbf3f