From f80fa6ae47ad4a5beacb287c0030c9913b046643 Mon Sep 17 00:00:00 2001
From: Alan Coopersmith <alan.coopersmith@oracle.com>
Date: Sat, 7 Jan 2023 12:44:28 -0800
Subject: [PATCH] Fix CVE-2022-44617: Runaway loop with width of 0 and enormous
 height

When reading XPM images from a file with libXpm 3.5.14 or older, if a
image has a width of 0 and a very large height, the ParsePixels() function
will loop over the entire height calling getc() and ungetc() repeatedly,
or in some circumstances, may loop seemingly forever, which may cause a
denial of service to the calling program when given a small crafted XPM
file to parse.

Closes: #2

Reported-by: Martin Ettl <ettl.martin78@googlemail.com>
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>

Origin:
https://gitlab.freedesktop.org/xorg/lib/libxpm/-/commit/f80fa6ae47ad4a5beacb287c0030c9913b046643
---
 lib/Xm/Xpmdata.c  | 20 ++++++++++++++------
 lib/Xm/Xpmparse.c | 31 +++++++++++++++++++++++++++----
 2 files changed, 41 insertions(+), 10 deletions(-)

diff --git a/lib/Xm/Xpmdata.c b/lib/Xm/Xpmdata.c
index d65ae57..45ccfae 100644
--- a/lib/Xm/Xpmdata.c
+++ b/lib/Xm/Xpmdata.c
@@ -189,19 +189,23 @@ xpmNextString(mdata)
 	register char c;
 
 	/* get to the end of the current string */
-	if (mdata->Eos)
-	    while ((c = *mdata->cptr++) && c != mdata->Eos);
+	if (mdata->Eos) {
+	    while ((c = *mdata->cptr++) && c != mdata->Eos && c != '\0');
+
+	    if (c == '\0')
+		return XpmFileInvalid;
+	}
 
 	/*
 	 * then get to the beginning of the next string looking for possible
 	 * comment
 	 */
 	if (mdata->Bos) {
-	    while ((c = *mdata->cptr++) && c != mdata->Bos)
+	    while ((c = *mdata->cptr++) && c != mdata->Bos && c != '\0')
 		if (mdata->Bcmt && c == mdata->Bcmt[0])
 		    ParseComment(mdata);
 	} else if (mdata->Bcmt) {	/* XPM2 natural */
-	    while ((c = *mdata->cptr++) == mdata->Bcmt[0])
+	    while (((c = *mdata->cptr++) == mdata->Bcmt[0]) && c != '\0')
 		ParseComment(mdata);
 	    mdata->cptr--;
 	}
@@ -210,9 +214,13 @@ xpmNextString(mdata)
 	FILE *file = mdata->stream.file;
 
 	/* get to the end of the current string */
-	if (mdata->Eos)
+	if (mdata->Eos) {
 	    while ((c = getc(file)) != mdata->Eos && c != EOF);
 
+	    if (c == EOF)
+		return XpmFileInvalid;
+        }
+
 	/*
 	 * then get to the beginning of the next string looking for possible
 	 * comment
@@ -228,7 +236,7 @@ xpmNextString(mdata)
 	    ungetc(c, file);
 	}
     }
-    return 0;
+    return XpmSuccess;
 }
 
 
diff --git a/lib/Xm/Xpmparse.c b/lib/Xm/Xpmparse.c
index a54bca9..da21dbb 100644
--- a/lib/Xm/Xpmparse.c
+++ b/lib/Xm/Xpmparse.c
@@ -523,6 +523,13 @@ ParsePixels(data, width, height, ncolors, cpp, colorTable, hashtable, pixels)
 {
     unsigned int *iptr, *iptr2 = NULL; /* found by Egbert Eich */
     unsigned int a, x, y;
+    int ErrorStatus;
+
+    if ((width == 0) && (height != 0))
+       return (XpmFileInvalid);
+
+    if ((height == 0) && (width != 0))
+       return (XpmFileInvalid);
 
     if ((height > 0 && width >= UINT_MAX / height) ||
 	width * height >= UINT_MAX / sizeof(unsigned int)) 
@@ -560,7 +567,11 @@ ParsePixels(data, width, height, ncolors, cpp, colorTable, hashtable, pixels)
 		colidx[(unsigned char)colorTable[a].string[0]] = a + 1;
 
 	    for (y = 0; y < height; y++) {
-		xpmNextString(data);
+		ErrorStatus = xpmNextString(data);
+		if (ErrorStatus != XpmSuccess) {
+		    XpmFree(iptr2);
+		    return (ErrorStatus);
+		}
 		for (x = 0; x < width; x++, iptr++) {
 		    int c = xpmGetC(data);
 
@@ -607,7 +618,11 @@ do \
 	    }
 
 	    for (y = 0; y < height; y++) {
-		xpmNextString(data);
+		ErrorStatus = xpmNextString(data);
+		if (ErrorStatus != XpmSuccess) {
+		    XpmFree(iptr2);
+		    return (ErrorStatus);
+		}
 		for (x = 0; x < width; x++, iptr++) {
 		    int cc1 = xpmGetC(data);
 		    if (cc1 > 0 && cc1 < 256) {
@@ -646,7 +661,11 @@ do \
 		xpmHashAtom *slot;
 
 		for (y = 0; y < height; y++) {
-		    xpmNextString(data);
+		    ErrorStatus = xpmNextString(data);
+		    if (ErrorStatus != XpmSuccess) {
+			XpmFree(iptr2);
+			return (ErrorStatus);
+		    }
 		    for (x = 0; x < width; x++, iptr++) {
 			for (a = 0, s = buf; a < cpp; a++, s++)
 			    *s = xpmGetC(data); /* int assigned to char, not a problem here */
@@ -660,7 +679,11 @@ do \
 		}
 	    } else {
 		for (y = 0; y < height; y++) {
-		    xpmNextString(data);
+		    ErrorStatus = xpmNextString(data);
+		    if (ErrorStatus != XpmSuccess) {
+			XpmFree(iptr2);
+			return (ErrorStatus);
+		    }
 		    for (x = 0; x < width; x++, iptr++) {
 			for (a = 0, s = buf; a < cpp; a++, s++)
 			    *s = xpmGetC(data); /* int assigned to char, not a problem here */
-- 
2.46.0

