Commit message: glx: make sure screen is non-negative in validGlxScreen

Reviewed-by: Adam Jackson <ajax@redhat.com>
Reviewed-by: Kristian Høgsberg <krh@bitplanet.net>
Reviewed-by: Daniel Stone <daniel@fooishbar.org>
Signed-off-by: Julien Cristau <jcristau@debian.org>
CVE: CVE-2010-4818
CWE: CWE-20
Vulnerability description: The GLX extension in X.Org xserver 1.7.7 allows remote authenticated users to cause a denial of service (server crash) and possibly execute arbitrary code via (1) a crafted request that triggers a client swap in glx/glxcmdsswap.c; or (2) a crafted length or (3) a negative value in the screen field in a request to glx/glxcmds.c.

Project: xserver
File: glxcmds.c

int __glXDisp_CreateWindow(__GLXclientState *cl, GLbyte *pc)
{
    xGLXCreateWindowReq	*req = (xGLXCreateWindowReq *) pc;
    __GLXconfig	*config;
    __GLXscreen		*pGlxScreen;
    ClientPtr		 client = cl->client;
    DrawablePtr		 pDraw;
    int			 err;

    LEGAL_NEW_RESOURCE(req->glxwindow, client);

    if (!validGlxScreen(client, req->screen, &pGlxScreen, &err))
	return err;
    if (!validGlxFBConfig(client, pGlxScreen, req->fbconfig, &config, &err))
	return err;

    err = dixLookupDrawable(&pDraw, req->window, client, 0, DixAddAccess);
    if (err != Success || pDraw->type != DRAWABLE_WINDOW) {
	client->errorValue = req->window;
	return BadWindow;
    }

    if (!validGlxFBConfigForWindow(client, config, pDraw, &err))
	return err;

    return DoCreateGLXDrawable(client, pGlxScreen, config,
			       pDraw, req->window,
			       req->glxwindow, GLX_DRAWABLE_WINDOW);
}

Project: xserver
File: glxcmds.c

int __glXDisp_GetFBConfigsSGIX(__GLXclientState *cl, GLbyte *pc)
{
    xGLXGetFBConfigsSGIXReq *req = (xGLXGetFBConfigsSGIXReq *) pc;
    return DoGetFBConfigs(cl, req->screen);
}

Project: xserver
File: glxcmds.c

int __glXDisp_QueryServerString(__GLXclientState *cl, GLbyte *pc)
{
    ClientPtr client = cl->client;
    xGLXQueryServerStringReq *req = (xGLXQueryServerStringReq *) pc;
    xGLXQueryServerStringReply reply;
    size_t n, length;
    const char *ptr;
    char *buf;
    __GLXscreen *pGlxScreen;
    int err;
    char ver_str[16];

    if (!validGlxScreen(client, req->screen, &pGlxScreen, &err))
	return err;

    switch(req->name) {
	case GLX_VENDOR:
	    ptr = pGlxScreen->GLXvendor;
	    break;
	case GLX_VERSION:
	    /* Return to the server version rather than the screen version
	     * to prevent confusion when they do not match.
	     */
	    snprintf(ver_str, 16, "%d.%d", glxMajorVersion, glxMinorVersion);
	    ptr = ver_str;
	    break;
	case GLX_EXTENSIONS:
	    ptr = pGlxScreen->GLXextensions;
	    break;
	default:
	    return BadValue; 
    }

    n = strlen(ptr) + 1;
    length = __GLX_PAD(n) >> 2;
    reply.type = X_Reply;
    reply.sequenceNumber = client->sequence;
    reply.length = length;
    reply.n = n;

    buf = (char *) malloc(length << 2);
    if (buf == NULL) {
        return BadAlloc;
    }
    memcpy(buf, ptr, n);

    if (client->swapped) {
        glxSwapQueryServerStringReply(client, &reply, buf);
    } else {
        WriteToClient(client, sz_xGLXQueryServerStringReply, (char *)&reply);
        WriteToClient(client, (int)(length << 2), buf);
    }

    free(buf);
    return Success;
}

Project: xserver
File: glxcmds.c

int __glXDisp_RenderLarge(__GLXclientState *cl, GLbyte *pc)
{
    xGLXRenderLargeReq *req;
    ClientPtr client= cl->client;
    size_t dataBytes;
    __GLXrenderLargeHeader *hdr;
    __GLXcontext *glxc;
    int error;
    CARD16 opcode;
    __GLX_DECLARE_SWAP_VARIABLES;
    
    req = (xGLXRenderLargeReq *) pc;
    if (client->swapped) {
	__GLX_SWAP_SHORT(&req->length);
	__GLX_SWAP_INT(&req->contextTag);
	__GLX_SWAP_INT(&req->dataBytes);
	__GLX_SWAP_SHORT(&req->requestNumber);
	__GLX_SWAP_SHORT(&req->requestTotal);
    }

    glxc = __glXForceCurrent(cl, req->contextTag, &error);
    if (!glxc) {
	/* Reset in case this isn't 1st request. */
	__glXResetLargeCommandStatus(cl);
	return error;
    }
    dataBytes = req->dataBytes;

    /*
    ** Check the request length.
    */
    if ((req->length << 2) != __GLX_PAD(dataBytes) + sz_xGLXRenderLargeReq) {
	client->errorValue = req->length;
	/* Reset in case this isn't 1st request. */
	__glXResetLargeCommandStatus(cl);
	return BadLength;
    }
    pc += sz_xGLXRenderLargeReq;
    
    if (cl->largeCmdRequestsSoFar == 0) {
	__GLXrenderSizeData entry;
	int extra;
	size_t cmdlen;
	int err;

	/*
	** This is the first request of a multi request command.
	** Make enough space in the buffer, then copy the entire request.
	*/
	if (req->requestNumber != 1) {
	    client->errorValue = req->requestNumber;
	    return __glXError(GLXBadLargeRequest);
	}

	hdr = (__GLXrenderLargeHeader *) pc;
	if (client->swapped) {
	    __GLX_SWAP_INT(&hdr->length);
	    __GLX_SWAP_INT(&hdr->opcode);
	}
	cmdlen = hdr->length;
	opcode = hdr->opcode;

	/*
	** Check for core opcodes and grab entry data.
	*/
	err = __glXGetProtocolSizeData(& Render_dispatch_info, opcode, & entry);
	if (err < 0) {
	    client->errorValue = opcode;
	    return __glXError(GLXBadLargeRequest);
	}

	if (entry.varsize) {
	    /*
	    ** If it's a variable-size command (a command whose length must
	    ** be computed from its parameters), all the parameters needed
	    ** will be in the 1st request, so it's okay to do this.
	    */
	    extra = (*entry.varsize)(pc + __GLX_RENDER_LARGE_HDR_SIZE,
				     client->swapped);
	    if (extra < 0) {
		extra = 0;
	    }
	    /* large command's header is 4 bytes longer, so add 4 */
	    if (cmdlen != __GLX_PAD(entry.bytes + 4 + extra)) {
		return BadLength;
	    }
	} else {
	    /* constant size command */
	    if (cmdlen != __GLX_PAD(entry.bytes + 4)) {
		return BadLength;
	    }
	}
	/*
	** Make enough space in the buffer, then copy the entire request.
	*/
	if (cl->largeCmdBufSize < cmdlen) {
	    if (!cl->largeCmdBuf) {
		cl->largeCmdBuf = (GLbyte *) malloc(cmdlen);
	    } else {
		cl->largeCmdBuf = (GLbyte *) realloc(cl->largeCmdBuf, cmdlen);
	    }
	    if (!cl->largeCmdBuf) {
		return BadAlloc;
	    }
	    cl->largeCmdBufSize = cmdlen;
	}
	memcpy(cl->largeCmdBuf, pc, dataBytes);

	cl->largeCmdBytesSoFar = dataBytes;
	cl->largeCmdBytesTotal = cmdlen;
	cl->largeCmdRequestsSoFar = 1;
	cl->largeCmdRequestsTotal = req->requestTotal;
	return Success;
	
    } else {
	/*
	** We are receiving subsequent (i.e. not the first) requests of a
	** multi request command.
	*/

	/*
	** Check the request number and the total request count.
	*/
	if (req->requestNumber != cl->largeCmdRequestsSoFar + 1) {
	    client->errorValue = req->requestNumber;
	    __glXResetLargeCommandStatus(cl);
	    return __glXError(GLXBadLargeRequest);
	}
	if (req->requestTotal != cl->largeCmdRequestsTotal) {
	    client->errorValue = req->requestTotal;
	    __glXResetLargeCommandStatus(cl);
	    return __glXError(GLXBadLargeRequest);
	}

	/*
	** Check that we didn't get too much data.
	*/
	if ((cl->largeCmdBytesSoFar + dataBytes) > cl->largeCmdBytesTotal) {
	    client->errorValue = dataBytes;
	    __glXResetLargeCommandStatus(cl);
	    return __glXError(GLXBadLargeRequest);
	}
	memcpy(cl->largeCmdBuf + cl->largeCmdBytesSoFar, pc, dataBytes);
	cl->largeCmdBytesSoFar += dataBytes;
	cl->largeCmdRequestsSoFar++;

	if (req->requestNumber == cl->largeCmdRequestsTotal) {
	    __GLXdispatchRenderProcPtr proc;

	    /*
	    ** This is the last request; it must have enough bytes to complete
	    ** the command.
	    */
	    /* NOTE: the two pad macros have been added below; they are needed
	    ** because the client library pads the total byte count, but not
	    ** the per-request byte counts.  The Protocol Encoding says the
	    ** total byte count should not be padded, so a proposal will be 
	    ** made to the ARB to relax the padding constraint on the total 
	    ** byte count, thus preserving backward compatibility.  Meanwhile, 
	    ** the padding done below fixes a bug that did not allow
	    ** large commands of odd sizes to be accepted by the server.
	    */
	    if (__GLX_PAD(cl->largeCmdBytesSoFar) !=
		__GLX_PAD(cl->largeCmdBytesTotal)) {
		client->errorValue = dataBytes;
		__glXResetLargeCommandStatus(cl);
		return __glXError(GLXBadLargeRequest);
	    }
	    hdr = (__GLXrenderLargeHeader *) cl->largeCmdBuf;
	    /*
	    ** The opcode and length field in the header had already been
	    ** swapped when the first request was received.
	    **
	    ** Use the opcode to index into the procedure table.
	    */
	    opcode = hdr->opcode;

	    proc = (__GLXdispatchRenderProcPtr)
	      __glXGetProtocolDecodeFunction(& Render_dispatch_info, opcode,
					     client->swapped);
	    if (proc == NULL) {
		client->errorValue = opcode;
		return __glXError(GLXBadLargeRequest);
	    }

	    /*
	    ** Skip over the header and execute the command.
	    */
	    (*proc)(cl->largeCmdBuf + __GLX_RENDER_LARGE_HDR_SIZE);
	    __GLX_NOTE_UNFLUSHED_CMDS(glxc);

	    /*
	    ** Reset for the next RenderLarge series.
	    */
	    __glXResetLargeCommandStatus(cl);
	} else {
	    /*
	    ** This is neither the first nor the last request.
	    */
	}
	return Success;
    }
}

Project: xserver
File: glxcmds.c

int __glXDisp_WaitGL(__GLXclientState *cl, GLbyte *pc)
{
    xGLXWaitGLReq *req = (xGLXWaitGLReq *)pc;
    GLXContextTag tag = req->contextTag;
    __GLXcontext *glxc = NULL;
    int error;

    if (tag) {
	glxc = __glXLookupContextByTag(cl, tag);
	if (!glxc)
	    return __glXError(GLXBadContextTag);
    
	if (!__glXForceCurrent(cl, req->contextTag, &error))
	    return error;

	CALL_Finish( GET_DISPATCH(), () );
    }

    if (glxc && glxc->drawPriv->waitGL)
	(*glxc->drawPriv->waitGL)(glxc->drawPriv);

    return Success;
}

Project: xserver
File: glxcmds.c

int __glXDisp_WaitX(__GLXclientState *cl, GLbyte *pc)
{
    xGLXWaitXReq *req = (xGLXWaitXReq *)pc;
    GLXContextTag tag = req->contextTag;
    __GLXcontext *glxc = NULL;
    int error;

    if (tag) {
	glxc = __glXLookupContextByTag(cl, tag);
	if (!glxc)
	    return __glXError(GLXBadContextTag);
    
	if (!__glXForceCurrent(cl, req->contextTag, &error))
	    return error;
    }

    if (glxc && glxc->drawPriv->waitX)
	(*glxc->drawPriv->waitX)(glxc->drawPriv);

    return Success;
}