Commit message: dix: Allow zero-height PutImage requests

The length checking code validates PutImage height and byte width by
making sure that byte-width >= INT32_MAX / height. If height is zero,
this generates a divide by zero exception. Allow zero height requests
explicitly, bypassing the INT32_MAX check.

Signed-off-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Alan Coopersmith <alan.coopersmith@oracle.com>
CVE: CVE-2015-3418
CWE: CWE-369
Vulnerability description: The ProcPutImage function in dix/dispatch.c in X.Org Server (aka xserver and xorg-server) before 1.16.4 allows attackers to cause a denial of service (divide-by-zero and crash) via a zero-height PutImage request.

Project: xserver
File: dispatch.c

AttachOffloadGPU(ScreenPtr pScreen, ScreenPtr new)
{
    assert(new->isGPU);
    xorg_list_add(&new->offload_head, &pScreen->offload_slave_list);
    new->current_master = pScreen;
}

Project: xserver
File: dispatch.c

CreateConnectionBlock(void)
{
    xConnSetup setup;
    xWindowRoot root;
    xDepth depth;
    xVisualType visual;
    xPixmapFormat format;
    unsigned long vid;
    int i, j, k, lenofblock, sizesofar = 0;
    char *pBuf;

    memset(&setup, 0, sizeof(xConnSetup));
    /* Leave off the ridBase and ridMask, these must be sent with
       connection */

    setup.release = VendorRelease;
    /*
     * per-server image and bitmap parameters are defined in Xmd.h
     */
    setup.imageByteOrder = screenInfo.imageByteOrder;

    setup.bitmapScanlineUnit = screenInfo.bitmapScanlineUnit;
    setup.bitmapScanlinePad = screenInfo.bitmapScanlinePad;

    setup.bitmapBitOrder = screenInfo.bitmapBitOrder;
    setup.motionBufferSize = NumMotionEvents();
    setup.numRoots = screenInfo.numScreens;
    setup.nbytesVendor = strlen(VendorString);
    setup.numFormats = screenInfo.numPixmapFormats;
    setup.maxRequestSize = MAX_REQUEST_SIZE;
    QueryMinMaxKeyCodes(&setup.minKeyCode, &setup.maxKeyCode);

    lenofblock = sizeof(xConnSetup) +
        pad_to_int32(setup.nbytesVendor) +
        (setup.numFormats * sizeof(xPixmapFormat)) +
        (setup.numRoots * sizeof(xWindowRoot));
    ConnectionInfo = malloc(lenofblock);
    if (!ConnectionInfo)
        return FALSE;

    memmove(ConnectionInfo, (char *) &setup, sizeof(xConnSetup));
    sizesofar = sizeof(xConnSetup);
    pBuf = ConnectionInfo + sizeof(xConnSetup);

    memmove(pBuf, VendorString, (int) setup.nbytesVendor);
    sizesofar += setup.nbytesVendor;
    pBuf += setup.nbytesVendor;
    i = padding_for_int32(setup.nbytesVendor);
    sizesofar += i;
    while (--i >= 0)
        *pBuf++ = 0;

    memset(&format, 0, sizeof(xPixmapFormat));
    for (i = 0; i < screenInfo.numPixmapFormats; i++) {
        format.depth = screenInfo.formats[i].depth;
        format.bitsPerPixel = screenInfo.formats[i].bitsPerPixel;
        format.scanLinePad = screenInfo.formats[i].scanlinePad;
        memmove(pBuf, (char *) &format, sizeof(xPixmapFormat));
        pBuf += sizeof(xPixmapFormat);
        sizesofar += sizeof(xPixmapFormat);
    }

    connBlockScreenStart = sizesofar;
    memset(&depth, 0, sizeof(xDepth));
    memset(&visual, 0, sizeof(xVisualType));
    for (i = 0; i < screenInfo.numScreens; i++) {
        ScreenPtr pScreen;
        DepthPtr pDepth;
        VisualPtr pVisual;

        pScreen = screenInfo.screens[i];
        root.windowId = pScreen->root->drawable.id;
        root.defaultColormap = pScreen->defColormap;
        root.whitePixel = pScreen->whitePixel;
        root.blackPixel = pScreen->blackPixel;
        root.currentInputMask = 0;      /* filled in when sent */
        root.pixWidth = pScreen->width;
        root.pixHeight = pScreen->height;
        root.mmWidth = pScreen->mmWidth;
        root.mmHeight = pScreen->mmHeight;
        root.minInstalledMaps = pScreen->minInstalledCmaps;
        root.maxInstalledMaps = pScreen->maxInstalledCmaps;
        root.rootVisualID = pScreen->rootVisual;
        root.backingStore = pScreen->backingStoreSupport;
        root.saveUnders = FALSE;
        root.rootDepth = pScreen->rootDepth;
        root.nDepths = pScreen->numDepths;
        memmove(pBuf, (char *) &root, sizeof(xWindowRoot));
        sizesofar += sizeof(xWindowRoot);
        pBuf += sizeof(xWindowRoot);

        pDepth = pScreen->allowedDepths;
        for (j = 0; j < pScreen->numDepths; j++, pDepth++) {
            lenofblock += sizeof(xDepth) +
                (pDepth->numVids * sizeof(xVisualType));
            pBuf = (char *) realloc(ConnectionInfo, lenofblock);
            if (!pBuf) {
                free(ConnectionInfo);
                return FALSE;
            }
            ConnectionInfo = pBuf;
            pBuf += sizesofar;
            depth.depth = pDepth->depth;
            depth.nVisuals = pDepth->numVids;
            memmove(pBuf, (char *) &depth, sizeof(xDepth));
            pBuf += sizeof(xDepth);
            sizesofar += sizeof(xDepth);
            for (k = 0; k < pDepth->numVids; k++) {
                vid = pDepth->vids[k];
                for (pVisual = pScreen->visuals;
                     pVisual->vid != vid; pVisual++);
                visual.visualID = vid;
                visual.class = pVisual->class;
                visual.bitsPerRGB = pVisual->bitsPerRGBValue;
                visual.colormapEntries = pVisual->ColormapEntries;
                visual.redMask = pVisual->redMask;
                visual.greenMask = pVisual->greenMask;
                visual.blueMask = pVisual->blueMask;
                memmove(pBuf, (char *) &visual, sizeof(xVisualType));
                pBuf += sizeof(xVisualType);
                sizesofar += sizeof(xVisualType);
            }
        }
    }
    connSetupPrefix.success = xTrue;
    connSetupPrefix.length = lenofblock / 4;
    connSetupPrefix.majorVersion = X_PROTOCOL;
    connSetupPrefix.minorVersion = X_PROTOCOL_REVISION;
    return TRUE;
}

Project: xserver
File: dispatch.c

Dispatch(void)
{
    int *clientReady;           /* array of request ready clients */
    int result;
    ClientPtr client;
    int nready;
    HWEventQueuePtr *icheck = checkForInput;
    long start_tick;

    nextFreeClientID = 1;
    nClients = 0;

    clientReady = malloc(sizeof(int) * MaxClients);
    if (!clientReady)
        return;

    SmartScheduleSlice = SmartScheduleInterval;
    while (!dispatchException) {
        if (*icheck[0] != *icheck[1]) {
            ProcessInputEvents();
            FlushIfCriticalOutputPending();
        }

        nready = WaitForSomething(clientReady);

        if (nready && !SmartScheduleDisable) {
            clientReady[0] = SmartScheduleClient(clientReady, nready);
            nready = 1;
        }
       /*****************
	*  Handle events in round robin fashion, doing input between
	*  each round
	*****************/

        while (!dispatchException && (--nready >= 0)) {
            client = clients[clientReady[nready]];
            if (!client) {
                /* KillClient can cause this to happen */
                continue;
            }
            /* GrabServer activation can cause this to be true */
            if (grabState == GrabKickout) {
                grabState = GrabActive;
                break;
            }
            isItTimeToYield = FALSE;

            start_tick = SmartScheduleTime;
            while (!isItTimeToYield) {
                if (*icheck[0] != *icheck[1])
                    ProcessInputEvents();

                FlushIfCriticalOutputPending();
                if (!SmartScheduleDisable &&
                    (SmartScheduleTime - start_tick) >= SmartScheduleSlice) {
                    /* Penalize clients which consume ticks */
                    if (client->smart_priority > SMART_MIN_PRIORITY)
                        client->smart_priority--;
                    break;
                }
                /* now, finally, deal with client requests */

                /* Update currentTime so request time checks, such as for input
                 * device grabs, are calculated correctly */
                UpdateCurrentTimeIf();
                result = ReadRequestFromClient(client);
                if (result <= 0) {
                    if (result < 0)
                        CloseDownClient(client);
                    break;
                }

                client->sequence++;
                client->majorOp = ((xReq *) client->requestBuffer)->reqType;
                client->minorOp = 0;
                if (client->majorOp >= EXTENSION_BASE) {
                    ExtensionEntry *ext = GetExtensionEntry(client->majorOp);

                    if (ext)
                        client->minorOp = ext->MinorOpcode(client);
                }
#ifdef XSERVER_DTRACE
                if (XSERVER_REQUEST_START_ENABLED())
                    XSERVER_REQUEST_START(LookupMajorName(client->majorOp),
                                          client->majorOp,
                                          ((xReq *) client->requestBuffer)->length,
                                          client->index,
                                          client->requestBuffer);
#endif
                if (result > (maxBigRequestSize << 2))
                    result = BadLength;
                else {
                    result = XaceHookDispatch(client, client->majorOp);
                    if (result == Success)
                        result =
                            (*client->requestVector[client->majorOp]) (client);
                    XaceHookAuditEnd(client, result);
                }
#ifdef XSERVER_DTRACE
                if (XSERVER_REQUEST_DONE_ENABLED())
                    XSERVER_REQUEST_DONE(LookupMajorName(client->majorOp),
                                         client->majorOp, client->sequence,
                                         client->index, result);
#endif

                if (client->noClientException != Success) {
                    CloseDownClient(client);
                    break;
                }
                else if (result != Success) {
                    SendErrorToClient(client, client->majorOp,
                                      client->minorOp,
                                      client->errorValue, result);
                    break;
                }
            }
            FlushAllOutput();
            client = clients[clientReady[nready]];
            if (client)
                client->smart_stop_tick = SmartScheduleTime;
        }
        dispatchException &= ~DE_PRIORITYCHANGE;
    }
#if defined(DDXBEFORERESET)
    ddxBeforeReset();
#endif
    KillAllClients();
    free(clientReady);
    dispatchException &= ~DE_RESET;
    SmartScheduleLatencyLimited = 0;
    ResetOsBuffers();
}

Project: xserver
File: dispatch.c

EnableLimitedSchedulingLatency(void)
{
    ++SmartScheduleLatencyLimited;
    SmartScheduleSlice = SmartScheduleInterval;
}

Project: xserver
File: dispatch.c

ProcChangeAccessControl(ClientPtr client)
{
    REQUEST(xSetAccessControlReq);

    REQUEST_SIZE_MATCH(xSetAccessControlReq);
    if ((stuff->mode != EnableAccess) && (stuff->mode != DisableAccess)) {
        client->errorValue = stuff->mode;
        return BadValue;
    }
    return ChangeAccessControl(client, stuff->mode == EnableAccess);
}

Project: xserver
File: dispatch.c

ProcChangeCloseDownMode(ClientPtr client)
{
    int rc;

    REQUEST(xSetCloseDownModeReq);
    REQUEST_SIZE_MATCH(xSetCloseDownModeReq);

    rc = XaceHook(XACE_CLIENT_ACCESS, client, client, DixManageAccess);
    if (rc != Success)
        return rc;

    if ((stuff->mode == AllTemporary) ||
        (stuff->mode == RetainPermanent) || (stuff->mode == RetainTemporary)) {
        client->closeDownMode = stuff->mode;
        return Success;
    }
    else {
        client->errorValue = stuff->mode;
        return BadValue;
    }
}

Project: xserver
File: dispatch.c

ProcImageText8(ClientPtr client)
{
    int err;
    DrawablePtr pDraw;
    GC *pGC;

    REQUEST(xImageTextReq);

    REQUEST_FIXED_SIZE(xImageTextReq, stuff->nChars);
    VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess);

    err = ImageText(client,
                    pDraw,
                    pGC,
                    stuff->nChars,
                    (unsigned char *) &stuff[1],
                    stuff->x, stuff->y, stuff->reqType, stuff->drawable);

    if (err == Success) {
        return Success;
    }
    else
        return err;
}

Project: xserver
File: dispatch.c

ProcKillClient(ClientPtr client)
{
    REQUEST(xResourceReq);
    ClientPtr killclient;
    int rc;

    REQUEST_SIZE_MATCH(xResourceReq);
    if (stuff->id == AllTemporary) {
        CloseDownRetainedResources();
        return Success;
    }

    rc = dixLookupClient(&killclient, stuff->id, client, DixDestroyAccess);
    if (rc == Success) {
        CloseDownClient(killclient);
        if (client == killclient) {
            /* force yield and return Success, so that Dispatch()
             * doesn't try to touch client
             */
            isItTimeToYield = TRUE;
        }
        return Success;
    }
    else
        return rc;
}

Project: xserver
File: dispatch.c

ProcListFonts(ClientPtr client)
{
    REQUEST(xListFontsReq);

    REQUEST_FIXED_SIZE(xListFontsReq, stuff->nbytes);

    return ListFonts(client, (unsigned char *) &stuff[1], stuff->nbytes,
                     stuff->maxNames);
}

Project: xserver
File: dispatch.c

ProcPolyRectangle(ClientPtr client)
{
    int nrects;
    GC *pGC;
    DrawablePtr pDraw;

    REQUEST(xPolyRectangleReq);

    REQUEST_AT_LEAST_SIZE(xPolyRectangleReq);
    VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess);
    nrects = (client->req_len << 2) - sizeof(xPolyRectangleReq);
    if (nrects & 4)
        return BadLength;
    nrects >>= 3;
    if (nrects)
        (*pGC->ops->PolyRectangle) (pDraw, pGC,
                                    nrects, (xRectangle *) &stuff[1]);
    return Success;
}

Project: xserver
File: dispatch.c

ProcSetFontPath(ClientPtr client)
{
    unsigned char *ptr;
    unsigned long nbytes, total;
    long nfonts;
    int n;

    REQUEST(xSetFontPathReq);

    REQUEST_AT_LEAST_SIZE(xSetFontPathReq);

    nbytes = (client->req_len << 2) - sizeof(xSetFontPathReq);
    total = nbytes;
    ptr = (unsigned char *) &stuff[1];
    nfonts = stuff->nFonts;
    while (--nfonts >= 0) {
        if ((total == 0) || (total < (n = (*ptr + 1))))
            return BadLength;
        total -= n;
        ptr += n;
    }
    if (total >= 4)
        return BadLength;
    return SetFontPath(client, stuff->nFonts, (unsigned char *) &stuff[1]);
}

Project: xserver
File: dispatch.c

SmartScheduleClient(int *clientReady, int nready)
{
    ClientPtr pClient;
    int i;
    int client;
    int bestPrio, best = 0;
    int bestRobin, robin;
    long now = SmartScheduleTime;
    long idle;

    bestPrio = -0x7fffffff;
    bestRobin = 0;
    idle = 2 * SmartScheduleSlice;
    for (i = 0; i < nready; i++) {
        client = clientReady[i];
        pClient = clients[client];
        /* Praise clients which haven't run in a while */
        if ((now - pClient->smart_stop_tick) >= idle) {
            if (pClient->smart_priority < 0)
                pClient->smart_priority++;
        }

        /* check priority to select best client */
        robin =
            (pClient->index -
             SmartLastIndex[pClient->smart_priority -
                            SMART_MIN_PRIORITY]) & 0xff;
        if (pClient->smart_priority > bestPrio ||
            (pClient->smart_priority == bestPrio && robin > bestRobin)) {
            bestPrio = pClient->smart_priority;
            bestRobin = robin;
            best = client;
        }
#ifdef SMART_DEBUG
        if ((now - SmartLastPrint) >= 5000)
            fprintf(stderr, " %2d: %3d", client, pClient->smart_priority);
#endif
    }
#ifdef SMART_DEBUG
    if ((now - SmartLastPrint) >= 5000) {
        fprintf(stderr, " use %2d\n", best);
        SmartLastPrint = now;
    }
#endif
    pClient = clients[best];
    SmartLastIndex[bestPrio - SMART_MIN_PRIORITY] = pClient->index;
    /*
     * Set current client pointer
     */
    if (SmartLastClient != pClient) {
        pClient->smart_start_tick = now;
        SmartLastClient = pClient;
    }
    /*
     * Adjust slice
     */
    if (nready == 1 && SmartScheduleLatencyLimited == 0) {
        /*
         * If it's been a long time since another client
         * has run, bump the slice up to get maximal
         * performance from a single client
         */
        if ((now - pClient->smart_start_tick) > 1000 &&
            SmartScheduleSlice < SmartScheduleMaxSlice) {
            SmartScheduleSlice += SmartScheduleInterval;
        }
    }
    else {
        SmartScheduleSlice = SmartScheduleInterval;
    }
    return best;
}

Project: xserver
File: dispatch.c

dixDestroyPixmap(void *value, XID pid)
{
    PixmapPtr pPixmap = (PixmapPtr) value;

    return (*pPixmap->drawable.pScreen->DestroyPixmap) (pPixmap);
}