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

AddGPUScreen(Bool (*pfnInit) (ScreenPtr /*pScreen */ ,
                              int /*argc */ ,
                              char **      /*argv */
                              ),
             int argc, char **argv)
{
    int i;
    ScreenPtr pScreen;
    Bool ret;

    i = screenInfo.numGPUScreens;
    if (i == MAXGPUSCREENS)
        return -1;

    pScreen = (ScreenPtr) calloc(1, sizeof(ScreenRec));
    if (!pScreen)
        return -1;

    ret = init_screen(pScreen, i, TRUE);
    if (ret != 0) {
        free(pScreen);
        return ret;
    }

    /* This is where screen specific stuff gets initialized.  Load the
       screen structure, call the hardware, whatever.
       This is also where the default colormap should be allocated and
       also pixel values for blackPixel, whitePixel, and the cursor
       Note that InitScreen is NOT allowed to modify argc, argv, or
       any of the strings pointed to by argv.  They may be passed to
       multiple screens.
     */
    screenInfo.gpuscreens[i] = pScreen;
    screenInfo.numGPUScreens++;
    if (!(*pfnInit) (pScreen, argc, argv)) {
        dixFreePrivates(pScreen->devPrivates, PRIVATE_SCREEN);
        free(pScreen);
        screenInfo.numGPUScreens--;
        return -1;
    }

    update_desktop_dimensions();

    /*
     * We cannot register the Screen PRIVATE_CURSOR key if cursors are already
     * created, because dix/privates.c does not have relocation code for
     * PRIVATE_CURSOR. Once this is fixed the if() can be removed and we can
     * register the Screen PRIVATE_CURSOR key unconditionally.
     */
    if (!dixPrivatesCreated(PRIVATE_CURSOR))
        dixRegisterScreenPrivateKey(&cursorScreenDevPriv, pScreen,
                                    PRIVATE_CURSOR, 0);

    return i;
}

Project: xserver

AttachOffloadGPU(ScreenPtr pScreen, ScreenPtr new)
{
    assert(new->isGPU);
    assert(!new->is_offload_slave);
    assert(new->current_master == pScreen);
    new->is_offload_slave = TRUE;
}

Project: xserver

AttachOutputGPU(ScreenPtr pScreen, ScreenPtr new)
{
    assert(new->isGPU);
    assert(!new->is_output_slave);
    assert(new->current_master == pScreen);
    new->is_output_slave = TRUE;
    new->current_master->output_slaves++;
}

Project: xserver

AttachUnboundGPU(ScreenPtr pScreen, ScreenPtr new)
{
    assert(new->isGPU);
    assert(!new->current_master);
    xorg_list_add(&new->slave_head, &pScreen->slave_list);
    new->current_master = pScreen;
}

Project: xserver

DetachOffloadGPU(ScreenPtr slave)
{
    assert(slave->isGPU);
    assert(slave->is_offload_slave);
    slave->is_offload_slave = FALSE;
}

Project: xserver

DetachOutputGPU(ScreenPtr slave)
{
    assert(slave->isGPU);
    assert(slave->is_output_slave);
    slave->current_master->output_slaves--;
    slave->is_output_slave = FALSE;
}

Project: xserver

DoGetImage(ClientPtr client, int format, Drawable drawable,
           int x, int y, int width, int height,
           Mask planemask)
{
    DrawablePtr pDraw, pBoundingDraw;
    int nlines, linesPerBuf, rc;
    int linesDone;

    /* coordinates relative to the bounding drawable */
    int relx, rely;
    long widthBytesLine, length;
    Mask plane = 0;
    char *pBuf;
    xGetImageReply xgi;
    RegionPtr pVisibleRegion = NULL;

    if ((format != XYPixmap) && (format != ZPixmap)) {
        client->errorValue = format;
        return BadValue;
    }
    rc = dixLookupDrawable(&pDraw, drawable, client, 0, DixReadAccess);
    if (rc != Success)
        return rc;

    memset(&xgi, 0, sizeof(xGetImageReply));

    relx = x;
    rely = y;

    if (pDraw->type == DRAWABLE_WINDOW) {
        WindowPtr pWin = (WindowPtr) pDraw;

        /* "If the drawable is a window, the window must be viewable ... or a
         * BadMatch error results" */
        if (!pWin->viewable)
            return BadMatch;

        /* If the drawable is a window, the rectangle must be contained within
         * its bounds (including the border). */
        if (x < -wBorderWidth(pWin) ||
            x + width > wBorderWidth(pWin) + (int) pDraw->width ||
            y < -wBorderWidth(pWin) ||
            y + height > wBorderWidth(pWin) + (int) pDraw->height)
            return BadMatch;

        relx += pDraw->x;
        rely += pDraw->y;

        if (pDraw->pScreen->GetWindowPixmap) {
            PixmapPtr pPix = (*pDraw->pScreen->GetWindowPixmap) (pWin);

            pBoundingDraw = &pPix->drawable;
#ifdef COMPOSITE
            relx -= pPix->screen_x;
            rely -= pPix->screen_y;
#endif
        }
        else {
            pBoundingDraw = (DrawablePtr) pDraw->pScreen->root;
        }

        xgi.visual = wVisual(pWin);
    }
    else {
        pBoundingDraw = pDraw;
        xgi.visual = None;
    }

    /* "If the drawable is a pixmap, the given rectangle must be wholly
     *  contained within the pixmap, or a BadMatch error results.  If the
     *  drawable is a window [...] it must be the case that if there were no
     *  inferiors or overlapping windows, the specified rectangle of the window
     *  would be fully visible on the screen and wholly contained within the
     *  outside edges of the window, or a BadMatch error results."
     *
     * We relax the window case slightly to mean that the rectangle must exist
     * within the bounds of the window's backing pixmap.  In particular, this
     * means that a GetImage request may succeed or fail with BadMatch depending
     * on whether any of its ancestor windows are redirected.  */
    if (relx < 0 || relx + width > (int) pBoundingDraw->width ||
        rely < 0 || rely + height > (int) pBoundingDraw->height)
        return BadMatch;

    xgi.type = X_Reply;
    xgi.sequenceNumber = client->sequence;
    xgi.depth = pDraw->depth;
    if (format == ZPixmap) {
        widthBytesLine = PixmapBytePad(width, pDraw->depth);
        length = widthBytesLine * height;

    }
    else {
        widthBytesLine = BitmapBytePad(width);
        plane = ((Mask) 1) << (pDraw->depth - 1);
        /* only planes asked for */
        length = widthBytesLine * height *
            Ones(planemask & (plane | (plane - 1)));

    }

    xgi.length = length;

    xgi.length = bytes_to_int32(xgi.length);
    if (widthBytesLine == 0 || height == 0)
        linesPerBuf = 0;
    else if (widthBytesLine >= IMAGE_BUFSIZE)
        linesPerBuf = 1;
    else {
        linesPerBuf = IMAGE_BUFSIZE / widthBytesLine;
        if (linesPerBuf > height)
            linesPerBuf = height;
    }
    length = linesPerBuf * widthBytesLine;
    if (linesPerBuf < height) {
        /* we have to make sure intermediate buffers don't need padding */
        while ((linesPerBuf > 1) &&
               (length & ((1L << LOG2_BYTES_PER_SCANLINE_PAD) - 1))) {
            linesPerBuf--;
            length -= widthBytesLine;
        }
        while (length & ((1L << LOG2_BYTES_PER_SCANLINE_PAD) - 1)) {
            linesPerBuf++;
            length += widthBytesLine;
        }
    }
    if (!(pBuf = calloc(1, length)))
        return BadAlloc;
    WriteReplyToClient(client, sizeof(xGetImageReply), &xgi);

    if (pDraw->type == DRAWABLE_WINDOW)
        pVisibleRegion = &((WindowPtr) pDraw)->borderClip;

    if (linesPerBuf == 0) {
        /* nothing to do */
    }
    else if (format == ZPixmap) {
        linesDone = 0;
        while (height - linesDone > 0) {
            nlines = min(linesPerBuf, height - linesDone);
            (*pDraw->pScreen->GetImage) (pDraw,
                                         x,
                                         y + linesDone,
                                         width,
                                         nlines,
                                         format, planemask, (void *) pBuf);
            if (pVisibleRegion)
                XaceCensorImage(client, pVisibleRegion, widthBytesLine,
                                pDraw, x, y + linesDone, width,
                                nlines, format, pBuf);

            /* Note that this is NOT a call to WriteSwappedDataToClient,
               as we do NOT byte swap */
            ReformatImage(pBuf, (int) (nlines * widthBytesLine),
                          BitsPerPixel(pDraw->depth), ClientOrder(client));

            WriteToClient(client, (int) (nlines * widthBytesLine), pBuf);
            linesDone += nlines;
        }
    }
    else {                      /* XYPixmap */

        for (; plane; plane >>= 1) {
            if (planemask & plane) {
                linesDone = 0;
                while (height - linesDone > 0) {
                    nlines = min(linesPerBuf, height - linesDone);
                    (*pDraw->pScreen->GetImage) (pDraw,
                                                 x,
                                                 y + linesDone,
                                                 width,
                                                 nlines,
                                                 format, plane, (void *) pBuf);
                    if (pVisibleRegion)
                        XaceCensorImage(client, pVisibleRegion,
                                        widthBytesLine,
                                        pDraw, x, y + linesDone, width,
                                        nlines, format, pBuf);

                    /* Note: NOT a call to WriteSwappedDataToClient,
                       as we do NOT byte swap */
                    ReformatImage(pBuf, (int) (nlines * widthBytesLine),
                                  1, ClientOrder(client));

                    WriteToClient(client, (int)(nlines * widthBytesLine), pBuf);
                    linesDone += nlines;
                }
            }
        }
    }
    free(pBuf);
    return Success;
}

Project: xserver

InitClient(ClientPtr client, int i, void *ospriv)
{
    client->index = i;
    xorg_list_init(&client->ready);
    xorg_list_init(&client->output_pending);
    client->clientAsMask = ((Mask) i) << CLIENTOFFSET;
    client->closeDownMode = i ? DestroyAll : RetainPermanent;
    client->requestVector = InitialVector;
    client->osPrivate = ospriv;
    QueryMinMaxKeyCodes(&client->minKC, &client->maxKC);
    client->smart_start_tick = SmartScheduleTime;
    client->smart_stop_tick = SmartScheduleTime;
    client->clientIds = NULL;
}

Project: xserver

NextAvailableClient(void *ospriv)
{
    int i;
    ClientPtr client;
    xReq data;

    i = nextFreeClientID;
    if (i == LimitClients)
        return (ClientPtr) NULL;
    clients[i] = client =
        dixAllocateObjectWithPrivates(ClientRec, PRIVATE_CLIENT);
    if (!client)
        return (ClientPtr) NULL;
    InitClient(client, i, ospriv);
    if (!InitClientResources(client)) {
        dixFreeObjectWithPrivates(client, PRIVATE_CLIENT);
        return (ClientPtr) NULL;
    }
    data.reqType = 1;
    data.length = bytes_to_int32(sz_xReq + sz_xConnClientPrefix);
    if (!InsertFakeRequest(client, (char *) &data, sz_xReq)) {
        FreeClientResources(client);
        dixFreeObjectWithPrivates(client, PRIVATE_CLIENT);
        return (ClientPtr) NULL;
    }
    if (i == currentMaxClients)
        currentMaxClients++;
    while ((nextFreeClientID < LimitClients) && clients[nextFreeClientID])
        nextFreeClientID++;

    /* Enable client ID tracking. This must be done before
     * ClientStateCallback. */
    ReserveClientIds(client);

    if (ClientStateCallback) {
        NewClientInfoRec clientinfo;

        clientinfo.client = client;
        clientinfo.prefix = (xConnSetupPrefix *) NULL;
        clientinfo.setup = (xConnSetup *) NULL;
        CallCallbacks((&ClientStateCallback), (void *) &clientinfo);
    }
    return client;
}

Project: xserver

ProcCreateCursor(ClientPtr client)
{
    CursorPtr pCursor;
    PixmapPtr src;
    PixmapPtr msk;
    unsigned char *srcbits;
    unsigned char *mskbits;
    unsigned short width, height;
    long n;
    CursorMetricRec cm;
    int rc;

    REQUEST(xCreateCursorReq);

    REQUEST_SIZE_MATCH(xCreateCursorReq);
    LEGAL_NEW_RESOURCE(stuff->cid, client);

    rc = dixLookupResourceByType((void **) &src, stuff->source, RT_PIXMAP,
                                 client, DixReadAccess);
    if (rc != Success) {
        client->errorValue = stuff->source;
        return rc;
    }

    if (src->drawable.depth != 1)
        return (BadMatch);

    /* Find and validate cursor mask pixmap, if one is provided */
    if (stuff->mask != None) {
        rc = dixLookupResourceByType((void **) &msk, stuff->mask, RT_PIXMAP,
                                     client, DixReadAccess);
        if (rc != Success) {
            client->errorValue = stuff->mask;
            return rc;
        }

        if (src->drawable.width != msk->drawable.width
            || src->drawable.height != msk->drawable.height
            || src->drawable.depth != 1 || msk->drawable.depth != 1)
            return BadMatch;
    }
    else
        msk = NULL;

    width = src->drawable.width;
    height = src->drawable.height;

    if (stuff->x > width || stuff->y > height)
        return BadMatch;

    srcbits = calloc(BitmapBytePad(width), height);
    if (!srcbits)
        return BadAlloc;
    n = BitmapBytePad(width) * height;
    mskbits = malloc(n);
    if (!mskbits) {
        free(srcbits);
        return BadAlloc;
    }

    (*src->drawable.pScreen->GetImage) ((DrawablePtr) src, 0, 0, width, height,
                                        XYPixmap, 1, (void *) srcbits);
    if (msk == (PixmapPtr) NULL) {
        unsigned char *bits = mskbits;

        while (--n >= 0)
            *bits++ = ~0;
    }
    else {
        /* zeroing the (pad) bits helps some ddx cursor handling */
        memset((char *) mskbits, 0, n);
        (*msk->drawable.pScreen->GetImage) ((DrawablePtr) msk, 0, 0, width,
                                            height, XYPixmap, 1,
                                            (void *) mskbits);
    }
    cm.width = width;
    cm.height = height;
    cm.xhot = stuff->x;
    cm.yhot = stuff->y;
    rc = AllocARGBCursor(srcbits, mskbits, NULL, &cm,
                         stuff->foreRed, stuff->foreGreen, stuff->foreBlue,
                         stuff->backRed, stuff->backGreen, stuff->backBlue,
                         &pCursor, client, stuff->cid);

    if (rc != Success)
        goto bail;
    if (!AddResource(stuff->cid, RT_CURSOR, (void *) pCursor)) {
        rc = BadAlloc;
        goto bail;
    }

    return Success;
 bail:
    free(srcbits);
    free(mskbits);
    return rc;
}

Project: xserver
File: dispatch.c

ProcPutImage(ClientPtr client)
{
    GC *pGC;
    DrawablePtr pDraw;
    long length;                /* length of scanline server padded */
    long lengthProto;           /* length of scanline protocol padded */
    char *tmpImage;

    REQUEST(xPutImageReq);

    REQUEST_AT_LEAST_SIZE(xPutImageReq);
    VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess);
    if (stuff->format == XYBitmap) {
        if ((stuff->depth != 1) ||
            (stuff->leftPad >= (unsigned int) screenInfo.bitmapScanlinePad))
            return BadMatch;
        length = BitmapBytePad(stuff->width + stuff->leftPad);
    }
    else if (stuff->format == XYPixmap) {
        if ((pDraw->depth != stuff->depth) ||
            (stuff->leftPad >= (unsigned int) screenInfo.bitmapScanlinePad))
            return BadMatch;
        length = BitmapBytePad(stuff->width + stuff->leftPad);
        length *= stuff->depth;
    }
    else if (stuff->format == ZPixmap) {
        if ((pDraw->depth != stuff->depth) || (stuff->leftPad != 0))
            return BadMatch;
        length = PixmapBytePad(stuff->width, stuff->depth);
    }
    else {
        client->errorValue = stuff->format;
        return BadValue;
    }

    tmpImage = (char *) &stuff[1];
    lengthProto = length;

    if (stuff->height != 0 && lengthProto >= (INT32_MAX / stuff->height))
        return BadLength;

    if ((bytes_to_int32(lengthProto * stuff->height) +
         bytes_to_int32(sizeof(xPutImageReq))) != client->req_len)
        return BadLength;

    ReformatImage(tmpImage, lengthProto * stuff->height,
                  stuff->format == ZPixmap ? BitsPerPixel(stuff->depth) : 1,
                  ClientOrder(client));

    (*pGC->ops->PutImage) (pDraw, pGC, stuff->depth, stuff->dstX, stuff->dstY,
                           stuff->width, stuff->height,
                           stuff->leftPad, stuff->format, tmpImage);

    return Success;
}

Project: xserver

ProcQueryColors(ClientPtr client)
{
    ColormapPtr pcmp;
    int rc;

    REQUEST(xQueryColorsReq);

    REQUEST_AT_LEAST_SIZE(xQueryColorsReq);
    rc = dixLookupResourceByType((void **) &pcmp, stuff->cmap, RT_COLORMAP,
                                 client, DixReadAccess);
    if (rc == Success) {
        int count;
        xrgb *prgbs;
        xQueryColorsReply qcr;

        count =
            bytes_to_int32((client->req_len << 2) - sizeof(xQueryColorsReq));
        prgbs = calloc(count, sizeof(xrgb));
        if (!prgbs && count)
            return BadAlloc;
        if ((rc =
             QueryColors(pcmp, count, (Pixel *) &stuff[1], prgbs, client))) {
            free(prgbs);
            return rc;
        }
        qcr = (xQueryColorsReply) {
            .type = X_Reply,
            .sequenceNumber = client->sequence,
            .length = bytes_to_int32(count * sizeof(xrgb)),
            .nColors = count
        };
        WriteReplyToClient(client, sizeof(xQueryColorsReply), &qcr);
        if (count) {
            client->pSwapReplyFunc = (ReplySwapPtr) SQColorsExtend;
            WriteSwappedDataToClient(client, count * sizeof(xrgb), prgbs);
        }
        free(prgbs);
        return Success;

    }
    else {
        client->errorValue = stuff->cmap;
        return rc;
    }
}

Project: xserver

SmartScheduleClient(void)
{
    ClientPtr pClient, best = NULL;
    int bestRobin, robin;
    long now = SmartScheduleTime;
    long idle;
    int nready = 0;

    bestRobin = 0;
    idle = 2 * SmartScheduleSlice;

    xorg_list_for_each_entry(pClient, &ready_clients, ready) {
        nready++;

        /* 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;

        /* pick the best client */
        if (!best ||
            pClient->priority > best->priority ||
            (pClient->priority == best->priority &&
             (pClient->smart_priority > best->smart_priority ||
              (pClient->smart_priority == best->smart_priority && robin > bestRobin))))
        {
            best = pClient;
            bestRobin = robin;
        }
#ifdef SMART_DEBUG
        if ((now - SmartLastPrint) >= 5000)
            fprintf(stderr, " %2d: %3d", pClient->index, pClient->smart_priority);
#endif
    }
#ifdef SMART_DEBUG
    if ((now - SmartLastPrint) >= 5000) {
        fprintf(stderr, " use %2d\n", best->index);
        SmartLastPrint = now;
    }
#endif
    SmartLastIndex[best->smart_priority - SMART_MIN_PRIORITY] = best->index;
    /*
     * Set current client pointer
     */
    if (SmartLastClient != best) {
        best->smart_start_tick = now;
        SmartLastClient = best;
    }
    /*
     * 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 - best->smart_start_tick) > 1000 &&
            SmartScheduleSlice < SmartScheduleMaxSlice) {
            SmartScheduleSlice += SmartScheduleInterval;
        }
    }
    else {
        SmartScheduleSlice = SmartScheduleInterval;
    }
    return best;
}

Project: xserver

UpdateCurrentTime(void)
{
    TimeStamp systime;

    /* To avoid time running backwards, we must call GetTimeInMillis before
     * calling ProcessInputEvents.
     */
    systime.months = currentTime.months;
    systime.milliseconds = GetTimeInMillis();
    if (systime.milliseconds < currentTime.milliseconds)
        systime.months++;
    if (InputCheckPending())
        ProcessInputEvents();
    if (CompareTimeStamps(systime, currentTime) == LATER)
        currentTime = systime;
}

Project: xserver

clients_are_ready(void)
{
    return !xorg_list_is_empty(&ready_clients);
}

Project: xserver

init_client_ready(void)
{
    xorg_list_init(&ready_clients);
    xorg_list_init(&saved_ready_clients);
    xorg_list_init(&output_pending_clients);
}

Project: xserver

static int init_screen(ScreenPtr pScreen, int i, Bool gpu)
{
    int scanlinepad, format, depth, bitsPerPixel, j, k;

    dixInitScreenSpecificPrivates(pScreen);

    if (!dixAllocatePrivates(&pScreen->devPrivates, PRIVATE_SCREEN)) {
        return -1;
    }
    pScreen->myNum = i;
    if (gpu) {
        pScreen->myNum += GPU_SCREEN_OFFSET;
        pScreen->isGPU = TRUE;
    }
    pScreen->totalPixmapSize = 0;       /* computed in CreateScratchPixmapForScreen */
    pScreen->ClipNotify = 0;    /* for R4 ddx compatibility */
    pScreen->CreateScreenResources = 0;

    xorg_list_init(&pScreen->pixmap_dirty_list);
    xorg_list_init(&pScreen->slave_list);

    /*
     * This loop gets run once for every Screen that gets added,
     * but thats ok.  If the ddx layer initializes the formats
     * one at a time calling AddScreen() after each, then each
     * iteration will make it a little more accurate.  Worst case
     * we do this loop N * numPixmapFormats where N is # of screens.
     * Anyway, this must be called after InitOutput and before the
     * screen init routine is called.
     */
    for (format = 0; format < screenInfo.numPixmapFormats; format++) {
        depth = screenInfo.formats[format].depth;
        bitsPerPixel = screenInfo.formats[format].bitsPerPixel;
        scanlinepad = screenInfo.formats[format].scanlinePad;
        j = indexForBitsPerPixel[bitsPerPixel];
        k = indexForScanlinePad[scanlinepad];
        PixmapWidthPaddingInfo[depth].padPixelsLog2 = answer[j][k];
        PixmapWidthPaddingInfo[depth].padRoundUp =
            (scanlinepad / bitsPerPixel) - 1;
        j = indexForBitsPerPixel[8];    /* bits per byte */
        PixmapWidthPaddingInfo[depth].padBytesLog2 = answer[j][k];
        PixmapWidthPaddingInfo[depth].bitsPerPixel = bitsPerPixel;
        if (answerBytesPerPixel[bitsPerPixel]) {
            PixmapWidthPaddingInfo[depth].notPower2 = 1;
            PixmapWidthPaddingInfo[depth].bytesPerPixel =
                answerBytesPerPixel[bitsPerPixel];
        }
        else {
            PixmapWidthPaddingInfo[depth].notPower2 = 0;
        }
    }
    return 0;
}

Project: xserver

mark_client_grab(ClientPtr grab)
{
    ClientPtr   client, tmp;

    xorg_list_for_each_entry_safe(client, tmp, &ready_clients, ready) {
        if (client != grab) {
            xorg_list_del(&client->ready);
            xorg_list_append(&client->ready, &saved_ready_clients);
        }
    }
}

Project: xserver

mark_client_not_ready(ClientPtr client)
{
    xorg_list_del(&client->ready);
}

Project: xserver

mark_client_ready(ClientPtr client)
{
    if (xorg_list_is_empty(&client->ready))
        xorg_list_append(&client->ready, &ready_clients);
}

Project: xserver

void mark_client_saved_ready(ClientPtr client)
{
    if (xorg_list_is_empty(&client->ready))
        xorg_list_append(&client->ready, &saved_ready_clients);
}

Project: xserver

mark_client_ungrab(void)
{
    ClientPtr   client, tmp;

    xorg_list_for_each_entry_safe(client, tmp, &saved_ready_clients, ready) {
        xorg_list_del(&client->ready);
        xorg_list_append(&client->ready, &ready_clients);
    }
}