From 76a453c43a7fb74f1e6258d452b3d3df51b97af4 Mon Sep 17 00:00:00 2001 From: Peter Hutterer 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 Part-of: --- 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