Commit message: None
CVE: CVE-2017-16612
CWE: CWE-190
Vulnerability description: libXcursor before 1.1.15 has various integer overflows that could lead to heap buffer overflows when processing malicious cursors, e.g., with programs like GIMP. It is also possible that an attack vector exists against the related code in cursor/xcursor.c in Wayland through 1.14.0.

Project: xcursor

XcursorFileLoad (FILE		    *file,
		 XcursorComments    **commentsp,
		 XcursorImages	    **imagesp)
{
    XcursorFile	f;

    if (!file || !commentsp || !imagesp)
        return XcursorFalse;

    _XcursorStdioFileInitialize (file, &f);
    return XcursorXcFileLoad (&f, commentsp, imagesp);
}

Project: xcursor

XcursorFileLoadAllImages (FILE *file)
{
    XcursorFile	f;

    if (!file)
        return NULL;

    _XcursorStdioFileInitialize (file, &f);
    return XcursorXcFileLoadAllImages (&f);
}

Project: xcursor

XcursorFileLoadImage (FILE *file, int size)
{
    XcursorFile	f;

    if (!file)
        return NULL;

    _XcursorStdioFileInitialize (file, &f);
    return XcursorXcFileLoadImage (&f, size);
}

Project: xcursor

XcursorFileSave (FILE *			file,
		 const XcursorComments	*comments,
		 const XcursorImages	*images)
{
    XcursorFile	    f;

    if (!file || !comments || !images)
        return XcursorFalse;

    _XcursorStdioFileInitialize (file, &f);
    return XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
}

Project: xcursor

XcursorFileSaveImages (FILE *file, const XcursorImages *images)
{
    XcursorComments *comments;
    XcursorFile	    f;
    XcursorBool	    ret;

    if (!file || !images)
	return 0;
    if ((comments = XcursorCommentsCreate (0)) == NULL)
	return 0;
    _XcursorStdioFileInitialize (file, &f);
    ret = XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF;
    XcursorCommentsDestroy (comments);
    return ret;
}

Project: xcursor

XcursorFilenameLoad (const char		*file,
		     XcursorComments	**commentsp,
		     XcursorImages	**imagesp)
{
    FILE	    *f;
    XcursorBool	    ret;

    if (!file)
        return XcursorFalse;

    f = fopen (file, "r");
    if (!f)
	return 0;
    ret = XcursorFileLoad (f, commentsp, imagesp);
    fclose (f);
    return ret;
}

Project: xcursor

XcursorFilenameLoadAllImages (const char *file)
{
    FILE	    *f;
    XcursorImages   *images;

    if (!file)
        return NULL;

    f = fopen (file, "r");
    if (!f)
	return NULL;
    images = XcursorFileLoadAllImages (f);
    fclose (f);
    return images;
}

Project: xcursor

XcursorFilenameLoadImage (const char *file, int size)
{
    FILE	    *f;
    XcursorImage    *image;

    if (!file || size < 0)
        return NULL;

    f = fopen (file, "r");
    if (!f)
	return NULL;
    image = XcursorFileLoadImage (f, size);
    fclose (f);
    return image;
}

Project: xcursor

XcursorFilenameLoadImages (const char *file, int size)
{
    FILE	    *f;
    XcursorImages   *images;

    if (!file || size < 0)
        return NULL;

    f = fopen (file, "r");
    if (!f)
	return NULL;
    images = XcursorFileLoadImages (f, size);
    fclose (f);
    return images;
}

Project: xcursor

XcursorFilenameSave (const char		    *file,
		     const XcursorComments  *comments,
		     const XcursorImages    *images)
{
    FILE	    *f;
    XcursorBool	    ret;

    if (!file || !comments || !images)
        return XcursorFalse;

    f = fopen (file, "w");
    if (!f)
	return 0;
    ret = XcursorFileSave (f, comments, images);
    return fclose (f) != EOF && ret;
}

Project: xcursor

XcursorFilenameSaveImages (const char *file, const XcursorImages *images)
{
    FILE	    *f;
    XcursorBool	    ret;

    if (!file || !images)
        return XcursorFalse;

    f = fopen (file, "w");
    if (!f)
	return 0;
    ret = XcursorFileSaveImages (f, images);
    return fclose (f) != EOF && ret;
}

Project: xcursor

XcursorImagesSetName (XcursorImages *images, const char *name)
{
    char    *new;

    if (!images || !name)
        return;

    new = strdup (name);

    if (!new)
	return;

    if (images->name)
	free (images->name);
    images->name = new;
}

Project: xcursor

XcursorXcFileLoad (XcursorFile	    *file,
		   XcursorComments  **commentsp,
		   XcursorImages    **imagesp)
{
    XcursorFileHeader	*fileHeader;
    int			nimage;
    int			ncomment;
    XcursorImages	*images;
    XcursorImage	*image;
    XcursorComment	*comment;
    XcursorComments	*comments;
    int			toc;

    if (!file)
        return 0;
    fileHeader = _XcursorReadFileHeader (file);
    if (!fileHeader)
	return 0;
    nimage = 0;
    ncomment = 0;
    for (toc = 0; toc < fileHeader->ntoc; toc++)
    {
	switch (fileHeader->tocs[toc].type) {
	case XCURSOR_COMMENT_TYPE:
	    ncomment++;
	    break;
	case XCURSOR_IMAGE_TYPE:
	    nimage++;
	    break;
	}
    }
    images = XcursorImagesCreate (nimage);
    if (!images)
	return 0;
    comments = XcursorCommentsCreate (ncomment);
    if (!comments)
    {
	XcursorImagesDestroy (images);
	return 0;
    }
    for (toc = 0; toc < fileHeader->ntoc; toc++)
    {
	switch (fileHeader->tocs[toc].type) {
	case XCURSOR_COMMENT_TYPE:
	    comment = _XcursorReadComment (file, fileHeader, toc);
	    if (comment)
	    {
		comments->comments[comments->ncomment] = comment;
		comments->ncomment++;
	    }
	    break;
	case XCURSOR_IMAGE_TYPE:
	    image = _XcursorReadImage (file, fileHeader, toc);
	    if (image)
	    {
		images->images[images->nimage] = image;
		images->nimage++;
	    }
	    break;
	}
    }
    _XcursorFileHeaderDestroy (fileHeader);
    if (images->nimage != nimage || comments->ncomment != ncomment)
    {
	XcursorImagesDestroy (images);
	XcursorCommentsDestroy (comments);
	images = NULL;
	comments = NULL;
	return XcursorFalse;
    }
    *imagesp = images;
    *commentsp = comments;
    return XcursorTrue;
}

Project: xcursor

XcursorXcFileLoadAllImages (XcursorFile *file)
{
    XcursorFileHeader	*fileHeader;
    XcursorImage	*image;
    XcursorImages	*images;
    int			nimage;
    int			n;
    int			toc;

    if (!file)
        return NULL;

    fileHeader = _XcursorReadFileHeader (file);
    if (!fileHeader)
	return NULL;
    nimage = 0;
    for (n = 0; n < fileHeader->ntoc; n++)
    {
	switch (fileHeader->tocs[n].type) {
	case XCURSOR_IMAGE_TYPE:
	    nimage++;
	    break;
	}
    }
    images = XcursorImagesCreate (nimage);
    if (!images)
    {
	_XcursorFileHeaderDestroy (fileHeader);
	return NULL;
    }
    for (toc = 0; toc < fileHeader->ntoc; toc++)
    {
	switch (fileHeader->tocs[toc].type) {
	case XCURSOR_IMAGE_TYPE:
	    image = _XcursorReadImage (file, fileHeader, toc);
	    if (image)
	    {
		images->images[images->nimage] = image;
		images->nimage++;
	    }
	    break;
	}
    }
    _XcursorFileHeaderDestroy (fileHeader);
    if (images->nimage != nimage)
    {
	XcursorImagesDestroy (images);
	images = NULL;
    }
    return images;
}

Project: xcursor

XcursorXcFileLoadImage (XcursorFile *file, int size)
{
    XcursorFileHeader	*fileHeader;
    XcursorDim		bestSize;
    int			nsize;
    int			toc;
    XcursorImage	*image;

    if (size < 0)
	return NULL;
    fileHeader = _XcursorReadFileHeader (file);
    if (!fileHeader)
	return NULL;
    bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
    if (!bestSize)
	return NULL;
    toc = _XcursorFindImageToc (fileHeader, bestSize, 0);
    if (toc < 0)
	return NULL;
    image = _XcursorReadImage (file, fileHeader, toc);
    _XcursorFileHeaderDestroy (fileHeader);
    return image;
}

Project: xcursor

XcursorXcFileSave (XcursorFile		    *file,
		   const XcursorComments    *comments,
		   const XcursorImages	    *images)
{
    XcursorFileHeader	*fileHeader;
    XcursorUInt		position;
    int			n;
    int			toc;

    if (!file || !comments || !images)
        return XcursorFalse;

    fileHeader = _XcursorFileHeaderCreate (comments->ncomment + images->nimage);
    if (!fileHeader)
	return XcursorFalse;

    position = _XcursorFileHeaderLength (fileHeader);

    /*
     * Compute the toc.  Place the images before the comments
     * as they're more often read
     */

    toc = 0;
    for (n = 0; n < images->nimage; n++)
    {
	fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE;
	fileHeader->tocs[toc].subtype = images->images[n]->size;
	fileHeader->tocs[toc].position = position;
	position += _XcursorImageLength (images->images[n]);
	toc++;
    }

    for (n = 0; n < comments->ncomment; n++)
    {
	fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE;
	fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type;
	fileHeader->tocs[toc].position = position;
	position += _XcursorCommentLength (comments->comments[n]);
	toc++;
    }

    /*
     * Write the header and the toc
     */
    if (!_XcursorWriteFileHeader (file, fileHeader))
	goto bail;

    /*
     * Write the images
     */
    toc = 0;
    for (n = 0; n < images->nimage; n++)
    {
	if (!_XcursorWriteImage (file, fileHeader, toc, images->images[n]))
	    goto bail;
	toc++;
    }

    /*
     * Write the comments
     */
    for (n = 0; n < comments->ncomment; n++)
    {
	if (!_XcursorWriteComment (file, fileHeader, toc, comments->comments[n]))
	    goto bail;
	toc++;
    }

    _XcursorFileHeaderDestroy (fileHeader);
    return XcursorTrue;
bail:
    _XcursorFileHeaderDestroy (fileHeader);
    return XcursorFalse;
}

Project: xcursor

_XcursorCommentLength (XcursorComment	    *comment)
{
    return XCURSOR_COMMENT_HEADER_LEN + strlen (comment->comment);
}

Project: xcursor

_XcursorImageLength (XcursorImage   *image)
{
    if (!image)
        return 0;

    return XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4;
}

Project: xcursor

_XcursorReadComment (XcursorFile	    *file,
		     XcursorFileHeader	    *fileHeader,
		     int		    toc)
{
    XcursorChunkHeader	chunkHeader;
    XcursorUInt		length;
    XcursorComment	*comment;

    if (!file || !fileHeader)
        return NULL;

    /* read chunk header */
    if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
	return NULL;
    /* read extra comment header fields */
    if (!_XcursorReadUInt (file, &length))
	return NULL;
    comment = XcursorCommentCreate (chunkHeader.subtype, length);
    if (!comment)
	return NULL;
    if (!_XcursorReadBytes (file, comment->comment, length))
    {
	XcursorCommentDestroy (comment);
	return NULL;
    }
    comment->comment[length] = '\0';
    return comment;
}

Project: xcursor

_XcursorWriteComment (XcursorFile	    *file,
		      XcursorFileHeader	    *fileHeader,
		      int		    toc,
		      XcursorComment	    *comment)
{
    XcursorChunkHeader	chunkHeader;
    XcursorUInt		length;

    if (!file || !fileHeader || !comment || !comment->comment)
        return XcursorFalse;

    length = strlen (comment->comment);

    /* sanity check data */
    if (length > XCURSOR_COMMENT_MAX_LEN)
	return XcursorFalse;

    /* read chunk header */
    chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN;
    chunkHeader.type = XCURSOR_COMMENT_TYPE;
    chunkHeader.subtype = comment->comment_type;
    chunkHeader.version = XCURSOR_COMMENT_VERSION;

    if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader))
	return XcursorFalse;

    /* write extra comment header fields */
    if (!_XcursorWriteUInt (file, length))
	return XcursorFalse;

    if (!_XcursorWriteBytes (file, comment->comment, length))
	return XcursorFalse;
    return XcursorTrue;
}

Project: xcursor

_XcursorWriteImage (XcursorFile		*file,
		    XcursorFileHeader	*fileHeader,
		    int			toc,
		    XcursorImage	*image)
{
    XcursorChunkHeader	chunkHeader;
    int			n;
    XcursorPixel	*p;

    if (!file || !fileHeader || !image)
        return XcursorFalse;

    /* sanity check data */
    if (image->width > XCURSOR_IMAGE_MAX_SIZE  ||
	image->height > XCURSOR_IMAGE_MAX_SIZE)
	return XcursorFalse;
    if (image->width == 0 || image->height == 0)
	return XcursorFalse;
    if (image->xhot > image->width || image->yhot > image->height)
	return XcursorFalse;

    /* write chunk header */
    chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN;
    chunkHeader.type = XCURSOR_IMAGE_TYPE;
    chunkHeader.subtype = image->size;
    chunkHeader.version = XCURSOR_IMAGE_VERSION;

    if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader))
	return XcursorFalse;

    /* write extra image header fields */
    if (!_XcursorWriteUInt (file, image->width))
	return XcursorFalse;
    if (!_XcursorWriteUInt (file, image->height))
	return XcursorFalse;
    if (!_XcursorWriteUInt (file, image->xhot))
	return XcursorFalse;
    if (!_XcursorWriteUInt (file, image->yhot))
	return XcursorFalse;
    if (!_XcursorWriteUInt (file, image->delay))
	return XcursorFalse;

    /* write the image */
    n = image->width * image->height;
    p = image->pixels;
    while (n--)
    {
	if (!_XcursorWriteUInt (file, *p))
	    return XcursorFalse;
	p++;
    }
    return XcursorTrue;
}