#1124341 gbdfed: Segfault opening font with large codepoint due to missing bounds check

Package:
gbdfed
Source:
gbdfed
Description:
X11 font editor
Submitter:
J. Neuschäfer
Date:
2025-12-30 21:13:03 UTC
Severity:
normal
Tags:
#1124341#5
Date:
2025-12-30 21:11:11 UTC
From:
To:
Hello,

I experienced a segfault in gbdfed while opening a font that contains a
character with a large codepoint ("ENCODING"). I have experimentally determined
that this happens with values greater/equal 150'000.

Here's an example file (crash.bdf):

8<---------------------------------
STARTFONT 2.1
FONT a
SIZE 11 75 75
FONTBOUNDINGBOX 5 8 0 -1
STARTPROPERTIES 0
ENDPROPERTIES
CHARS 1

STARTCHAR thing
ENCODING 150000
SWIDTH 436 0
DWIDTH 5 0
BBX 5 8 0 -1
BITMAP
00
00
00
00
00
00
ENDCHAR
8<---------------------------------

Here's an abridged GDB session:

$ gdb --args gbdfed crash.bdf
Reading symbols from gbdfed...
Downloading separate debug info for /usr/bin/gbdfed
Reading symbols from /home/jn/.cache/debuginfod_client/003746153518d9047e8fe77773982444a2470f5f/debuginfo...
(gdb) dir gbdfed-1.6/
Source directories searched: /home/jn/dev/cccac/rgb/busdisplay/crash/gbdfed-1.6:$cdir:$cwd
(gdb) r
Starting program: /usr/bin/gbdfed crash.bdf
Downloading 47.93 K separate debug info for system-supplied DSO at 0x7ffff7fc1000
...
[New Thread 0x7ffff628f6c0 (LWP 4173232)]
[New Thread 0x7ffff5a8e6c0 (LWP 4173233)]
[New Thread 0x7ffff528d6c0 (LWP 4173234)]

Thread 1 "gbdfed" received signal SIGSEGV, Segmentation fault.
0x0000555555564452 in _bdf_parse_glyphs (line=0x7ffffffeb815 "ENCODING", linelen=<optimized out>, lineno=10,
    call_data=<optimized out>, client_data=0x7fffffffb800) at ./bdf.c:1377
⚠️ warning: Source file is more recent than executable.
1377	            if (_bdf_glyph_modified(p->have, p->glyph_enc)) {
(gdb) x/3i $pc
=> 0x555555564452 <_bdf_parse_glyphs+370>:	mov    (%rbx,%rax,4),%edx
   0x555555564455 <_bdf_parse_glyphs+373>:	test   %esi,%edx
   0x555555564457 <_bdf_parse_glyphs+375>:	jne    0x555555564758 <_bdf_parse_glyphs+1144>
(gdb) p p
$1 = (_bdf_parse_t *) 0x7fffffffb800
(gdb) i r rbx rax edx
rbx            0x7fffffffb800      140737488336896
rax            0x1267              4711
edx            0xfffdb610          -150000
(gdb) p *p
$2 = {
  flags = 111,
  cnt = 1,
  row = 0,
  bpr = 0,
  minlb = 32767,
  maxlb = 0,
  maxrb = 0,
  maxas = 0,
  maxds = 0,
  rbearing = 0,
  glyph_name = 0x555555636410 "thing",
  glyph_enc = 150000,
  font = 0x5555556603a0,
  opts = 0x5555555d6284 <options+132>,
  client_data = 0x0,
  callback = 0x0,
  cb = {
    reason = 0,
    current = 0,
    total = 0,
    errlineno = 0
  },
  have = {0 <repeats 2048 times>},
  list = {
    field = 0x55555565ca60,
    size = 10,
    used = 2,
    bfield = 0x0,
    bsize = 0,
    bused = 0
  }
}
(gdb) list
1372	        /*
1373	         * Check to see if this encoding has already been encountered.  If it
1374	         * has then change it to unencoded so it gets added if indicated.
1375	         */
1376	        if (p->glyph_enc >= 0) {
1377	            if (_bdf_glyph_modified(p->have, p->glyph_enc)) {    // <---- crashed here
1378	                /*
1379	                 * Add a message saying a glyph has been moved to the
1380	                 * unencoded area.
1381	                 */
1382	                sprintf(nbuf, ACMSG12, p->glyph_enc, p->glyph_name);
1383	                _bdf_add_acmsg(font, nbuf, strlen(nbuf));
1384	                p->glyph_enc = -1;
1385	                font->modified = 1;
1386	            } else
1387	              _bdf_set_glyph_modified(p->have, p->glyph_enc);
1388	        }
1389
1390	        if (p->glyph_enc >= 0) {
1391	            /*
(gdb)

bdfP.h contains this definition:

  #define _bdf_glyph_modified(map, e) ((map)[(e) >> 5] & (1 << ((e) & 31)))

p->have is defined in bdf.c / typedef _bdf_parse_t as:

  unsigned int have[2048];

which covers 2048 * 32 = 65536 bit elements. Any access beyond that point can't
be safe, but there doesn't appear to be a bounds check. The previously
determined limit of 150'000 probably depends on the memory layout.
&p->have[150000 >> 5] just happens to be just above 0x800000000000 on my
system, which makes it an invalid (non-canonical) address on x86_64.


Best regards,
J. Neuschäfer