1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-05-29 13:46:41 +02:00

Fix to avoid a denial-of-service attack if the display width is

enormous.  Also, clean up the code a bit by removing duplicate code.

(init_column_info): Remove forward decl; no longer needed.
(calculate_columns): New function, that contains code that used
to be common to print_many_per_line and print_horizontal.
(print_many_per_line, print_horizontal): Use it.
(decode_switches): Set max_idx here, not in calculate_columns.
(print_current_files): Don't call init_column_info; calculate_columns
now does that.
(init_column_info): Don't allocate a lot more space than is needed
to represent the current set of files.  Allocate all the new
size_t cells in one call to xnmalloc, rather than a row at a time.
This commit is contained in:
Jim Meyering
2003-10-14 09:09:43 +00:00
parent 49c9c00c8a
commit d7bed21394
+119 -122
View File
@@ -264,7 +264,7 @@ static void extract_dirs_from_files (const char *dirname,
int ignore_dot_and_dot_dot);
static void get_link_name (const char *filename, struct fileinfo *f);
static void indent (size_t from, size_t to);
static void init_column_info (void);
static size_t calculate_columns (bool by_columns);
static void print_current_files (void);
static void print_dir (const char *name, const char *realname);
static void print_file_name_and_frills (const struct fileinfo *f);
@@ -1654,6 +1654,8 @@ decode_switches (int argc, char **argv)
}
}
max_idx = MAX (1, line_length / MIN_COLUMN_WIDTH);
filename_quoting_options = clone_quoting_options (NULL);
if (get_quoting_style (filename_quoting_options) == escape_quoting_style)
set_char_quoting (filename_quoting_options, ' ', 1);
@@ -2799,12 +2801,10 @@ print_current_files (void)
break;
case many_per_line:
init_column_info ();
print_many_per_line ();
break;
case horizontal:
init_column_info ();
print_horizontal ();
break;
@@ -3482,75 +3482,29 @@ length_of_file_name_and_frills (const struct fileinfo *f)
return len;
}
/* FIXME: the first 40+ lines of this function are nearly identical
to those in the print_horizontal function. Fix that. */
static void
print_many_per_line (void)
{
struct column_info *line_fmt;
size_t filesno; /* Index into files. */
size_t row; /* Current row. */
size_t max_name_length; /* Length of longest file name + frills. */
size_t name_length; /* Length of each file name + frills. */
size_t pos; /* Current character column. */
size_t cols; /* Number of files across. */
size_t rows; /* Maximum number of files down. */
size_t max_cols;
/* Normally the maximum number of columns is determined by the
screen width. But if few files are available this might limit it
as well. */
max_cols = max_idx > files_index ? files_index : max_idx;
/* Compute the maximum number of possible columns. */
for (filesno = 0; filesno < files_index; ++filesno)
{
size_t i;
name_length = length_of_file_name_and_frills (files + filesno);
for (i = 0; i < max_cols; ++i)
{
if (column_info[i].valid_len)
{
size_t idx = filesno / ((files_index + i) / (i + 1));
size_t real_length = name_length + (idx == i ? 0 : 2);
if (real_length > column_info[i].col_arr[idx])
{
column_info[i].line_len += (real_length
- column_info[i].col_arr[idx]);
column_info[i].col_arr[idx] = real_length;
column_info[i].valid_len = column_info[i].line_len < line_length;
}
}
}
}
/* Find maximum allowed columns. */
for (cols = max_cols; cols > 1; --cols)
{
if (column_info[cols - 1].valid_len)
break;
}
line_fmt = &column_info[cols - 1];
size_t cols = calculate_columns (true);
struct column_info const *line_fmt = &column_info[cols - 1];
/* Calculate the number of rows that will be in each column except possibly
for a short column on the right. */
rows = files_index / cols + (files_index % cols != 0);
size_t rows = files_index / cols + (files_index % cols != 0);
for (row = 0; row < rows; row++)
{
size_t col = 0;
filesno = row;
pos = 0;
size_t filesno = row;
size_t pos = 0;
/* Print the next row. */
while (1)
{
size_t name_length = length_of_file_name_and_frills (files + filesno);
size_t max_name_length = line_fmt->col_arr[col++];
print_file_name_and_frills (files + filesno);
name_length = length_of_file_name_and_frills (files + filesno);
max_name_length = line_fmt->col_arr[col++];
filesno += rows;
if (filesno >= files_index)
@@ -3566,60 +3520,15 @@ print_many_per_line (void)
static void
print_horizontal (void)
{
struct column_info *line_fmt;
size_t filesno;
size_t max_name_length;
size_t name_length;
size_t cols;
size_t pos;
size_t max_cols;
/* Normally the maximum number of columns is determined by the
screen width. But if few files are available this might limit it
as well. */
max_cols = max_idx > files_index ? files_index : max_idx;
/* Compute the maximum file name length. */
max_name_length = 0;
for (filesno = 0; filesno < files_index; ++filesno)
{
size_t i;
name_length = length_of_file_name_and_frills (files + filesno);
for (i = 0; i < max_cols; ++i)
{
if (column_info[i].valid_len)
{
size_t idx = filesno % (i + 1);
size_t real_length = name_length + (idx == i ? 0 : 2);
if (real_length > column_info[i].col_arr[idx])
{
column_info[i].line_len += (real_length
- column_info[i].col_arr[idx]);
column_info[i].col_arr[idx] = real_length;
column_info[i].valid_len = column_info[i].line_len < line_length;
}
}
}
}
/* Find maximum allowed columns. */
for (cols = max_cols; cols > 1; --cols)
{
if (column_info[cols - 1].valid_len)
break;
}
line_fmt = &column_info[cols - 1];
pos = 0;
size_t pos = 0;
size_t cols = calculate_columns (false);
struct column_info const *line_fmt = &column_info[cols - 1];
size_t name_length = length_of_file_name_and_frills (files);
size_t max_name_length = line_fmt->col_arr[0];
/* Print first entry. */
print_file_name_and_frills (files);
name_length = length_of_file_name_and_frills (files);
max_name_length = line_fmt->col_arr[0];
/* Now the rest. */
for (filesno = 1; filesno < files_index; ++filesno)
@@ -3722,40 +3631,128 @@ attach (char *dest, const char *dirname, const char *name)
*dest = 0;
}
/* FIXME: this code allocates allocates O(N^2) space when ls is invoked
with `--width=N' and -x or -C. Either fix that or limit N. */
/* Allocate enough column info suitable for the current number of
files and display columns, and initialize the info to represent the
narrowest possible columns. */
static void
init_column_info (void)
{
size_t i;
int allocate = 0;
size_t max_cols = MIN (max_idx, files_index);
max_idx = line_length / MIN_COLUMN_WIDTH;
if (max_idx == 0)
max_idx = 1;
/* Currently allocated columns in column_info. */
static size_t column_info_alloc;
if (column_info == NULL)
if (column_info_alloc < max_cols)
{
column_info = xnmalloc (max_idx, sizeof *column_info);
allocate = 1;
size_t new_column_info_alloc;
size_t *p;
if (max_cols < max_idx / 2)
{
/* The number of columns is far less than the display width
allows. Grow the allocation, but only so that it's
double the current requirements. If the display is
extremely wide, this avoids allocating a lot of memory
that is never needed. */
column_info = xnrealloc (column_info, max_cols,
2 * sizeof *column_info);
new_column_info_alloc = 2 * max_cols;
}
else
{
column_info = xnrealloc (column_info, max_idx, sizeof *column_info);
new_column_info_alloc = max_idx;
}
/* Allocate the new size_t objects by computing the triangle
formula n * (n + 1) / 2, except that we don't need to
allocate the part of the triangle that we've already
allocated. Check for address arithmetic overflow. */
{
size_t column_info_growth = new_column_info_alloc - column_info_alloc;
size_t s = column_info_alloc + 1 + new_column_info_alloc;
size_t t = s * column_info_growth;
if (s < new_column_info_alloc || t / column_info_growth != s)
xalloc_die ();
p = xnmalloc (t / 2, sizeof *p);
}
/* Grow the triangle by parceling out the cells just allocated. */
for (i = column_info_alloc; i < new_column_info_alloc; i++)
{
column_info[i].col_arr = p;
p += i + 1;
}
column_info_alloc = new_column_info_alloc;
}
for (i = 0; i < max_idx; ++i)
for (i = 0; i < max_cols; ++i)
{
size_t j;
column_info[i].valid_len = true;
column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
if (allocate)
column_info[i].col_arr = xnmalloc (i + 1,
sizeof column_info[i].col_arr[0]);
for (j = 0; j <= i; ++j)
column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
}
}
/* Calculate the number of columns needed to represent the current set
of files in the current display width. */
static size_t
calculate_columns (bool by_columns)
{
size_t filesno; /* Index into files. */
size_t cols; /* Number of files across. */
/* Normally the maximum number of columns is determined by the
screen width. But if few files are available this might limit it
as well. */
size_t max_cols = MIN (max_idx, files_index);
init_column_info ();
/* Compute the maximum number of possible columns. */
for (filesno = 0; filesno < files_index; ++filesno)
{
size_t name_length = length_of_file_name_and_frills (files + filesno);
size_t i;
for (i = 0; i < max_cols; ++i)
{
if (column_info[i].valid_len)
{
size_t idx = (by_columns
? filesno / ((files_index + i) / (i + 1))
: filesno % (i + 1));
size_t real_length = name_length + (idx == i ? 0 : 2);
if (column_info[i].col_arr[idx] < real_length)
{
column_info[i].line_len += (real_length
- column_info[i].col_arr[idx]);
column_info[i].col_arr[idx] = real_length;
column_info[i].valid_len = (column_info[i].line_len
< line_length);
}
}
}
}
/* Find maximum allowed columns. */
for (cols = max_cols; 1 < cols; --cols)
{
if (column_info[cols - 1].valid_len)
break;
}
return cols;
}
void
usage (int status)
{