Hi Paul,

At 2026-07-23T19:15:19-0700, Paul Eggert wrote:
> On 2026-07-23 17:25, G. Branden Robinson wrote:
> > The good news is that this seems_not_ to be a release blocker for
> > groff.  If the problem ain't in groff, I can't fix it in groff.
> 
> What Collin said.
> 
> For what it's worth, when I tried to debug this on Fedora 44 I found
> that I couldn't build groff head (commit
> d6675ad14d71b4b36b2274b889cd1e2594bfc79c), because groff's build
> procedure assumes that because stdckdint.h works with C it also works
> with C++, which is not true on Fedora 44.

Rut-roh.

> I worked around that problem by syncing with current Gnulib (which
> fixes this problem), and I assume you'll do something similar before a
> groff release.

I'm using stable/2026-07.  That's not fresh enough?

[more below this long quote]

> Also for what it's worth I built with this shell command:
> 
> ASAN_OPTIONS=detect_leaks=0 make CXXFLAGS='-g3 -O2 -fsanitize=address'
> CFLAGS='-g3 -O2 -fsanitize=address'
> 
> and when that failed, with this shell command:
> 
> ASAN_OPTIONS=detect_leaks=0,alloc_dealloc_mismatch=0 make CXXFLAGS='-g3 -O2
> -fsanitize=address' CFLAGS='-g3 -O2 -fsanitize=address'
> 
> and got the following failures, the first with the first command and the
> second with the second:
> 
> ...
>   GROFF    doc/groff-man-pages.pdf
> =================================================================
> ==484606==ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs
> operator delete []) on 0x7b6448be0560
>     #0 0x7f044a0f104f in operator delete[](void*)
> (/lib64/libasan.so.8+0xf104f) (BuildId:
> 5395ec74f54d9ec7bf97c06583dd39a96c230822)
>     #1 0x00000043027d in block_entry::~block_entry()
> src/preproc/tbl/table.cpp:654
>     #2 0x00000043027d in left_block_entry::~left_block_entry()
> src/preproc/tbl/table.cpp:324
>     #3 0x00000043027d in left_block_entry::~left_block_entry()
> src/preproc/tbl/table.cpp:324
>     #4 0x000000419d1b in table::~table() src/preproc/tbl/table.cpp:1293
>     #5 0x00000040e249 in process_table(table_input&)
> src/preproc/tbl/main.cpp:1618
>     #6 0x00000040ec10 in process_input_file(_IO_FILE*)
> src/preproc/tbl/main.cpp:256
>     #7 0x000000402237 in main src/preproc/tbl/main.cpp:1710
>     #8 0x7f0449a0a680 in __libc_start_call_main (/lib64/libc.so.6+0x3680)
> (BuildId: 17f2e1fd905f485786f6fd6e3bede4ad737137e7)
>     #9 0x7f0449a0a797 in __libc_start_main@GLIBC_2.2.5
> (/lib64/libc.so.6+0x3797) (BuildId:
> 17f2e1fd905f485786f6fd6e3bede4ad737137e7)
>     #10 0x000000402af4 in _start (/home/eggert/src/gnu/groff/tbl+0x402af4)
> (BuildId: c478463b4fa0a8213180485cd5ed9b4c8f3f7067)
> 
> 0x7b6448be0560 is located 0 bytes inside of 62-byte region
> [0x7b6448be0560,0x7b6448be059e)
> allocated by thread T0 here:
>     #0 0x7f044a0ef41f in malloc (/lib64/libasan.so.8+0xef41f) (BuildId:
> 5395ec74f54d9ec7bf97c06583dd39a96c230822)
>     #1 0x000000436cc9 in string::extract() const
> src/libs/libgroff/string.cpp:308
> 
> SUMMARY: AddressSanitizer: alloc-dealloc-mismatch
> src/preproc/tbl/table.cpp:654 in block_entry::~block_entry()
> ==484606==HINT: if you don't care about these errors you may set
> ASAN_OPTIONS=alloc_dealloc_mismatch=0
> ==484606==ABORTING
> troff: warning: name '3rele' not defined
> troff: error: automatically ending diversion '3section' on exit
> pdfmom: fatal error: groff exited with status 4
> 
> 
> ...
> fonts_n.roff: listing fonts in ./font/devps
> =================================================================
> ==484862==ERROR: AddressSanitizer: heap-use-after-free on address
> 0x7c88d65e52c0 at pc 0x7f78d7aaf861 bp 0x7fffdd9e1100 sp 0x7fffdd9e08c0
> READ of size 1 at 0x7c88d65e52c0 thread T0
>     #0 0x7f78d7aaf860 in strcmp.part.0 (/lib64/libasan.so.8+0xaf860)
> (BuildId: 5395ec74f54d9ec7bf97c06583dd39a96c230822)
>     #1 0x000000455a7f in file_iterator::get_location(bool, char const**,
> int*) src/roff/troff/input.cpp:499
>     #2 0x000000455a7f in input_stack::get_location(bool, char const**, int*)
> src/roff/troff/input.cpp:800
>     #3 0x000000493830 in macro::macro() src/roff/troff/input.cpp:3509
>     #4 0x000000493830 in do_define_string(define_mode, comp_mode)
> src/roff/troff/input.cpp:4478
>     #5 0x000000495133 in request::invoke(symbol, bool)
> src/roff/troff/input.cpp:3334
>     #6 0x000000495133 in interpolate_macro src/roff/troff/input.cpp:4072
>     #7 0x0000004a09f2 in process_input_stack() src/roff/troff/input.cpp:3100
>     #8 0x0000004a2c4d in process_input_file src/roff/troff/input.cpp:8801
>     #9 0x00000040a271 in main src/roff/troff/input.cpp:9153
>     #10 0x7f78d740a680 in __libc_start_call_main (/lib64/libc.so.6+0x3680)
> (BuildId: 17f2e1fd905f485786f6fd6e3bede4ad737137e7)
>     #11 0x7f78d740a797 in __libc_start_main@GLIBC_2.2.5
> (/lib64/libc.so.6+0x3797) (BuildId:
> 17f2e1fd905f485786f6fd6e3bede4ad737137e7)
>     #12 0x00000040be74 in _start (/home/eggert/src/gnu/groff/troff+0x40be74)
> (BuildId: cf528e78ad252e121bde02c92df44d512e25e23d)
> 
> 0x7c88d65e52c0 is located 0 bytes inside of 256-byte region
> [0x7c88d65e52c0,0x7c88d65e53c0)
> freed by thread T0 here:
>     #0 0x7f78d7af104f in operator delete[](void*)
> (/lib64/libasan.so.8+0xf104f) (BuildId:
> 5395ec74f54d9ec7bf97c06583dd39a96c230822)
>     #1 0x00000048c64c in pipe_source_request() src/roff/troff/input.cpp:6661
> 
> previously allocated by thread T0 here:
>     #0 0x7f78d7af065f in operator new[](unsigned long)
> (/lib64/libasan.so.8+0xf065f) (BuildId:
> 5395ec74f54d9ec7bf97c06583dd39a96c230822)
>     #1 0x00000046a477 in read_string() src/roff/troff/input.cpp:8423
> 
> SUMMARY: AddressSanitizer: heap-use-after-free src/roff/troff/input.cpp:499
> in file_iterator::get_location(bool, char const**, int*)
> Shadow bytes around the buggy address:
>   0x7c88d65e5000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
>   0x7c88d65e5080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x7c88d65e5100: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
>   0x7c88d65e5180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x7c88d65e5200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> =>0x7c88d65e5280: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
>   0x7c88d65e5300: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
>   0x7c88d65e5380: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
>   0x7c88d65e5400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x7c88d65e5480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>   0x7c88d65e5500: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
> Shadow byte legend (one shadow byte represents 8 application bytes):
>   Addressable:           00
>   Partially addressable: 01 02 03 04 05 06 07
>   Heap left redzone:       fa
>   Freed heap region:       fd
>   Stack left redzone:      f1
>   Stack mid redzone:       f2
>   Stack right redzone:     f3
>   Stack after return:      f5
>   Stack use after scope:   f8
>   Global redzone:          f9
>   Global init order:       f6
>   Poisoned by user:        f7
>   Container overflow:      fc
>   Array cookie:            ac
>   Intra object redzone:    bb
>   ASan internal:           fe
>   Left alloca redzone:     ca
>   Right alloca redzone:    cb
> ==484862==ABORTING
> make[2]: *** [Makefile:17135: contrib/hdtbl/examples/fonts_n.ps] Error 4
> make[2]: *** Deleting file 'contrib/hdtbl/examples/fonts_n.ps'
> make[2]: Leaving directory '/home/eggert/src/gnu/groff'
> make[1]: *** [Makefile:12162: all-recursive] Error 1
> make[1]: Leaving directory '/home/eggert/src/gnu/groff'
> make: *** [Makefile:6996: all] Error 2

Thanks.  Unfortunately, making groff ASAN-quiet is not a scheduled goal.

As I put it elsewhere earlier today:

"I see lots of memory leaks.  Not a surprise at all--most of the C/C++
parts of [the] code base are up to 37 years old.  Resolving sloppy
memory management is part of a technical debt clearance war that is
happening on many fronts.  References available on request."

https://savannah.gnu.org/bugs/?68544#comment2

If someone has a case for gating the release on either of Paul's ASAN
problems, I'd like to hear it.

It's possible the use-after-free report qualifies as gating, but the
alloc_dealloc_mismatch doesn't.  I concede the possibility of the
use-after-free being of recent vintage, as I've had my fingers in
`pipe_source_request()` and `read_string()` in this development cycle,
but not in tbl's `block_entry` destructor.  On the other hand, I
recently refactored groff's `string` class _aggressively_, so it's
possible I exposed a latent bug here.

Thoughts?

Regards,
Branden

Attachment: signature.asc
Description: PGP signature

  • ... G. Branden Robinson
    • ... Collin Funk
    • ... Paul Eggert
      • ... G. Branden Robinson
        • ... Paul Eggert
        • ... Bruno Haible via discussion of the GNU roff typesetting system and related software
    • ... Bruno Haible via discussion of the GNU roff typesetting system and related software
      • ... G. Branden Robinson

Reply via email to