table

Помоћни програм из командне линије за форматирање и приказ CSV-а
git clone https://git.sr.ht/~strahinja/table
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување cb3800d5387475aa5b83c98a334b1976a43039c0
родитељ aa4fdf8bd7342ef2f1850121d5a2733406c6f236
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Thu, 16 Apr 2026 10:39:44 +0000

Fix edge cases when the input is binary (discovered by afl-fuzz)

Diffstat:
Mdefs.h | 1+
Mtable.1.in | 2++
Mtable.c | 31+++++++++++++++++++++++++++----
Mutf8.c | 2+-
измењених датотека: 4, додавања: 31(+), брисања: 5(-)

diff --git a/defs.h b/defs.h @@ -114,6 +114,7 @@ enum { ERR_NOTREG_FILE, ERR_UNMATCHED_QUOTE, ERR_STREAM_ERROR, + ERR_BINARY_INPUT, }; enum { Q_PROCESS, Q_IGNORE }; diff --git a/table.1.in b/table.1.in @@ -260,6 +260,8 @@ File is not a regular file. A quote is present without its corresponding closing quote. .It Li 211 There has been an input stream error. +.It Li 212 +Input is not text (it is binary). .El . .Sh EXAMPLES diff --git a/table.c b/table.c @@ -101,6 +101,7 @@ const char* errors[] = { [ERR_NOTREG_FILE] = "'%s' is not a regular file", [ERR_UNMATCHED_QUOTE] = "Unmatched quote", [ERR_STREAM_ERROR] = "Input stream error", + [ERR_BINARY_INPUT] = "Input is not text", }; /* clang-format on */ @@ -144,10 +145,13 @@ number_of_columns(const u8* input, u32 delimiter) while (*pinput) { if (u8_rune_to_u32(&uch, pinput, &ch_len)) + { warning("Malformed UTF-8 at position %td: " "uch=%02X, " "pinput=%02X", pinput - input, uch, *pinput); + ch_len = 1; + } if (uch == delimiter) result++; pinput += ch_len; @@ -193,7 +197,9 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (!no_ansi && lineno == 0) fputs(ANSI_SGR_BOLD_ON, stdout); - u8_rune_to_u32(&uch, ps, &ch_len); + if (u8_rune_to_u32(&uch, ps, &ch_len)) + ch_len = 1; + output_chars = 0; while (output_chars != ch_len) { @@ -401,7 +407,7 @@ set_format(const char* arg, int** format, size_t* format_size) { *ptoken = 0; errno = 0; - num = strtonumber(token, INT_MIN, INT_MAX, &errornum); + num = strtonumber(token, INT_MIN, INT_MAX, &errornum); if (stn_handle_error(token, INT_MIN, INT_MAX, errornum)) { free(token); @@ -577,12 +583,14 @@ main(int argc, char** argv) char* sep = NULL; char* progname = NULL; char* eol = NULL; + char* nulbyte = NULL; ptrdiff_t outbuf_len; size_t outbufrunelen = 0; size_t ch_len = 0; size_t output_lines = 0; size_t column_start = 0; int in_quote = 0; + int possible_nulbyte = 0; int u8len = 0; UNUSED(argc); @@ -876,7 +884,8 @@ done_arg: CALLOC(line, u8, line_size); outbuf_size = line_max; CALLOC(outbuf, u8, outbuf_size); - pline = line; + pline = line; + possible_nulbyte = 0; do_input: if (feof(input)) @@ -892,6 +901,11 @@ do_input: goto done_input; } + if (possible_nulbyte) + { + error_code = ERR_BINARY_INPUT; + goto main_done; + } eol = strchr((char*)pline, '\n'); if (eol) { @@ -899,7 +913,16 @@ do_input: *(eol - 1) = 0; *eol = 0; } - else + + nulbyte = strchr((char*)pline, 0); + + /* We don't know if the NUL byte not being equal to + * (possibly NULL) eol is the result of EOF or not + */ + if (nulbyte && (nulbyte != eol)) + possible_nulbyte = 1; + + if (!eol) { line_size += line_max; REALLOC(line, u8, line_size); diff --git a/utf8.c b/utf8.c @@ -54,7 +54,7 @@ u8_rune_to_u32(u32* to, const u8* from, size_t* from_delta) *to = *from & extract_masks[len - 1]; for (int i = 1; i < len; i++) { - pfrom = from + i; + pfrom = &from[i]; if (!*pfrom) return 1; *to <<= 6;