180 lines
7.1 KiB
Diff
180 lines
7.1 KiB
Diff
From 0f1f4bcbfb1f23b800dfe386782d3a0f05b6756f Mon Sep 17 00:00:00 2001
|
|
From: Peter Hutterer <peter.hutterer@who-t.net>
|
|
Date: Mon, 1 Jun 2026 20:44:34 +1000
|
|
Subject: [PATCH xserver 1/2] fb/mi/glamor: reject glyphs with negative
|
|
dimensions
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
GLYPHWIDTHPIXELS and GLYPHHEIGHTPIXELS compute glyph dimensions from
|
|
signed INT16 fields. A crafted PCF font can produce negative results
|
|
(e.g. rightSideBearing < leftSideBearing).
|
|
|
|
All callees have a while (height--) loop which will end up in OOB writes
|
|
for negative height values.
|
|
|
|
In the case of a negative height, some callees cast to size_t or end up
|
|
with negative strides.
|
|
|
|
The same pattern exists in fbPoloyGlyphBit, miPolyGlyphBlt and
|
|
glamor_poly_glyph_blt_gl, so let's fix all of them in one go.
|
|
|
|
Assisted-by: Claude:claude-opus-4-6
|
|
(cherry picked from commit e31efd3e106e53bfc29d499ff0a34b0d20013f2d)
|
|
|
|
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2252>
|
|
---
|
|
fb/fbglyph.c | 4 ++--
|
|
glamor/glamor_glyphblt.c | 2 +-
|
|
mi/miglblt.c | 2 +-
|
|
3 files changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/fb/fbglyph.c b/fb/fbglyph.c
|
|
index 1bdc2d4fd..5c9318f5e 100644
|
|
--- a/fb/fbglyph.c
|
|
+++ b/fb/fbglyph.c
|
|
@@ -95,7 +95,7 @@ fbPolyGlyphBlt(DrawablePtr pDrawable,
|
|
pglyph = FONTGLYPHBITS(pglyphBase, pci);
|
|
gWidth = GLYPHWIDTHPIXELS(pci);
|
|
gHeight = GLYPHHEIGHTPIXELS(pci);
|
|
- if (gWidth && gHeight) {
|
|
+ if (gWidth > 0 && gHeight > 0) {
|
|
gx = x + pci->metrics.leftSideBearing;
|
|
gy = y - pci->metrics.ascent;
|
|
if (glyph && gWidth <= sizeof(FbStip) * 8 &&
|
|
@@ -197,7 +197,7 @@ fbImageGlyphBlt(DrawablePtr pDrawable,
|
|
pglyph = FONTGLYPHBITS(pglyphBase, pci);
|
|
gWidth = GLYPHWIDTHPIXELS(pci);
|
|
gHeight = GLYPHHEIGHTPIXELS(pci);
|
|
- if (gWidth && gHeight) {
|
|
+ if (gWidth > 0 && gHeight > 0) {
|
|
gx = x + pci->metrics.leftSideBearing;
|
|
gy = y - pci->metrics.ascent;
|
|
if (glyph && gWidth <= sizeof(FbStip) * 8 &&
|
|
diff --git a/glamor/glamor_glyphblt.c b/glamor/glamor_glyphblt.c
|
|
index cada7bae7..d9665f778 100644
|
|
--- a/glamor/glamor_glyphblt.c
|
|
+++ b/glamor/glamor_glyphblt.c
|
|
@@ -90,7 +90,7 @@ glamor_poly_glyph_blt_gl(DrawablePtr drawable, GCPtr gc,
|
|
int h = GLYPHHEIGHTPIXELS(charinfo);
|
|
uint8_t *glyphbits = FONTGLYPHBITS(NULL, charinfo);
|
|
|
|
- if (w && h) {
|
|
+ if (w > 0 && h > 0) {
|
|
int glyph_x = x + charinfo->metrics.leftSideBearing;
|
|
int glyph_y = y - charinfo->metrics.ascent;
|
|
int glyph_stride = GLYPHWIDTHBYTESPADDED(charinfo);
|
|
diff --git a/mi/miglblt.c b/mi/miglblt.c
|
|
index 169d44ffc..8e33e90d8 100644
|
|
--- a/mi/miglblt.c
|
|
+++ b/mi/miglblt.c
|
|
@@ -143,7 +143,7 @@ miPolyGlyphBlt(DrawablePtr pDrawable, GC * pGC, int x, int y, unsigned int nglyp
|
|
pglyph = FONTGLYPHBITS(pglyphBase, pci);
|
|
gWidth = GLYPHWIDTHPIXELS(pci);
|
|
gHeight = GLYPHHEIGHTPIXELS(pci);
|
|
- if (gWidth && gHeight) {
|
|
+ if (gWidth > 0 && gHeight > 0) {
|
|
nbyGlyphWidth = GLYPHWIDTHBYTESPADDED(pci);
|
|
nbyPadGlyph = BitmapBytePad(gWidth);
|
|
|
|
--
|
|
2.54.0
|
|
|
|
From f3df3c9a52b4f480cbcff45cc6569c19de6bfede Mon Sep 17 00:00:00 2001
|
|
From: Peter Hutterer <peter.hutterer@who-t.net>
|
|
Date: Sun, 31 May 2026 19:37:01 +1000
|
|
Subject: [PATCH xserver 2/2] glamor: reject fonts with per-glyph metrics
|
|
exceeding maxbounds
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
glamor_font_get() computes the atlas slot size from the font's declared
|
|
maxbounds, but copies each glyph's bitmap using the per-glyph metrics
|
|
(GLYPHHEIGHTPIXELS/GLYPHWIDTHBYTES macros). When a malicious PCF font
|
|
has per-glyph metrics exceeding maxbounds, the memcpy writes past the
|
|
heap-allocated atlas buffer. Negative per-glyph metrics are even worse:
|
|
the loop counter wraps to ~4 billion iterations (via unsigned cast) or
|
|
memcpy's size parameter wraps to SIZE_MAX.
|
|
|
|
The PCF parser in libXfont2 does not recompute maxbounds from per-glyph
|
|
data (only the BDF parser does), so the file's declared maxbounds values
|
|
are trusted as-is.
|
|
|
|
Reject fonts where any per-glyph metric is negative or exceeds the
|
|
atlas slot size, falling back to software rendering which uses per-glyph
|
|
metrics directly without an atlas.
|
|
|
|
This vulnerability was discovered by:
|
|
Anonymous working with Trend Micro Zero Day Initiative
|
|
|
|
CVE-2026-55999/ZDI-CAN-30498
|
|
|
|
Assisted-by: Claude:claude-opus-4-6
|
|
(cherry picked from commit fbf7bac22e2c6bd627fb042742a23318263edae1)
|
|
|
|
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2252>
|
|
---
|
|
glamor/glamor_font.c | 30 +++++++++++++++++++++++++++---
|
|
1 file changed, 27 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/glamor/glamor_font.c b/glamor/glamor_font.c
|
|
index c689455c9..9c4941b05 100644
|
|
--- a/glamor/glamor_font.c
|
|
+++ b/glamor/glamor_font.c
|
|
@@ -71,6 +71,9 @@ glamor_font_get(ScreenPtr screen, FontPtr font)
|
|
glyph_width_pixels = font->info.maxbounds.rightSideBearing - font->info.minbounds.leftSideBearing;
|
|
glyph_height = font->info.maxbounds.ascent + font->info.maxbounds.descent;
|
|
|
|
+ if (glyph_width_pixels <= 0 || glyph_height <= 0)
|
|
+ return NULL;
|
|
+
|
|
glyph_width_bytes = (glyph_width_pixels + 7) >> 3;
|
|
|
|
glamor_font->glyph_width_pixels = glyph_width_pixels;
|
|
@@ -130,7 +133,28 @@ glamor_font_get(ScreenPtr screen, FontPtr font)
|
|
if (count) {
|
|
char *dst;
|
|
char *src = glyph->bits;
|
|
- unsigned y;
|
|
+ int gw = GLYPHWIDTHBYTES(glyph);
|
|
+ int gh = GLYPHHEIGHTPIXELS(glyph);
|
|
+
|
|
+ /* Reject fonts where any per-glyph metric is negative
|
|
+ * or exceeds the atlas slot size derived from maxbounds.
|
|
+ * The PCF parser in libXfont2 does not recompute
|
|
+ * maxbounds from per-glyph data, so a crafted PCF file
|
|
+ * can violate the maxbounds invariant.
|
|
+ *
|
|
+ * gw is passed as size_t to memcpy and a negative value
|
|
+ * would thus result in OOB access.
|
|
+ *
|
|
+ * Returning NULL makes glamor fall back to software
|
|
+ * rendering.
|
|
+ */
|
|
+ if (gw < 0 || gh < 0 ||
|
|
+ gw > glyph_width_bytes || gh > glyph_height) {
|
|
+ glDeleteTextures(1, &glamor_font->texture_id);
|
|
+ glamor_font->texture_id = 0;
|
|
+ free(bits);
|
|
+ return NULL;
|
|
+ }
|
|
|
|
dst = bits;
|
|
/* get offset of start of first row */
|
|
@@ -139,8 +163,8 @@ glamor_font_get(ScreenPtr screen, FontPtr font)
|
|
dst += (row & 1) ? glamor_font->row_width : 0;
|
|
|
|
dst += col * glyph_width_bytes;
|
|
- for (y = 0; y < GLYPHHEIGHTPIXELS(glyph); y++) {
|
|
- memcpy(dst, src, GLYPHWIDTHBYTES(glyph));
|
|
+ for (int y = 0; y < gh; y++) {
|
|
+ memcpy(dst, src, gw);
|
|
dst += overall_width;
|
|
src += GLYPHWIDTHBYTESPADDED(glyph);
|
|
}
|
|
--
|
|
2.54.0
|
|
|