чување 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:
измењених датотека: 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;