URL: <https://savannah.gnu.org/bugs/?68570>
Summary: [PATCH] [tbl] SEGV when table format description
consists only of horizontal span descriptors
Group: GNU roff
Submitter: gbranden
Submitted: Mon 27 Jul 2026 01:22:04 AM UTC
Category: Preprocessor tbl
Severity: 4 - Important
Item Group: Crash/Unresponsive
Status: In Progress
Privacy: Public
Assigned to: gbranden
Open/Closed: Open
Discussion Lock: Unlocked
Planned Release: None
_______________________________________________________
Follow-up Comments:
-------------------------------------------------------
Date: Mon 27 Jul 2026 01:22:04 AM UTC By: G. Branden Robinson <gbranden>
This report came to me via private email.
I don't regard it as sensitive.
Hello Branden,
I was fuzzing the groff preprocessors and found a small, reproducible crash
in tbl. It is
a NULL pointer dereference, so a denial of service rather than anything
worse, but I wanted
to send it to you before writing anything up.
When a table's format section marks the first column as horizontally
spanned (s) across all
of its format rows, no table entry ever gets created. tbl correctly prints
the "first column
cannot be horizontally spanned" diagnostic, but then continues into
table::init_output() and
dereferences entry_list, which is still NULL because the table has no
entries.
Reproducer (6 lines), crashes stock /usr/bin/tbl:
.TS
s s
s.
_
x
.TE
$ tbl < crash.tbl >/dev/null
tbl:<standard input>:2: error: first column cannot be horizontally spanned
tbl:<standard input>:3: error: first column cannot be horizontally spanned
Segmentation fault (core dumped)
Reproduced on the groff 1.23.0 shipped in Ubuntu 26.04 (groff-base
1.23.0-10), and the same
code path looks present on current git: table::init_output() still calls
entry_list->set_location() with no NULL check, and I could not find any
entry_list == 0 guard
nearby. There are sibling entry_list->set_location() calls in the file that
look like the same
shape.
This is reachable wherever tbl runs on an untrusted document, for instance
man rendering a
man page that requests table processing, so a crafted page or roff file
crashes tbl. Impact
is limited to the crash.
A guard for the empty-table case would fix it, either skipping the
entry_list->set_location()
calls when entry_list == 0 or returning from init_output() early when the
table has no entries.
I have the reproducer and can share whatever is useful. I will hold off on
any public writeup
until you have looked and a fix is out. Thanks for all your work on groff.
Best regards,
Elias
A few minutes' work produced this patch.
diff --git a/src/preproc/tbl/main.cpp b/src/preproc/tbl/main.cpp
index 82b6c581e..9c878c584 100644
--- a/src/preproc/tbl/main.cpp
+++ b/src/preproc/tbl/main.cpp
@@ -791,6 +791,7 @@ static format *process_format(table_input &in, options
*opt,
input_entry_format *list = 0 /* nullptr */;
bool have_expand = false;
bool is_first_row = true;
+ bool is_first_column = true;
int c = in.get();
for (;;) {
int vrule_count = 0;
@@ -833,8 +834,14 @@ static format *process_format(table_input &in, options
*opt,
break;
case 's':
case 'S':
- got_format = true;
- t = FORMAT_SPAN;
+ if (is_first_column)
+ error("cannot horizontally span in first column;"
+ " ignoring column descriptor '%1'",
+ static_cast<char>(c));
+ else {
+ got_format = true;
+ t = FORMAT_SPAN;
+ }
break;
case '^':
got_format = true;
@@ -872,6 +879,8 @@ static format *process_format(table_input &in, options
*opt,
list = 0 /* nullptr */;
return 0 /* nullptr */;
}
+ if (got_format)
+ is_first_column = false;
if (got_period)
break;
c = in.get();
@@ -1180,6 +1189,7 @@ static format *process_format(table_input &in, options
*opt,
if (c == '\n' || c == ',') {
vrule_count = 0;
is_first_row = false;
+ is_first_column = true;
c = in.get();
list->is_last_column = true;
}
All tests pass, but I'll need a new test of course.
_______________________________________________________
Reply to this item at:
<https://savannah.gnu.org/bugs/?68570>
_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/
signature.asc
Description: PGP signature
