Commit message: Unvalidated extra length in ProcEstablishConnection (CVE-2017-12176)

Reviewed-by: Julien Cristau <jcristau@debian.org>
Signed-off-by: Nathan Kidd <nkidd@opentext.com>
Signed-off-by: Julien Cristau <jcristau@debian.org>
CVE: CVE-2017-12176
CWE: CWE-20
Vulnerability description: xorg-x11-server before 1.19.5 was missing extra length validation in ProcEstablishConnection function allowing malicious X client to cause X server to crash or possibly execute arbitrary code.

Project: xserver

CloseDownClient(ClientPtr client)
{
    Bool really_close_down = client->clientGone ||
        client->closeDownMode == DestroyAll;

    if (!client->clientGone) {
        /* ungrab server if grabbing client dies */
        if (grabState != GrabNone && grabClient == client) {
            UngrabServer(client);
        }
        BITCLEAR(grabWaiters, client->index);
        DeleteClientFromAnySelections(client);
        ReleaseActiveGrabs(client);
        DeleteClientFontStuff(client);
        if (!really_close_down) {
            /*  This frees resources that should never be retained
             *  no matter what the close down mode is.  Actually we
             *  could do this unconditionally, but it's probably
             *  better not to traverse all the client's resources
             *  twice (once here, once a few lines down in
             *  FreeClientResources) in the common case of
             *  really_close_down == TRUE.
             */
            FreeClientNeverRetainResources(client);
            client->clientState = ClientStateRetained;
            if (ClientStateCallback) {
                NewClientInfoRec clientinfo;

                clientinfo.client = client;
                clientinfo.prefix = (xConnSetupPrefix *) NULL;
                clientinfo.setup = (xConnSetup *) NULL;
                CallCallbacks((&ClientStateCallback), (void *) &clientinfo);
            }
        }
        client->clientGone = TRUE;      /* so events aren't sent to client */
        if (ClientIsAsleep(client))
            ClientSignal(client);
        ProcessWorkQueueZombies();
        CloseDownConnection(client);
        output_pending_clear(client);
        mark_client_not_ready(client);

        /* If the client made it to the Running stage, nClients has
         * been incremented on its behalf, so we need to decrement it
         * now.  If it hasn't gotten to Running, nClients has *not*
         * been incremented, so *don't* decrement it.
         */
        if (client->clientState != ClientStateInitial) {
            --nClients;
        }
    }

    if (really_close_down) {
        if (client->clientState == ClientStateRunning && nClients == 0)
            dispatchException |= dispatchExceptionAtReset;

        client->clientState = ClientStateGone;
        if (ClientStateCallback) {
            NewClientInfoRec clientinfo;

            clientinfo.client = client;
            clientinfo.prefix = (xConnSetupPrefix *) NULL;
            clientinfo.setup = (xConnSetup *) NULL;
            CallCallbacks((&ClientStateCallback), (void *) &clientinfo);
        }
        TouchListenerGone(client->clientAsMask);
        FreeClientResources(client);
        /* Disable client ID tracking. This must be done after
         * ClientStateCallback. */
        ReleaseClientIds(client);
#ifdef XSERVER_DTRACE
        XSERVER_CLIENT_DISCONNECT(client->index);
#endif
        if (client->index < nextFreeClientID)
            nextFreeClientID = client->index;
        clients[client->index] = NullClient;
        SmartLastClient = NullClient;
        dixFreeObjectWithPrivates(client, PRIVATE_CLIENT);

        while (!clients[currentMaxClients - 1])
            currentMaxClients--;
    }
}

Project: xserver

DetachUnboundGPU(ScreenPtr slave)
{
    assert(slave->isGPU);
    assert(!slave->is_output_slave);
    assert(!slave->is_offload_slave);
    xorg_list_del(&slave->slave_head);
    slave->current_master = NULL;
}

Project: xserver

ProcGrabServer(ClientPtr client)
{
    int rc;

    REQUEST_SIZE_MATCH(xReq);
    if (grabState != GrabNone && client != grabClient) {
        ResetCurrentRequest(client);
        client->sequence--;
        BITSET(grabWaiters, client->index);
        IgnoreClient(client);
        return Success;
    }
    rc = OnlyListenToOneClient(client);
    if (rc != Success)
        return rc;
    grabState = GrabActive;
    grabClient = client;
    mark_client_grab(client);

    if (ServerGrabCallback) {
        ServerGrabInfoRec grabinfo;

        grabinfo.client = client;
        grabinfo.grabstate = SERVER_GRABBED;
        CallCallbacks(&ServerGrabCallback, (void *) &grabinfo);
    }

    return Success;
}

Project: xserver

ProcQueryTextExtents(ClientPtr client)
{
    xQueryTextExtentsReply reply;
    FontPtr pFont;
    ExtentInfoRec info;
    unsigned long length;
    int rc;

    REQUEST(xQueryTextExtentsReq);
    REQUEST_AT_LEAST_SIZE(xQueryTextExtentsReq);

    rc = dixLookupFontable(&pFont, stuff->fid, client, DixGetAttrAccess);
    if (rc != Success)
        return rc;

    length = client->req_len - bytes_to_int32(sizeof(xQueryTextExtentsReq));
    length = length << 1;
    if (stuff->oddLength) {
        if (length == 0)
            return BadLength;
        length--;
    }
    if (!xfont2_query_text_extents(pFont, length, (unsigned char *) &stuff[1], &info))
        return BadAlloc;
    reply = (xQueryTextExtentsReply) {
        .type = X_Reply,
        .drawDirection = info.drawDirection,
        .sequenceNumber = client->sequence,
        .length = 0,
        .fontAscent = info.fontAscent,
        .fontDescent = info.fontDescent,
        .overallAscent = info.overallAscent,
        .overallDescent = info.overallDescent,
        .overallWidth = info.overallWidth,
        .overallLeft = info.overallLeft,
        .overallRight = info.overallRight
    };
    WriteReplyToClient(client, sizeof(xQueryTextExtentsReply), &reply);
    return Success;
}

Project: xserver

ProcQueryTree(ClientPtr client)
{
    xQueryTreeReply reply;
    int rc, numChildren = 0;
    WindowPtr pChild, pWin, pHead;
    Window *childIDs = (Window *) NULL;

    REQUEST(xResourceReq);

    REQUEST_SIZE_MATCH(xResourceReq);
    rc = dixLookupWindow(&pWin, stuff->id, client, DixListAccess);
    if (rc != Success)
        return rc;

    reply = (xQueryTreeReply) {
        .type = X_Reply,
        .sequenceNumber = client->sequence,
        .root = pWin->drawable.pScreen->root->drawable.id,
        .parent = (pWin->parent) ? pWin->parent->drawable.id : (Window) None
    };
    pHead = RealChildHead(pWin);
    for (pChild = pWin->lastChild; pChild != pHead; pChild = pChild->prevSib)
        numChildren++;
    if (numChildren) {
        int curChild = 0;

        childIDs = xallocarray(numChildren, sizeof(Window));
        if (!childIDs)
            return BadAlloc;
        for (pChild = pWin->lastChild; pChild != pHead;
             pChild = pChild->prevSib)
            childIDs[curChild++] = pChild->drawable.id;
    }

    reply.nChildren = numChildren;
    reply.length = bytes_to_int32(numChildren * sizeof(Window));

    WriteReplyToClient(client, sizeof(xQueryTreeReply), &reply);
    if (numChildren) {
        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
        WriteSwappedDataToClient(client, numChildren * sizeof(Window),
                                 childIDs);
        free(childIDs);
    }

    return Success;
}

Project: xserver

UngrabServer(ClientPtr client)
{
    int i;

    grabState = GrabNone;
    ListenToAllClients();
    mark_client_ungrab();
    for (i = mskcnt; --i >= 0 && !grabWaiters[i];);
    if (i >= 0) {
        i <<= 5;
        while (!GETBIT(grabWaiters, i))
            i++;
        BITCLEAR(grabWaiters, i);
        AttendClient(clients[i]);
    }

    if (ServerGrabCallback) {
        ServerGrabInfoRec grabinfo;

        grabinfo.client = client;
        grabinfo.grabstate = SERVER_UNGRABBED;
        CallCallbacks(&ServerGrabCallback, (void *) &grabinfo);
    }
}