Compare commits

...

No commits in common. "c8s" and "c9-beta" have entirely different histories.
c8s ... c9-beta

10 changed files with 32 additions and 975 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
SOURCES/libXfont2-2.0.3.tar.bz2 SOURCES/libXfont2-2.0.3.tar.bz2
/libXfont2-2.0.3.tar.bz2

1
.libXfont2.metadata Normal file
View File

@ -0,0 +1 @@
1110f1ad4061d9e8131ecb941757480e3e32bca0 SOURCES/libXfont2-2.0.3.tar.bz2

View File

@ -1,71 +0,0 @@
From be0b08e2d354138d3222b4490e2a77c6ee42f778 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Mon, 1 Jun 2026 16:46:10 +1000
Subject: [PATCH libXfont 1/3] bitscale: fix integer overflow in
BitmapScaleBitmaps bytestoalloc
bytestoalloc is declared as unsigned int (32-bit). When the sum of
per-glyph byte counts exceeds 2^32, the value wraps around and calloc()
allocates a buffer that is too small. The subsequent ScaleBitmap loop
then writes past the end of the allocated buffer.
Change bytestoalloc from unsigned int to size_t to match the actual
allocation size type, and add an explicit overflow check in the
accumulation loop to bail out if the total would exceed SIZE_MAX.
This vulnerability was discovered by:
Anonymous working with TrendAI Zero Day Initiative
CVE-2026-56001/ZDI-CAN-30558
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Part-of: <https://gitlab.freedesktop.org/xorg/lib/libxfont/-/merge_requests/34>
---
src/bitmap/bitscale.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/src/bitmap/bitscale.c b/src/bitmap/bitscale.c
index 3f3c10e..5f465d1 100644
--- a/src/bitmap/bitscale.c
+++ b/src/bitmap/bitscale.c
@@ -1456,7 +1456,7 @@ BitmapScaleBitmaps(FontPtr pf, /* scaled font */
opci;
FontInfoPtr pfi;
int glyph;
- unsigned bytestoalloc = 0;
+ size_t bytestoalloc = 0;
int firstCol, lastCol, firstRow, lastRow;
double xform[4], inv_xform[4];
@@ -1483,8 +1483,25 @@ BitmapScaleBitmaps(FontPtr pf, /* scaled font */
glyph = pf->glyph;
for (i = 0; i < nchars; i++)
{
- if ((pci = ACCESSENCODING(bitmapFont->encoding, i)))
- bytestoalloc += BYTES_FOR_GLYPH(pci, glyph);
+ if ((pci = ACCESSENCODING(bitmapFont->encoding, i))) {
+ size_t glyphsize = BYTES_FOR_GLYPH(pci, glyph);
+ if (bytestoalloc > SIZE_MAX - glyphsize) {
+ fprintf(stderr,
+ "Error: bitmap allocation overflow for scaled font\n");
+ goto bail;
+ }
+ bytestoalloc += glyphsize;
+ }
+ }
+
+ /* Reject unreasonably large bitmap allocations that could result
+ * from malicious fonts with extreme scale factors. 256 MiB is
+ * far beyond any legitimate scaled bitmap font. */
+#define BITMAP_SCALE_MAX_ALLOC (256 * 1024 * 1024)
+ if (bytestoalloc > BITMAP_SCALE_MAX_ALLOC) {
+ fprintf(stderr,
+ "Error: scaled bitmap size %zu exceeds limit\n", bytestoalloc);
+ goto bail;
}
/* Do we add the font malloc stuff for VALUE ADDED ? */
--
2.55.0

View File

@ -1,134 +0,0 @@
From b4389e0b1d84a690b819bb27b1439968811a3674 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Mon, 1 Jun 2026 16:48:40 +1000
Subject: [PATCH libXfont 2/3] pcfread: validate bitmap sizes and offsets
against per-glyph metrics
pcfReadFont() uses bitmapSizes[] read directly from the PCF file to
allocate the repadded bitmap buffer. However, per-glyph metrics (also
from the file) control how much data RepadBitmap() writes. A malicious
PCF font can declare a small bitmapSizes[] value while having per-glyph
metrics that require more space, causing a heap buffer overflow.
A similar issue happens with the encoding offsets: pcfReadFont reads
encoding offsets from the PCF file and uses them to index into the
metrics array without bounds checking. A crafted font can set an
encoding offset larger than nmetrics, causing an out-of-bounds pointer
that is later dereferenced when glyphs are accessed through the encoding
table.
And the no-repad bitmap path (when PCF_GLYPH_PAD matches the requested
glyph pad) only validated that each glyph's offset was within the bitmap
buffer, but did not check that the full glyph extent (offset +
BYTES_PER_ROW * height) fits within the buffer. A crafted font with a
glyph offset near the end of a small bitmap buffer but large glyph
metrics causes a heap buffer over-read when the glyph is later rendered.
This vulnerability was discovered by:
Anonymous working with TrendAI Zero Day Initiative
CVE-2026-56002/ZDI-CAN-30559
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Part-of: <https://gitlab.freedesktop.org/xorg/lib/libxfont/-/merge_requests/34>
---
src/bitmap/pcfread.c | 59 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 56 insertions(+), 3 deletions(-)
diff --git a/src/bitmap/pcfread.c b/src/bitmap/pcfread.c
index 7c2e7e1..a385331 100644
--- a/src/bitmap/pcfread.c
+++ b/src/bitmap/pcfread.c
@@ -532,25 +532,74 @@ pcfReadFont(FontPtr pFont, FontFilePtr file,
int old,
new;
xCharInfo *metric;
+ int srcPad = PCF_GLYPH_PAD(format);
- sizepadbitmaps = bitmapSizes[PCF_SIZE_TO_INDEX(glyph)];
- padbitmaps = malloc(sizepadbitmaps);
+ /* Compute the actual required size from per-glyph metrics instead
+ * of trusting the file's bitmapSizes[] value, which may be smaller
+ * than the actual data written by RepadBitmap. */
+ sizepadbitmaps = 0;
+ for (i = 0; i < nbitmaps; i++) {
+ int w, h, glyphBytes;
+ metric = &metrics[i].metrics;
+ w = metric->rightSideBearing - metric->leftSideBearing;
+ h = metric->ascent + metric->descent;
+ glyphBytes = BYTES_PER_ROW(w, glyph) * h;
+ if (glyphBytes < 0 || (glyphBytes > 0 && sizepadbitmaps > INT_MAX - glyphBytes)) {
+ pcfError("pcfReadFont(): bitmap size overflow\n");
+ goto Bail;
+ }
+ sizepadbitmaps += glyphBytes;
+ }
+ padbitmaps = malloc(sizepadbitmaps ? sizepadbitmaps : 1);
if (!padbitmaps) {
pcfError("pcfReadFont(): Couldn't allocate padbitmaps (%d)\n", sizepadbitmaps);
goto Bail;
}
new = 0;
for (i = 0; i < nbitmaps; i++) {
+ int srcGlyphBytes;
+
old = offsets[i];
metric = &metrics[i].metrics;
+
+ /* Validate source offset and source glyph size against the
+ * source bitmap buffer to prevent out-of-bounds reads. */
+ srcGlyphBytes = BYTES_PER_ROW(
+ metric->rightSideBearing - metric->leftSideBearing,
+ srcPad) * (metric->ascent + metric->descent);
+ if (old < 0 || old > sizebitmaps ||
+ srcGlyphBytes < 0 || srcGlyphBytes > sizebitmaps - old) {
+ pcfError("pcfReadFont(): bitmap offset/size out of bounds\n");
+ free(padbitmaps);
+ goto Bail;
+ }
+
offsets[i] = new;
new += RepadBitmap(bitmaps + old, padbitmaps + new,
- PCF_GLYPH_PAD(format), glyph,
+ srcPad, glyph,
metric->rightSideBearing - metric->leftSideBearing,
metric->ascent + metric->descent);
}
free(bitmaps);
bitmaps = padbitmaps;
+ } else {
+ /* Validate offsets and full glyph extents against bitmap buffer */
+ for (i = 0; i < nbitmaps; i++) {
+ int glyphBytes;
+ xCharInfo *metric = &metrics[i].metrics;
+
+ glyphBytes = BYTES_PER_ROW(
+ metric->rightSideBearing - metric->leftSideBearing,
+ glyph) * (metric->ascent + metric->descent);
+ if (offsets[i] >= (CARD32)sizebitmaps ||
+ glyphBytes < 0 ||
+ glyphBytes > sizebitmaps - (int)offsets[i]) {
+ pcfError("pcfReadFont(): bitmap offset/size out of bounds "
+ "(offset %u, size %d, total %d)\n",
+ offsets[i], glyphBytes, sizebitmaps);
+ goto Bail;
+ }
+ }
}
for (i = 0; i < nbitmaps; i++)
metrics[i].bits = bitmaps + offsets[i];
@@ -625,6 +674,10 @@ pcfReadFont(FontPtr pFont, FontFilePtr file,
if (IS_EOF(file)) goto Bail;
if (encodingOffset == 0xFFFF) {
pFont->info.allExist = FALSE;
+ } else if (encodingOffset >= nmetrics) {
+ pcfError("pcfReadFont(): encoding offset %d out of range (nmetrics=%d)\n",
+ encodingOffset, nmetrics);
+ goto Bail;
} else {
if(!encoding[SEGMENT_MAJOR(i)]) {
encoding[SEGMENT_MAJOR(i)]=
--
2.55.0

View File

@ -1,110 +0,0 @@
From dff957a5158da038a282a59a31fe736702732939 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Mon, 1 Jun 2026 16:49:55 +1000
Subject: [PATCH libXfont 3/3] bitscale: add bounds check to computeProps for
property buffer
ComputeScaledProperties allocates a fixed-size property buffer of 70
slots. computeProps iterates the source font's properties and writes 1
slot for unscaled properties or 2 slots for scaledX/scaledY properties,
with no bounds check. A malicious font with many duplicate properties
matching fontPropTable entries can overflow the allocated buffer.
Fix this by passing the remaining buffer capacity to computeProps and
checking it before each write. Properties that would exceed the buffer
are silently skipped.
The function is also restructured to handle the buffer writes for
scaledX/scaledY inside the switch cases directly, rather than in a
separate block after the switch. This makes the control flow clearer and
ensures the bounds check covers all writes.
This vulnerability was discovered by:
Anonymous working with TrendAI Zero Day Initiative
CVE-2026-56003/ZDI-CAN-30560
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Part-of: <https://gitlab.freedesktop.org/xorg/lib/libxfont/-/merge_requests/34>
---
src/bitmap/bitscale.c | 39 ++++++++++++++++++++-------------------
1 file changed, 20 insertions(+), 19 deletions(-)
diff --git a/src/bitmap/bitscale.c b/src/bitmap/bitscale.c
index 5f465d1..ec57f55 100644
--- a/src/bitmap/bitscale.c
+++ b/src/bitmap/bitscale.c
@@ -507,7 +507,8 @@ static int
computeProps(FontPropPtr pf, char *wasStringProp,
FontPropPtr npf, char *isStringProp,
unsigned int nprops, double xfactor, double yfactor,
- double sXfactor, double sYfactor)
+ double sXfactor, double sYfactor,
+ int maxprops)
{
int n;
int count;
@@ -522,14 +523,26 @@ computeProps(FontPropPtr pf, char *wasStringProp,
switch (t->type) {
case scaledX:
- npf->value = doround(xfactor * (double)pf->value);
- rawfactor = sXfactor;
- break;
case scaledY:
- npf->value = doround(yfactor * (double)pf->value);
- rawfactor = sYfactor;
+ if (count + 2 > maxprops)
+ continue;
+ npf->value = (t->type == scaledX)
+ ? doround(xfactor * (double)pf->value)
+ : doround(yfactor * (double)pf->value);
+ rawfactor = (t->type == scaledX) ? sXfactor : sYfactor;
+ npf->name = pf->name;
+ npf++;
+ count++;
+ npf->value = doround(rawfactor * (double)pf->value);
+ npf->name = rawFontPropTable[t - fontPropTable].atom;
+ npf++;
+ count++;
+ *isStringProp++ = *wasStringProp;
+ *isStringProp++ = *wasStringProp;
break;
case unscaled:
+ if (count + 1 > maxprops)
+ continue;
npf->value = pf->value;
npf->name = pf->name;
npf++;
@@ -539,18 +552,6 @@ computeProps(FontPropPtr pf, char *wasStringProp,
default:
break;
}
- if (t->type != unscaled)
- {
- npf->name = pf->name;
- npf++;
- count++;
- npf->value = doround(rawfactor * (double)pf->value);
- npf->name = rawFontPropTable[t - fontPropTable].atom;
- npf++;
- count++;
- *isStringProp++ = *wasStringProp;
- *isStringProp++ = *wasStringProp;
- }
}
return count;
}
@@ -667,7 +668,7 @@ ComputeScaledProperties(FontInfoPtr sourceFontInfo, /* the font to be scaled */
n = NPROPS;
n += computeProps(sourceFontInfo->props, sourceFontInfo->isStringProp,
fp, isStringProp, sourceFontInfo->nprops, dx, dy,
- sdx, sdy);
+ sdx, sdy, nProps - NPROPS);
return n;
}
--
2.55.0

View File

@ -1,117 +0,0 @@
From 76a453c43a7fb74f1e6258d452b3d3df51b97af4 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Mon, 13 Jul 2026 15:48:06 +1000
Subject: [PATCH] fserve: validate num_chars against encoding array size in
fs_read_glyphs
FS_QueryXExtents16 causes us to allocate the encoding[] array, later
during the FS_QueryXBitmaps16 reply handling we fill in that array.
There is no verification that the allocation is large enough, a
malicious font server could send us a small numExtents and a
large num_chars to force underallocation and OOB read/rwrite.
A regression test is included that constructs a crafted
FS_QueryXBitmaps16 reply with num_chars > num_encoding and verifies
the library rejects it.
CVE-2026-59679
Found-by: Zhixi "Jace" Sun, independent security researcher
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Part-of: <https://gitlab.freedesktop.org/xorg/lib/libxfont/-/merge_requests/36>
---
Makefile.am | 16 ++++++++++++++++
src/fc/fserve.c | 22 ++++++++++++++++++++++
src/fc/fservestr.h | 1 +
3 files changed, 39 insertions(+)
diff --git a/Makefile.am b/Makefile.am
index c1a3db2..ef9bc40 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -157,6 +157,22 @@ endif
EXTRA_DIST = src/builtins/buildfont
+# Security regression tests
+TESTS =
+check_PROGRAMS =
+
+if XFONT_FC
+TESTS += test-fserve-read-glyphs
+check_PROGRAMS += test-fserve-read-glyphs
+
+# The test #includes fserve.c directly to access static functions so
+# we statically link against libXfont2.a.
+test_fserve_read_glyphs_SOURCES = test/test-fserve-read-glyphs.c
+test_fserve_read_glyphs_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include -I$(top_srcdir)/src/fc
+test_fserve_read_glyphs_LDFLAGS = -static
+test_fserve_read_glyphs_LDADD = libXfont2.la $(LTLIBOBJS)
+endif XFONT_FC
+
MAINTAINERCLEANFILES = ChangeLog INSTALL
.PHONY: ChangeLog INSTALL
diff --git a/src/fc/fserve.c b/src/fc/fserve.c
index 708fc35..4141c3c 100644
--- a/src/fc/fserve.c
+++ b/src/fc/fserve.c
@@ -1097,6 +1097,7 @@ fs_read_extent_info(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
return AllocError;
}
fsfont->encoding = pCI;
+ fsfont->num_encoding = numExtents;
if (haveInk)
fsfont->inkMetrics = pCI + numExtents;
else
@@ -2004,6 +2005,17 @@ fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
{
minchar = 0;
maxchar = rep->num_chars;
+
+ /* Reject replies where num_chars exceeds the encoding array
+ size allocated in fs_read_extent_info() to prevent
+ out-of-bounds access on encoding[]. */
+ if (rep->num_chars > (CARD32)fsdata->num_encoding)
+ {
+ ErrorF("fserve: num_chars (%u) > num_encoding (%d)\n",
+ (unsigned) rep->num_chars, fsdata->num_encoding);
+ err = AllocError;
+ goto bail;
+ }
}
off_adr = (char *)ppbits;
@@ -2025,6 +2037,16 @@ fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
for (i = 0; i < rep->num_chars; i++)
{
memcpy(&local_off, off_adr, SIZEOF(fsOffset32)); /* align it */
+ /* Bounds-check minchar against the encoding array size to
+ prevent out-of-bounds access from a malicious font server
+ reply with more num_chars than num_extents. */
+ if (minchar >= (unsigned long)fsdata->num_encoding)
+ {
+ ErrorF("fserve: glyph index %lu >= num_encoding (%d)\n",
+ minchar, fsdata->num_encoding);
+ err = AllocError;
+ goto bail;
+ }
if (blockrec->type == FS_OPEN_FONT ||
fsdata->encoding[minchar].bits == &_fs_glyph_requested)
{
diff --git a/src/fc/fservestr.h b/src/fc/fservestr.h
index 29ae46e..da95e41 100644
--- a/src/fc/fservestr.h
+++ b/src/fc/fservestr.h
@@ -43,6 +43,7 @@ typedef struct _fs_glyph {
typedef struct _fs_font {
CharInfoPtr pDefault;
CharInfoPtr encoding;
+ int num_encoding;
CharInfoPtr inkMetrics;
FSGlyphPtr glyphs;
} FSFontRec, *FSFontPtr;
--
2.39.5

View File

@ -1,13 +1,13 @@
Summary: X.Org X11 libXfont2 runtime library Summary: X.Org X11 libXfont2 runtime library
Name: libXfont2 Name: libXfont2
Version: 2.0.3 Version: 2.0.3
Release: 2%{?dist}.3 Release: 12%{?dist}
License: MIT License: MIT
Group: System Environment/Libraries
URL: http://www.x.org URL: http://www.x.org
Source0: http://www.x.org/pub/individual/lib/%{name}-%{version}.tar.bz2 Source0: http://www.x.org/pub/individual/lib/%{name}-%{version}.tar.bz2
BuildRequires: make
BuildRequires: autoconf automake libtool BuildRequires: autoconf automake libtool
BuildRequires: pkgconfig(fontsproto) BuildRequires: pkgconfig(fontsproto)
BuildRequires: xorg-x11-util-macros BuildRequires: xorg-x11-util-macros
@ -15,21 +15,11 @@ BuildRequires: xorg-x11-xtrans-devel >= 1.0.3-3
BuildRequires: libfontenc-devel BuildRequires: libfontenc-devel
BuildRequires: freetype-devel BuildRequires: freetype-devel
Patch1: 0001-bitscale-fix-integer-overflow-in-BitmapScaleBitmaps-.patch
Patch2: 0002-pcfread-validate-bitmap-sizes-and-offsets-against-pe.patch
Patch3: 0003-bitscale-add-bounds-check-to-computeProps-for-proper.patch
# https://gitlab.freedesktop.org/xorg/lib/libxfont/-/commit/c2d222bb22c623d8a40f3275077fc7e6617f2c8a
Patch4: libXfont2-2.0.3-CVE-2026-44950.patch
# https://gitlab.freedesktop.org/xorg/lib/libxfont/-/commit/668fea81f40bcb48ec67fb55d0b851049d265290
Patch5: 0004-fserve-validate-num_chars-against-encoding-array-siz.patch
%description %description
X.Org X11 libXfont2 runtime library X.Org X11 libXfont2 runtime library
%package devel %package devel
Summary: X.Org X11 libXfont2 development package Summary: X.Org X11 libXfont2 development package
Group: Development/Libraries
Requires: %{name}%{?_isa} = %{version}-%{release} Requires: %{name}%{?_isa} = %{version}-%{release}
Requires: libfontenc-devel%{?_isa} Requires: libfontenc-devel%{?_isa}
@ -37,7 +27,7 @@ Requires: libfontenc-devel%{?_isa}
X.Org X11 libXfont development package X.Org X11 libXfont development package
%prep %prep
%autosetup -p1 %autosetup
%build %build
autoreconf -v --install --force autoreconf -v --install --force
@ -65,19 +55,36 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
%{_libdir}/pkgconfig/xfont2.pc %{_libdir}/pkgconfig/xfont2.pc
%changelog %changelog
* Thu Aug 06 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.0.3-2.3 * Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 2.0.3-12
- CVE fix for: CVE-2026-59679 - Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
Resolves: https://redhat.atlassian.net/browse/RHEL-221956 Related: rhbz#1991688
* Thu Aug 06 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.0.3-2.2 * Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 2.0.3-11
- CVE fix for: CVE-2026-44950 - Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
Resolves: https://redhat.atlassian.net/browse/RHEL-222015
* Wed Jul 08 2026 Olivier Fourdan <ofourdan@redhat.com> - 2.0.3-2.1 * Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.3-10
- CVE fix for: CVE-2026-56001, CVE-2026-56002, CVE-2026-56003 - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
Resolves: https://redhat.atlassian.net/browse/RHEL-191877
Resolves: https://redhat.atlassian.net/browse/RHEL-191928 * Thu Nov 5 11:25:30 AEST 2020 Peter Hutterer <peter.hutterer@redhat.com> - 2.0.3-9
Resolves: https://redhat.atlassian.net/browse/RHEL-191948 - Add BuildRequires for make
* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.3-8
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.3-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.3-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Thu Mar 21 2019 Adam Jackson <ajax@redhat.com> - 2.0.3-5
- Rebuild for xtrans 1.4.0
* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.3-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.3-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Fri Jun 29 2018 Adam Jackson <ajax@redhat.com> - 2.0.3-2 * Fri Jun 29 2018 Adam Jackson <ajax@redhat.com> - 2.0.3-2
- Use ldconfig scriptlet macros - Use ldconfig scriptlet macros

View File

@ -1,6 +0,0 @@
--- !Policy
product_versions:
- rhel-8
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: desktop-qe.desktop-ci.tier1-gating.functional}

View File

@ -1,511 +0,0 @@
From fa38750d94ec161fc57d3f53d44a00bb1dfc3d89 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Mon, 13 Jul 2026 15:50:09 +1000
Subject: [PATCH] fserve: bounds-check cumulative glyph data writes in
fs_read_glyphs
fs_read_glyphs() copies each glyph's bitmap into a single allbits
buffer allocated to rep->nbytes bytes. The per-glyph guard validates
only that the source slice (position, length) lies within the pbitmaps
source buffer. It does not check whether the running destination cursor
has exceeded the allocation.
A malicious font server can send overlapping source offsets (e.g. 1000
glyphs each referencing {position:0, length:64} with nbytes=64). Each
individual source range passes validation, but the cumulative writes
total 64000 bytes into a 64-byte destination buffer.
Interestingly there was an unconditional debug printf in place that
sort-of warned about this but didn't prevent this. Let's remove that and
instead use the actual check to bail out before we run OOB.
A regression test is included that sends 100 glyphs each referencing
the same 64-byte source slice into a 64-byte destination buffer, and
verifies the library rejects the overflow.
CVE-2026-44950
Found-by: Zhixi "Jace" Sun, independent security researcher
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Part-of: <https://gitlab.freedesktop.org/xorg/lib/libxfont/-/merge_requests/36>
---
src/fc/fserve.c | 23 +-
test/test-fserve-read-glyphs.c | 412 +++++++++++++++++++++++++++++++++
2 files changed, 426 insertions(+), 9 deletions(-)
create mode 100644 test/test-fserve-read-glyphs.c
diff --git a/src/fc/fserve.c b/src/fc/fserve.c
index 708fc35..f17cffa 100644
--- a/src/fc/fserve.c
+++ b/src/fc/fserve.c
@@ -1923,10 +1923,7 @@ fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
fsOffset32 local_off;
char *off_adr;
pointer pbitmaps;
- char *bits, *allbits;
-#ifdef DEBUG
- char *origallbits;
-#endif
+ char *bits, *allbits, *origallbits;
int i,
err;
int nranges = 0;
@@ -2016,8 +2013,8 @@ fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
goto bail;
}
-#ifdef DEBUG
origallbits = allbits;
+#ifdef DEBUG
fprintf (stderr, "Reading %d glyphs in %d bytes for %s\n",
(int) rep->num_chars, (int) rep->nbytes, fsd->name);
#endif
@@ -2038,6 +2035,18 @@ fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
(local_off.position < rep->nbytes) &&
(local_off.length <= (rep->nbytes - local_off.position)))
{
+ /* Check that the destination buffer has enough room
+ for this glyph to prevent a heap overflow from
+ overlapping source offsets. */
+ if (local_off.length >
+ rep->nbytes - (allbits - origallbits))
+ {
+ ErrorF("fserve: glyph data overflow: "
+ "cumulative write exceeds nbytes (%u)\n",
+ (unsigned) rep->nbytes);
+ err = AllocError;
+ goto bail;
+ }
bits = allbits;
allbits += local_off.length;
memcpy(bits, (char *)pbitmaps + local_off.position,
@@ -2065,10 +2074,6 @@ fs_read_glyphs(FontPathElementPtr fpe, FSBlockDataPtr blockrec)
}
off_adr += SIZEOF(fsOffset32);
}
-#ifdef DEBUG
- fprintf (stderr, "Used %d bytes instead of %d\n",
- (int) (allbits - origallbits), (int) rep->nbytes);
-#endif
if (blockrec->type == FS_OPEN_FONT)
{
diff --git a/test/test-fserve-read-glyphs.c b/test/test-fserve-read-glyphs.c
new file mode 100644
index 0000000..21c3410
--- /dev/null
+++ b/test/test-fserve-read-glyphs.c
@@ -0,0 +1,412 @@
+/*
+ * Security regression tests for fs_read_glyphs() in src/fc/fserve.c.
+ *
+ * Approach: include fserve.c directly to access the static fs_read_glyphs()
+ * function. Pre-fill the FSFpeRec.inBuf with a crafted protocol reply so
+ * fs_get_reply() returns it without any network I/O.
+ *
+ * Copyright (c) 2026, Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Include fserve.c directly to access the static fs_read_glyphs().
+ * All non-static symbols from fserve.c are hidden in libXfont2.so
+ * (via the linker version script), so there are no duplicate symbol
+ * conflicts when linking against the library.
+ */
+#include "src/fc/fserve.c"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Set up an FSFpeRec with its inBuf pre-filled with the given data.
+ * fs_get_reply() will return this data without attempting any network I/O
+ * because fs_inqueued(conn) >= size.
+ */
+static void
+setup_conn(FSFpeRec *conn, const void *reply_data, long reply_size)
+{
+ memset(conn, 0, sizeof(*conn));
+
+ /*
+ * fs_get_reply() checks: conn->fs_fd != -1 && conn->fs_listening
+ * Use a dup'd fd so it's valid but harmless.
+ */
+ conn->fs_fd = dup(STDERR_FILENO);
+ conn->fs_listening = TRUE;
+
+ /* Pre-fill the input buffer with our crafted reply */
+ conn->inBuf.buf = malloc(reply_size);
+ if (!conn->inBuf.buf) {
+ fprintf(stderr, "FAIL: malloc for inBuf\n");
+ exit(1);
+ }
+ memcpy(conn->inBuf.buf, reply_data, reply_size);
+ conn->inBuf.size = reply_size;
+ conn->inBuf.insert = reply_size;
+ conn->inBuf.remove = 0;
+ conn->inNeed = 0;
+
+ /* Allocate a minimal output buffer to keep _fs_flush happy */
+ conn->outBuf.buf = calloc(1, FS_BUF_INC);
+ conn->outBuf.size = FS_BUF_INC;
+ conn->outBuf.insert = 0;
+ conn->outBuf.remove = 0;
+}
+
+static void
+cleanup_conn(FSFpeRec *conn)
+{
+ if (conn->fs_fd >= 0)
+ close(conn->fs_fd);
+ free(conn->inBuf.buf);
+ free(conn->outBuf.buf);
+}
+
+/*
+ * Set up the minimum font state needed by fs_read_glyphs():
+ * - FontPathElementRec (fpe) with fpe->private = conn
+ * - FontRec (pfont) with info, fontPrivate, fpePrivate
+ * - FSFontRec (fsfont) with encoding[] array
+ * - FSFontDataRec (fsd)
+ * - FSBlockDataRec (blockrec) of type FS_OPEN_FONT
+ * - FSBlockedFontRec (bfont) embedded in blockrec->data
+ */
+struct test_font_state {
+ FontPathElementRec fpe;
+ FontRec pfont;
+ FSFontRec fsfont;
+ FSFontDataRec fsd;
+ FSBlockDataRec blockrec;
+ FSBlockedFontRec bfont;
+ CharInfoPtr encoding;
+};
+
+static void
+setup_font_state(struct test_font_state *s, FSFpeRec *conn,
+ int num_encoding)
+{
+ int i;
+
+ memset(s, 0, sizeof(*s));
+
+ /* Font path element */
+ s->fpe.name = (char *)"test-fserve";
+ s->fpe.name_length = strlen(s->fpe.name);
+ s->fpe.private = conn;
+
+ /* Font data (fpePrivate) */
+ s->fsd.name = (char *)"test-font";
+ s->fsd.namelen = strlen(s->fsd.name);
+ s->fsd.glyphs_to_get = 0;
+
+ /* Encoding array -- this is what num_extents sized */
+ s->encoding = calloc(num_encoding, sizeof(CharInfoRec));
+ if (!s->encoding) {
+ fprintf(stderr, "FAIL: calloc encoding\n");
+ exit(1);
+ }
+ /* Mark all glyphs as having nonzero metrics and undefined bits
+ * so fs_read_glyphs will try to process them */
+ for (i = 0; i < num_encoding; i++) {
+ s->encoding[i].metrics.ascent = 10;
+ s->encoding[i].metrics.descent = 2;
+ s->encoding[i].metrics.characterWidth = 8;
+ s->encoding[i].metrics.leftSideBearing = 0;
+ s->encoding[i].metrics.rightSideBearing = 8;
+ s->encoding[i].bits = &_fs_glyph_undefined;
+ }
+
+ /* FSFontRec */
+ s->fsfont.encoding = s->encoding;
+ s->fsfont.num_encoding = num_encoding;
+ s->fsfont.pDefault = NULL;
+ s->fsfont.inkMetrics = s->encoding;
+ s->fsfont.glyphs = NULL;
+
+ /* FontRec */
+ s->pfont.fontPrivate = &s->fsfont;
+ s->pfont.fpePrivate = &s->fsd;
+ s->pfont.fpe = &s->fpe;
+ s->pfont.info.firstRow = 0;
+ s->pfont.info.lastRow = 0;
+ s->pfont.info.firstCol = 0;
+ s->pfont.info.lastCol = num_encoding > 0 ? num_encoding - 1 : 0;
+ s->pfont.info.maxbounds.ascent = 20;
+ s->pfont.info.maxbounds.descent = 10;
+ s->pfont.info.maxbounds.characterWidth = 20;
+
+ /* Block record -- simulating FS_OPEN_FONT path */
+ s->bfont.pfont = &s->pfont;
+ s->bfont.flags = FontLoadBitmaps;
+ s->bfont.state = FS_GLYPHS_REPLY;
+ s->bfont.freeFont = FALSE;
+
+ s->blockrec.type = FS_OPEN_FONT;
+ s->blockrec.data = (pointer)&s->bfont;
+ s->blockrec.client = NULL;
+ s->blockrec.sequenceNumber = 0;
+ s->blockrec.errcode = 0;
+ s->blockrec.depending = NULL;
+ s->blockrec.next = NULL;
+}
+
+static void
+cleanup_font_state(struct test_font_state *s)
+{
+ FSGlyphPtr g, next;
+
+ /* Free any glyph allocations made by fs_alloc_glyphs */
+ for (g = s->fsfont.glyphs; g; g = next) {
+ next = g->next;
+ free(g);
+ }
+ free(s->encoding);
+}
+
+/*
+ * Build a crafted fsQueryXBitmaps16Reply in a buffer.
+ * Returns the total buffer size. Caller must free *out_buf.
+ *
+ * The reply contains:
+ * - fsQueryXBitmaps16Reply header
+ * - num_chars fsOffset32 entries
+ * - nbytes of bitmap data
+ */
+static long
+build_reply(char **out_buf,
+ CARD32 num_chars, CARD32 nbytes,
+ CARD32 off_position, CARD32 off_length)
+{
+ long hdr_size = SIZEOF(fsQueryXBitmaps16Reply);
+ long offsets_size = SIZEOF(fsOffset32) * num_chars;
+ /* Bitmap data area: at least nbytes, but we need off_position + off_length
+ * to be valid source, so ensure bitmap area is large enough */
+ long bitmap_size = nbytes;
+ long total = hdr_size + offsets_size + bitmap_size;
+ long total_padded = (total + 3) & ~3; /* pad to 4 bytes */
+ char *buf;
+ fsQueryXBitmaps16Reply *rep;
+ fsOffset32 off;
+ long i;
+
+ buf = calloc(1, total_padded);
+ if (!buf) {
+ fprintf(stderr, "FAIL: calloc reply buffer\n");
+ exit(1);
+ }
+
+ /* Fill header */
+ rep = (fsQueryXBitmaps16Reply *)buf;
+ rep->type = FS_Reply; /* normal reply (0), not FS_Error (1) */
+ rep->sequenceNumber = 0;
+ rep->length = total_padded >> 2; /* length in 32-bit words */
+ rep->replies_hint = 0;
+ rep->num_chars = num_chars;
+ rep->nbytes = nbytes;
+
+ /* Fill offset entries -- all pointing to the same source range */
+ off.position = off_position;
+ off.length = off_length;
+ for (i = 0; i < (long)num_chars; i++) {
+ memcpy(buf + hdr_size + i * SIZEOF(fsOffset32),
+ &off, SIZEOF(fsOffset32));
+ }
+
+ /* Fill bitmap data with recognizable pattern */
+ memset(buf + hdr_size + offsets_size, 0xAA, bitmap_size);
+
+ *out_buf = buf;
+ return total_padded;
+}
+
+/*
+ * Test 1: num_chars > num_encoding
+ *
+ * Allocate encoding[] with 2 entries, but send a reply with
+ * num_chars = 100. Without the fix, this would read/write
+ * encoding[2..99] out of bounds.
+ */
+static int
+test_num_chars_exceeds_encoding(void)
+{
+ FSFpeRec conn;
+ struct test_font_state state;
+ char *reply_buf;
+ long reply_size;
+ int result;
+ int num_encoding = 2;
+ CARD32 num_chars = 100;
+ CARD32 nbytes = num_chars * 16; /* enough bitmap data */
+
+ /* Build a reply with num_chars=100 but valid source data */
+ reply_size = build_reply(&reply_buf, num_chars, nbytes, 0, 16);
+ setup_conn(&conn, reply_buf, reply_size);
+ setup_font_state(&state, &conn, num_encoding);
+
+ result = fs_read_glyphs(&state.fpe, &state.blockrec);
+
+ cleanup_font_state(&state);
+ cleanup_conn(&conn);
+ free(reply_buf);
+
+ if (result != Successful) {
+ printf("ok 1 - num_chars (%u) > num_encoding (%d) rejected\n",
+ (unsigned)num_chars, num_encoding);
+ return 0;
+ } else {
+ printf("not ok 1 - num_chars (%u) > num_encoding (%d) "
+ "should have been rejected\n",
+ (unsigned)num_chars, num_encoding);
+ return 1;
+ }
+}
+
+/*
+ * Test 2: cumulative glyph data overflow
+ *
+ * Allocate allbits with nbytes=64, but send 100 glyphs each
+ * with offset {position:0, length:64}. Each individual source
+ * range is valid, but the cumulative writes total 6400 bytes
+ * into a 64-byte buffer.
+ */
+static int
+test_cumulative_allbits_overflow(void)
+{
+ FSFpeRec conn;
+ struct test_font_state state;
+ char *reply_buf;
+ long reply_size;
+ int result;
+ int num_encoding = 100; /* match num_chars so encoding[] is fine */
+ CARD32 num_chars = 100;
+ CARD32 nbytes = 64; /* tiny destination buffer */
+
+ /* All offsets point to {position:0, length:64} -- each source
+ * range is valid but they overlap, causing 100*64=6400 bytes
+ * to be written to a 64-byte buffer */
+ reply_size = build_reply(&reply_buf, num_chars, nbytes, 0, 64);
+ setup_conn(&conn, reply_buf, reply_size);
+ setup_font_state(&state, &conn, num_encoding);
+
+ result = fs_read_glyphs(&state.fpe, &state.blockrec);
+
+ cleanup_font_state(&state);
+ cleanup_conn(&conn);
+ free(reply_buf);
+
+ if (result != Successful) {
+ printf("ok 2 - cumulative allbits overflow (100 * 64 into 64) rejected\n");
+ return 0;
+ } else {
+ printf("not ok 2 - cumulative allbits overflow (100 * 64 into 64) "
+ "should have been rejected\n");
+ return 1;
+ }
+}
+
+/*
+ * Test 3: legitimate reply should still be accepted
+ *
+ * num_chars == num_encoding, each glyph has unique non-overlapping
+ * offsets, and total data fits in nbytes.
+ */
+static int
+test_legitimate_reply(void)
+{
+ FSFpeRec conn;
+ struct test_font_state state;
+ char *reply_buf;
+ long hdr_size = SIZEOF(fsQueryXBitmaps16Reply);
+ long offsets_size;
+ int result;
+ int num_encoding = 4;
+ CARD32 num_chars = 4;
+ CARD32 glyph_size = 16;
+ CARD32 nbytes = num_chars * glyph_size;
+ long total, total_padded;
+ fsQueryXBitmaps16Reply *rep;
+ fsOffset32 off;
+ int i;
+
+ offsets_size = SIZEOF(fsOffset32) * num_chars;
+ total = hdr_size + offsets_size + nbytes;
+ total_padded = (total + 3) & ~3;
+
+ reply_buf = calloc(1, total_padded);
+ if (!reply_buf) {
+ fprintf(stderr, "FAIL: calloc\n");
+ return 1;
+ }
+
+ rep = (fsQueryXBitmaps16Reply *)reply_buf;
+ rep->type = FS_Reply;
+ rep->sequenceNumber = 0;
+ rep->length = total_padded >> 2;
+ rep->replies_hint = 0;
+ rep->num_chars = num_chars;
+ rep->nbytes = nbytes;
+
+ /* Each glyph gets its own non-overlapping slice */
+ for (i = 0; i < (int)num_chars; i++) {
+ off.position = i * glyph_size;
+ off.length = glyph_size;
+ memcpy(reply_buf + hdr_size + i * SIZEOF(fsOffset32),
+ &off, SIZEOF(fsOffset32));
+ }
+ memset(reply_buf + hdr_size + offsets_size, 0xBB, nbytes);
+
+ setup_conn(&conn, reply_buf, total_padded);
+ setup_font_state(&state, &conn, num_encoding);
+
+ result = fs_read_glyphs(&state.fpe, &state.blockrec);
+
+ cleanup_font_state(&state);
+ cleanup_conn(&conn);
+ free(reply_buf);
+
+ if (result == Successful) {
+ printf("ok 3 - legitimate reply (4 glyphs, non-overlapping) accepted\n");
+ return 0;
+ } else {
+ printf("not ok 3 - legitimate reply (4 glyphs, non-overlapping) "
+ "rejected with error %d\n", result);
+ return 1;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int failures = 0;
+
+ printf("1..3\n");
+
+ failures += test_num_chars_exceeds_encoding();
+ failures += test_cumulative_allbits_overflow();
+ failures += test_legitimate_reply();
+
+ return failures ? 1 : 0;
+}

View File

@ -1 +0,0 @@
SHA512 (libXfont2-2.0.3.tar.bz2) = 648b664e2aa58cbc7366a1b05873aa06bd4a38060f64085783043388244af8ceced77b29a22c3ac8b6d34cd226e093bbbcc785ea1748ea65720fe7ea05b4b44b