libxpm: fix CVE-2026-4367

Backport the upstream fix for CVE-2026-4367, in which the
`xpmNextWord()` function could attempt to read beyond the file's
end due to improper validation of file boundaries.

Reference: https://nvd.nist.gov/vuln/detail/CVE-2026-4367

(From OE-Core rev: b3f8956f38b2f2cb0f260ab8853e9d028831f420)

Signed-off-by: Enoch Ng <enoch.ng@windriver.com>
Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
Signed-off-by: Paul Barker <paul@pbarker.dev>
This commit is contained in:
Enoch Ng
2026-07-30 13:21:16 +01:00
committed by Paul Barker
parent 44fd9295ca
commit 9747726708
2 changed files with 141 additions and 0 deletions
@@ -0,0 +1,140 @@
From 5448e1bd7252780b16db869c2253d24e0fe0ae18 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofourdan@redhat.com>
Date: Tue, 17 Feb 2026 11:59:56 +0100
Subject: [PATCH libXpm] Fix CVE-2026-4367: Out-of-bounds read in xpmNextWord()
xpmNextWord() checks for the terminator character to detect the end of
the file, but a very small malformed XPM file may cause the function to
read past the end of the buffer, causing out-of-bound reads:
== Invalid read of size 1
== at 0x48AD3A4: xpmParseColors (parse.c:239)
== by 0x48AF9D8: xpmParseData (parse.c:783)
== by 0x48B1C18: XpmCreateXpmImageFromBuffer (CrIFrBuf.c:101)
== by 0x4005A6: main ()
== Address 0x4c413bf is 0 bytes after a block of size 15 alloc'd
== at 0x4841B26: malloc (vg_replace_malloc.c:447)
== by 0x48B2809: XpmReadFileToBuffer (RdFToBuf.c:96)
== by 0x400554: main ()
==
== Invalid read of size 1
== at 0x48AC8D5: xpmNextWord.constprop.0 (data.c:262)
== by 0x48AD492: xpmParseColors (parse.c:266)
== by 0x48AF9D8: xpmParseData (parse.c:783)
== by 0x48B1C18: XpmCreateXpmImageFromBuffer (CrIFrBuf.c:101)
== by 0x4005A6: main ()
== Address 0x4c413c0 is 1 bytes after a block of size 15 alloc'd
== at 0x4841B26: malloc (vg_replace_malloc.c:447)
== by 0x48B2809: XpmReadFileToBuffer (RdFToBuf.c:96)
== by 0x400554: main ()
==
== Invalid read of size 1
== at 0x48AC965: xpmNextWord.constprop.0 (data.c:265)
== by 0x48AD492: xpmParseColors (parse.c:266)
== by 0x48AF9D8: xpmParseData (parse.c:783)
== by 0x48B1C18: XpmCreateXpmImageFromBuffer (CrIFrBuf.c:101)
== by 0x4005A6: main ()
== Address 0x4c413c0 is 1 bytes after a block of size 15 alloc'd
== at 0x4841B26: malloc (vg_replace_malloc.c:447)
== by 0x48B2809: XpmReadFileToBuffer (RdFToBuf.c:96)
== by 0x400554: main ()
The problem actually comes from xpmNextString() and xpmParseColors():
1) xpmNextString() checks for the NULL terminator when looking for the
end of the string (Eos) but not when looking for the beginning of the
next string (Bos).
2) xpmParseColors() does not check the return value from xpmNextString()
and continues even when xpmNextString() raised an invalid XPM file.
To avoid the issue, fix xpmNextString() to check for the NULL string
terminator when looking for the beginning of the next string and fix
xpmParseColors() to stop when xpmNextString() reported an invalid XPM
error.
CVE-2026-4367
This vulnerability was discovered by:
Naoki Wakamatsu
v2: Fix the XPM 1 code path the same.
Signed-off-by: Olivier Fourdan <ofourdan@redhat.com>
Part-of: <https://gitlab.freedesktop.org/xorg/lib/libxpm/-/merge_requests/31>
CVE: CVE-2026-4367
Upstream-Status: Backport [https://gitlab.freedesktop.org/xorg/lib/libxpm/-/commit/5448e1bd7252780b16db869c2253d24e0fe0ae18]
Signed-off-by: Enoch Ng <enoch.ng@windriver.com>
---
src/data.c | 3 +++
src/parse.c | 19 ++++++++++++++-----
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/data.c b/src/data.c
index 6e87455..a2b4acc 100644
--- a/src/data.c
+++ b/src/data.c
@@ -210,6 +210,9 @@ xpmNextString(xpmData *data)
while ((c = *data->cptr++) && c != data->Bos && c != '\0')
if (data->Bcmt && c == data->Bcmt[0])
ParseComment(data);
+
+ if (c == '\0')
+ return XpmFileInvalid;
} else if (data->Bcmt) { /* XPM2 natural */
while (((c = *data->cptr++) == data->Bcmt[0]) && c != '\0')
ParseComment(data);
diff --git a/src/parse.c b/src/parse.c
index cd923f9..268954d 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -216,7 +216,9 @@ xpmParseColors(
if (!data->format) { /* XPM 2 or 3 */
for (a = 0, color = colorTable; a < ncolors; a++, color++) {
- xpmNextString(data); /* skip the line */
+ ErrorStatus = xpmNextString(data); /* skip the line */
+ if (ErrorStatus != XpmSuccess)
+ goto error;
/*
* read pixel value
@@ -314,7 +316,9 @@ xpmParseColors(
/* get to the beginning of the first string */
data->Bos = '"';
data->Eos = '\0';
- xpmNextString(data);
+ ErrorStatus = xpmNextString(data);
+ if (ErrorStatus != XpmSuccess)
+ goto error;
data->Eos = '"';
for (a = 0, color = colorTable; a < ncolors; a++, color++) {
@@ -354,7 +358,9 @@ xpmParseColors(
/*
* read color values
*/
- xpmNextString(data); /* get to the next string */
+ ErrorStatus = xpmNextString(data); /* get to the next string */
+ if (ErrorStatus != XpmSuccess)
+ goto error;
*curbuf = '\0'; /* init curbuf */
while ((l = xpmNextWord(data, buf, BUFSIZ))) {
if (*curbuf != '\0') {
@@ -378,8 +384,11 @@ xpmParseColors(
memcpy(s, curbuf, len);
color->c_color = s;
*curbuf = '\0'; /* reset curbuf */
- if (a < ncolors - 1) /* can we trust ncolors -> leave data's bounds */
- xpmNextString(data); /* get to the next string */
+ if (a < ncolors - 1) { /* can we trust ncolors -> leave data's bounds */
+ ErrorStatus = xpmNextString(data); /* get to the next string */
+ if (ErrorStatus != XpmSuccess)
+ goto error;
+ }
}
}
*colorTablePtr = colorTable;
@@ -22,6 +22,7 @@ PACKAGES =+ "sxpm cxpm"
FILES:cxpm = "${bindir}/cxpm"
FILES:sxpm = "${bindir}/sxpm"
SRC_URI += " file://0001-Fix-CVE-2026-4367-Out-of-bounds-read-in-xpmNextWord.patch"
SRC_URI[sha256sum] = "64b31f81019e7d388c822b0b28af8d51c4622b83f1f0cb6fa3fc95e271226e43"
BBCLASSEXTEND = "native"