Resolves: #1482445 - ptx: fix a possible crash caused by integer overflow

This commit is contained in:
Kamil Dudka 2017-08-18 17:52:50 +02:00
parent d03c327d23
commit 80f5fc5d4d
2 changed files with 551 additions and 0 deletions

View File

@ -0,0 +1,547 @@
From 7408a9758924a1ccbabf9fcab401a31ef1b7c755 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 17 Aug 2017 12:02:16 -0700
Subject: [PATCH] ptx: fix some integer overflow bugs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Problem reported by Lukas Zachar at:
http://bugzilla.redhat.com/1482445
* src/ptx.c (line_width, gap_size, maximum_word_length)
(reference_max_width, half_line_width, before_max_width)
(keyafter_max_width, truncation_string_length, compare_words)
(compare_occurs, search_table, find_occurs_in_text, print_spaces)
(fix_output_parameters, define_all_fields):
Use ptrdiff_t, not int, for object offsets and sizes.
(WORD, OCCURS): Use ptrdiff_t, not short int.
(WORD_TABLE, number_of_occurs, generate_all_output):
Prefer ptrdiff_t to size_t where either will do.
(total_line_count, file_line_count, OCCURS, fix_output_parameters)
(define_all_fields):
Use intmax_t, not int, for line counts.
(DELTA): Remove. All uses changed.
(OCCURS, find_occurs_in_text, fix_output_parameters):
Use int, not size_t, for file indexes.
(tail_truncation, before_truncation, keyafter_truncation)
(head_truncation, search_table, define_all_fields)
(generate_all_output):
Use bool for booleans.
(digest_word_file, find_occurs_in_text):
Use x2nrealloc instead of checking for overflow by hand.
(find_occurs_in_text, fix_output_parameters, define_all_fields):
Omit unnecessary cast.
(fix_output_parameters): Dont assume integers fit in 11 digits.
(fix_output_parameters, define_all_fields):
Use sprintf return value rather than calling strlen.
(define_all_fields): Do not rely on sprintf to generate a string
that may contain more than INT_MAX bytes.
(main): Use xstrtoimax, not xstrtoul.
Use xnmalloc to catch integer overflow.
Upstream-commit: 1d9765a764790cc68ec52c16d7ccbf633c404f0e
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/ptx.c | 194 +++++++++++++++++++++++++++++---------------------------------
1 file changed, 91 insertions(+), 103 deletions(-)
diff --git a/src/ptx.c b/src/ptx.c
index c0c9733..2aababf 100644
--- a/src/ptx.c
+++ b/src/ptx.c
@@ -59,9 +59,9 @@
/* Global definitions. */
/* FIXME: There are many unchecked integer overflows in this file,
- that will cause this command to misbehave given large inputs or
- options. Many of the "int" values below should be "size_t" or
- something else like that. */
+ and in theory they could cause this command to have undefined
+ behavior given large inputs or options. This command should
+ diagnose any such overflow and exit. */
/* Program options. */
@@ -77,8 +77,8 @@ static bool gnu_extensions = true; /* trigger all GNU extensions */
static bool auto_reference = false; /* refs are 'file_name:line_number:' */
static bool input_reference = false; /* refs at beginning of input lines */
static bool right_reference = false; /* output refs after right context */
-static int line_width = 72; /* output line width in characters */
-static int gap_size = 3; /* number of spaces between output fields */
+static ptrdiff_t line_width = 72; /* output line width in characters */
+static ptrdiff_t gap_size = 3; /* number of spaces between output fields */
static const char *truncation_string = "/";
/* string used to mark line truncations */
static const char *macro_name = "xx"; /* macro name for roff or TeX output */
@@ -105,8 +105,8 @@ static struct regex_data context_regex; /* end of context */
static struct regex_data word_regex; /* keyword */
/* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
- whole file. A WORD is something smaller, its length should fit in a
- short integer. A WORD_TABLE may contain several WORDs. */
+ whole file. A WORD is similar, except it is intended for smaller regions.
+ A WORD_TABLE may contain several WORDs. */
typedef struct
{
@@ -118,7 +118,7 @@ BLOCK;
typedef struct
{
char *start; /* pointer to beginning of region */
- short int size; /* length of the region */
+ ptrdiff_t size; /* length of the region */
}
WORD;
@@ -126,7 +126,7 @@ typedef struct
{
WORD *start; /* array of WORDs */
size_t alloc; /* allocated length */
- size_t length; /* number of used entries */
+ ptrdiff_t length; /* number of used entries */
}
WORD_TABLE;
@@ -149,10 +149,10 @@ static struct re_registers word_regs;
static char word_fastmap[CHAR_SET_SIZE];
/* Maximum length of any word read. */
-static int maximum_word_length;
+static ptrdiff_t maximum_word_length;
/* Maximum width of any reference used. */
-static int reference_max_width;
+static ptrdiff_t reference_max_width;
/* Ignore and Only word tables. */
@@ -162,9 +162,9 @@ static WORD_TABLE only_table; /* table of words to select */
/* Source text table, and scanning macros. */
static int number_input_files; /* number of text input files */
-static int total_line_count; /* total number of lines seen so far */
+static intmax_t total_line_count; /* total number of lines seen so far */
static const char **input_file_name; /* array of text input file names */
-static int *file_line_count; /* array of 'total_line_count' values at end */
+static intmax_t *file_line_count; /* array of line count values at end */
static BLOCK *text_buffers; /* files to study */
@@ -219,20 +219,18 @@ static BLOCK *text_buffers; /* files to study */
When automatic references are used, the 'reference' value is the
overall line number in all input files read so far, in this case, it
- is of type (int). When input references are used, the 'reference'
+ is of type intmax_t. When input references are used, the 'reference'
value indicates the distance between the keyword beginning and the
- start of the reference field, it is of type (DELTA) and usually
+ start of the reference field, and it fits in ptrdiff_t and is usually
negative. */
-typedef short int DELTA; /* to hold displacement within one context */
-
typedef struct
{
WORD key; /* description of the keyword */
- DELTA left; /* distance to left context start */
- DELTA right; /* distance to right context end */
- int reference; /* reference descriptor */
- size_t file_index; /* corresponding file */
+ ptrdiff_t left; /* distance to left context start */
+ ptrdiff_t right; /* distance to right context end */
+ intmax_t reference; /* reference descriptor */
+ int file_index; /* corresponding file */
}
OCCURS;
@@ -241,7 +239,7 @@ OCCURS;
static OCCURS *occurs_table[1]; /* all words retained from the read text */
static size_t occurs_alloc[1]; /* allocated size of occurs_table */
-static size_t number_of_occurs[1]; /* number of used slots in occurs_table */
+static ptrdiff_t number_of_occurs[1]; /* number of used slots in occurs_table */
/* Communication among output routines. */
@@ -249,10 +247,17 @@ static size_t number_of_occurs[1]; /* number of used slots in occurs_table */
/* Indicate if special output processing is requested for each character. */
static char edited_flag[CHAR_SET_SIZE];
-static int half_line_width; /* half of line width, reference excluded */
-static int before_max_width; /* maximum width of before field */
-static int keyafter_max_width; /* maximum width of keyword-and-after field */
-static int truncation_string_length;/* length of string that flags truncation */
+/* Half of line width, reference excluded. */
+static ptrdiff_t half_line_width;
+
+/* Maximum width of before field. */
+static ptrdiff_t before_max_width;
+
+/* Maximum width of keyword-and-after field. */
+static ptrdiff_t keyafter_max_width;
+
+/* Length of string that flags truncation. */
+static ptrdiff_t truncation_string_length;
/* When context is limited by lines, wraparound may happen on final output:
the 'head' pointer gives access to some supplementary left context which
@@ -261,16 +266,16 @@ static int truncation_string_length;/* length of string that flags truncation */
beginning of the output line. */
static BLOCK tail; /* tail field */
-static int tail_truncation; /* flag truncation after the tail field */
+static bool tail_truncation; /* flag truncation after the tail field */
static BLOCK before; /* before field */
-static int before_truncation; /* flag truncation before the before field */
+static bool before_truncation; /* flag truncation before the before field */
static BLOCK keyafter; /* keyword-and-after field */
-static int keyafter_truncation; /* flag truncation after the keyafter field */
+static bool keyafter_truncation; /* flag truncation after the keyafter field */
static BLOCK head; /* head field */
-static int head_truncation; /* flag truncation before the head field */
+static bool head_truncation; /* flag truncation before the head field */
static BLOCK reference; /* reference field for input reference mode */
@@ -540,8 +545,8 @@ compare_words (const void *void_first, const void *void_second)
{
#define first ((const WORD *) void_first)
#define second ((const WORD *) void_second)
- int length; /* minimum of two lengths */
- int counter; /* cursor in words */
+ ptrdiff_t length; /* minimum of two lengths */
+ ptrdiff_t counter; /* cursor in words */
int value; /* value of comparison */
length = first->size < second->size ? first->size : second->size;
@@ -567,7 +572,7 @@ compare_words (const void *void_first, const void *void_second)
}
}
- return first->size - second->size;
+ return first->size < second->size ? -1 : first->size > second->size;
#undef first
#undef second
}
@@ -586,21 +591,21 @@ compare_occurs (const void *void_first, const void *void_second)
int value;
value = compare_words (&first->key, &second->key);
- return value == 0 ? first->key.start - second->key.start : value;
+ return (value ? value
+ : first->key.start < second->key.start ? -1
+ : first->key.start > second->key.start);
#undef first
#undef second
}
-/*------------------------------------------------------------.
-| Return !0 if WORD appears in TABLE. Uses a binary search. |
-`------------------------------------------------------------*/
+/* True if WORD appears in TABLE. Uses a binary search. */
-static int _GL_ATTRIBUTE_PURE
+static bool _GL_ATTRIBUTE_PURE
search_table (WORD *word, WORD_TABLE *table)
{
- int lowest; /* current lowest possible index */
- int highest; /* current highest possible index */
- int middle; /* current middle index */
+ ptrdiff_t lowest; /* current lowest possible index */
+ ptrdiff_t highest; /* current highest possible index */
+ ptrdiff_t middle; /* current middle index */
int value; /* value from last comparison */
lowest = 0;
@@ -614,9 +619,9 @@ search_table (WORD *word, WORD_TABLE *table)
else if (value > 0)
lowest = middle + 1;
else
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/*---------------------------------------------------------------------.
@@ -713,14 +718,8 @@ digest_word_file (const char *file_name, WORD_TABLE *table)
if (cursor > word_start)
{
if (table->length == table->alloc)
- {
- if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
- xalloc_die ();
- table->alloc = table->alloc * 2 + 1;
- table->start = xrealloc (table->start,
- table->alloc * sizeof *table->start);
- }
-
+ table->start = x2nrealloc (table->start, &table->alloc,
+ sizeof *table->start);
table->start[table->length].start = word_start;
table->start[table->length].size = cursor - word_start;
table->length++;
@@ -744,13 +743,13 @@ digest_word_file (const char *file_name, WORD_TABLE *table)
`----------------------------------------------------------------------*/
static void
-find_occurs_in_text (size_t file_index)
+find_occurs_in_text (int file_index)
{
char *cursor; /* for scanning the source text */
char *scan; /* for scanning the source text also */
char *line_start; /* start of the current input line */
char *line_scan; /* newlines scanned until this point */
- int reference_length; /* length of reference in input mode */
+ ptrdiff_t reference_length; /* length of reference in input mode */
WORD possible_key; /* possible key, to ease searches */
OCCURS *occurs_cursor; /* current OCCURS under construction */
@@ -946,16 +945,9 @@ find_occurs_in_text (size_t file_index)
where it will be constructed. */
if (number_of_occurs[0] == occurs_alloc[0])
- {
- if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
- < occurs_alloc[0])
- xalloc_die ();
- occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
- occurs_table[0] =
- xrealloc (occurs_table[0],
- occurs_alloc[0] * sizeof *occurs_table[0]);
- }
-
+ occurs_table[0] = x2nrealloc (occurs_table[0],
+ &occurs_alloc[0],
+ sizeof *occurs_table[0]);
occurs_cursor = occurs_table[0] + number_of_occurs[0];
/* Define the reference field, if any. */
@@ -990,8 +982,7 @@ find_occurs_in_text (size_t file_index)
of the reference. The reference position is simply the
value of 'line_start'. */
- occurs_cursor->reference
- = (DELTA) (line_start - possible_key.start);
+ occurs_cursor->reference = line_start - possible_key.start;
if (reference_length > reference_max_width)
reference_max_width = reference_length;
}
@@ -1023,11 +1014,9 @@ find_occurs_in_text (size_t file_index)
`-----------------------------------------*/
static void
-print_spaces (int number)
+print_spaces (ptrdiff_t number)
{
- int counter;
-
- for (counter = number; counter > 0; counter--)
+ for (ptrdiff_t counter = number; counter > 0; counter--)
putchar (' ');
}
@@ -1200,10 +1189,9 @@ print_field (BLOCK field)
static void
fix_output_parameters (void)
{
- size_t file_index; /* index in text input file arrays */
- int line_ordinal; /* line ordinal value for reference */
- char ordinal_string[12]; /* edited line ordinal for reference */
- int reference_width; /* width for the whole reference */
+ int file_index; /* index in text input file arrays */
+ intmax_t line_ordinal; /* line ordinal value for reference */
+ ptrdiff_t reference_width; /* width for the whole reference */
int character; /* character ordinal */
const char *cursor; /* cursor in some constant strings */
@@ -1219,15 +1207,15 @@ fix_output_parameters (void)
line_ordinal = file_line_count[file_index] + 1;
if (file_index > 0)
line_ordinal -= file_line_count[file_index - 1];
- sprintf (ordinal_string, "%d", line_ordinal);
- reference_width = strlen (ordinal_string);
+ char ordinal_string[INT_BUFSIZE_BOUND (intmax_t)];
+ reference_width = sprintf (ordinal_string, "%"PRIdMAX, line_ordinal);
if (input_file_name[file_index])
reference_width += strlen (input_file_name[file_index]);
if (reference_width > reference_max_width)
reference_max_width = reference_width;
}
reference_max_width++;
- reference.start = xmalloc ((size_t) reference_max_width + 1);
+ reference.start = xmalloc (reference_max_width + 1);
}
/* If the reference appears to the left of the output line, reserve some
@@ -1355,14 +1343,14 @@ fix_output_parameters (void)
static void
define_all_fields (OCCURS *occurs)
{
- int tail_max_width; /* allowable width of tail field */
- int head_max_width; /* allowable width of head field */
+ ptrdiff_t tail_max_width; /* allowable width of tail field */
+ ptrdiff_t head_max_width; /* allowable width of head field */
char *cursor; /* running cursor in source text */
char *left_context_start; /* start of left context */
char *right_context_end; /* end of right context */
char *left_field_start; /* conservative start for 'head'/'before' */
const char *file_name; /* file name for reference */
- int line_ordinal; /* line ordinal for reference */
+ intmax_t line_ordinal; /* line ordinal for reference */
const char *buffer_start; /* start of buffered file for this occurs */
const char *buffer_end; /* end of buffered file for this occurs */
@@ -1435,7 +1423,7 @@ define_all_fields (OCCURS *occurs)
before_truncation = cursor > left_context_start;
}
else
- before_truncation = 0;
+ before_truncation = false;
SKIP_WHITE (before.start, buffer_end);
@@ -1468,11 +1456,11 @@ define_all_fields (OCCURS *occurs)
if (tail.end > tail.start)
{
- keyafter_truncation = 0;
+ keyafter_truncation = false;
tail_truncation = truncation_string && tail.end < right_context_end;
}
else
- tail_truncation = 0;
+ tail_truncation = false;
SKIP_WHITE_BACKWARDS (tail.end, tail.start);
}
@@ -1483,7 +1471,7 @@ define_all_fields (OCCURS *occurs)
tail.start = NULL;
tail.end = NULL;
- tail_truncation = 0;
+ tail_truncation = false;
}
/* 'head' could not take more columns than what has been left in the right
@@ -1506,12 +1494,12 @@ define_all_fields (OCCURS *occurs)
if (head.end > head.start)
{
- before_truncation = 0;
+ before_truncation = false;
head_truncation = (truncation_string
&& head.start > left_context_start);
}
else
- head_truncation = 0;
+ head_truncation = false;
SKIP_WHITE (head.start, head.end);
}
@@ -1522,7 +1510,7 @@ define_all_fields (OCCURS *occurs)
head.start = NULL;
head.end = NULL;
- head_truncation = 0;
+ head_truncation = false;
}
if (auto_reference)
@@ -1540,8 +1528,8 @@ define_all_fields (OCCURS *occurs)
if (occurs->file_index > 0)
line_ordinal -= file_line_count[occurs->file_index - 1];
- sprintf (reference.start, "%s:%d", file_name, line_ordinal);
- reference.end = reference.start + strlen (reference.start);
+ char *file_end = stpcpy (reference.start, file_name);
+ reference.end = file_end + sprintf (file_end, ":%"PRIdMAX, line_ordinal);
}
else if (input_reference)
{
@@ -1549,7 +1537,7 @@ define_all_fields (OCCURS *occurs)
/* Reference starts at saved position for reference and extends right
until some white space is met. */
- reference.start = keyafter.start + (DELTA) occurs->reference;
+ reference.start = keyafter.start + occurs->reference;
reference.end = reference.start;
SKIP_NON_WHITE (reference.end, right_context_end);
}
@@ -1753,7 +1741,7 @@ output_one_dumb_line (void)
static void
generate_all_output (void)
{
- size_t occurs_index; /* index of keyword entry being processed */
+ ptrdiff_t occurs_index; /* index of keyword entry being processed */
OCCURS *occurs_cursor; /* current keyword entry being processed */
/* The following assignments are useful to provide default values in case
@@ -1762,11 +1750,11 @@ generate_all_output (void)
tail.start = NULL;
tail.end = NULL;
- tail_truncation = 0;
+ tail_truncation = false;
head.start = NULL;
head.end = NULL;
- head_truncation = 0;
+ head_truncation = false;
/* Loop over all keyword occurrences. */
@@ -1946,12 +1934,12 @@ main (int argc, char **argv)
case 'g':
{
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
+ intmax_t tmp;
+ if (! (xstrtoimax (optarg, NULL, 0, &tmp, NULL) == LONGINT_OK
+ && 0 < tmp && tmp <= PTRDIFF_MAX))
die (EXIT_FAILURE, 0, _("invalid gap width: %s"),
quote (optarg));
- gap_size = tmp_ulong;
+ gap_size = tmp;
break;
}
@@ -1973,12 +1961,12 @@ main (int argc, char **argv)
case 'w':
{
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
+ intmax_t tmp;
+ if (! (xstrtoimax (optarg, NULL, 0, &tmp, NULL) == LONGINT_OK
+ && 0 < tmp && tmp <= PTRDIFF_MAX))
die (EXIT_FAILURE, 0, _("invalid line width: %s"),
quote (optarg));
- line_width = tmp_ulong;
+ line_width = tmp;
break;
}
@@ -2045,9 +2033,9 @@ main (int argc, char **argv)
else if (gnu_extensions)
{
number_input_files = argc - optind;
- input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
- file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
- text_buffers = xmalloc (number_input_files * sizeof *text_buffers);
+ input_file_name = xnmalloc (number_input_files, sizeof *input_file_name);
+ file_line_count = xnmalloc (number_input_files, sizeof *file_line_count);
+ text_buffers = xnmalloc (number_input_files, sizeof *text_buffers);
for (file_index = 0; file_index < number_input_files; file_index++)
{
--
2.9.5

View File

@ -28,6 +28,9 @@ Patch3: coreutils-8.27-tail-inotify-recreate.patch
# doc: mention `setpriv --no-new-privs` feature in runcon info (#1360903)
Patch4: coreutils-8.27-runcon-doc.patch
# ptx: fix a possible crash caused by integer overflow (#1482445)
Patch5: coreutils-8.27-ptx-int-overflow.patch
# disable the test-lock gnulib test prone to deadlock
Patch100: coreutils-8.26-test-lock.patch
@ -311,6 +314,7 @@ fi
%changelog
* Fri Aug 18 2017 Kamil Dudka <kdudka@redhat.com> - 8.27-6
- ptx: fix a possible crash caused by integer overflow (#1482445)
- doc: mention `setpriv --no-new-privs` feature in runcon info (#1360903)
* Fri Apr 28 2017 Sebastian Kisela <skisela@redhat.com> - 8.27-5