diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2005-09-13 22:43:11 +0000 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2005-09-13 22:43:11 +0000 |
commit | 36081fa6358527fcec93c8df3f3a8cfbf0b72208 (patch) | |
tree | 48854bfab86743d0317ec7d3fa3e0cb35eaa7b77 /src | |
parent | 360b98042fa20a4418371cb91ad0d881c6d3762d (diff) | |
download | coreutils-36081fa6358527fcec93c8df3f3a8cfbf0b72208.tar.xz |
* src/dd.c: Detect some very unlikely buffer overflows.
(INPUT_BLOCK_SLOP, OUTPUT_BLOCK_SLOP): New macros.
(MAX_BLOCKSIZE): Now accepts an arg. All uses changed.
(page_size): New var.
(scanargs, skip, main): Use more-straightforward way to detect overflow.
(dd_copy): Use page_size rather than invoking getpagesize.
Use INPUT_BLOCK_SLOP, OUTPUT_BLOCK_SLOP.
(main): Set page_size.
Avoid a call to stat in the usual case where ftruncate succeeds.
Diffstat (limited to 'src')
-rw-r--r-- | src/dd.c | 80 |
1 files changed, 45 insertions, 35 deletions
@@ -81,11 +81,18 @@ static void process_signals (void); /* Default input and output blocksize. */ #define DEFAULT_BLOCKSIZE 512 -/* Maximum blocksize. Keep it smaller than SIZE_MAX, so that we can +/* How many bytes to add to the input and output block sizes before invoking + malloc. See dd_copy for details. INPUT_BLOCK_SLOP must be no less than + OUTPUT_BLOCK_SLOP. */ +#define INPUT_BLOCK_SLOP (2 * SWAB_ALIGN_OFFSET + 2 * page_size - 1) +#define OUTPUT_BLOCK_SLOP (page_size - 1) + +/* Maximum blocksize for the given SLOP. + Keep it smaller than SIZE_MAX - SLOP, so that we can allocate buffers that size. Keep it smaller than SSIZE_MAX, for the benefit of system calls like "read". And keep it smaller than OFF_T_MAX, for the benefit of the large-offset seek code. */ -#define MAX_BLOCKSIZE MIN (SIZE_MAX, MIN (SSIZE_MAX, OFF_T_MAX)) +#define MAX_BLOCKSIZE(slop) MIN (SIZE_MAX - (slop), MIN (SSIZE_MAX, OFF_T_MAX)) /* Conversions bit masks. */ enum @@ -128,6 +135,9 @@ static char const *input_file = NULL; /* The name of the output file, or NULL for the standard output. */ static char const *output_file = NULL; +/* The page size on this host. */ +static size_t page_size; + /* The number of bytes in which atomic reads are done. */ static size_t input_blocksize = 0; @@ -883,26 +893,25 @@ scanargs (int argc, char **argv) if (STREQ (name, "ibs")) { - invalid |= ! (0 < n && n <= MAX_BLOCKSIZE); + invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP)); input_blocksize = n; conversions_mask |= C_TWOBUFS; } else if (STREQ (name, "obs")) { - invalid |= ! (0 < n && n <= MAX_BLOCKSIZE); + invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (OUTPUT_BLOCK_SLOP)); output_blocksize = n; conversions_mask |= C_TWOBUFS; } else if (STREQ (name, "bs")) { - invalid |= ! (0 < n && n <= MAX_BLOCKSIZE); + invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP)); output_blocksize = input_blocksize = n; } else if (STREQ (name, "cbs")) { + invalid |= ! (0 < n && n <= SIZE_MAX); conversion_blocksize = n; - invalid |= (conversion_blocksize != n - || conversion_blocksize == 0); } else if (STREQ (name, "skip")) skip_records = n; @@ -1116,14 +1125,14 @@ static uintmax_t skip (int fdesc, char const *file, uintmax_t records, size_t blocksize, char *buf) { - off_t offset = records * blocksize; + uintmax_t offset = records * blocksize; /* Try lseek and if an error indicates it was an inappropriate operation -- or if the the file offset is not representable as an off_t -- fall back on using read. */ errno = 0; - if ((uintmax_t) offset / blocksize == records + if (records <= OFF_T_MAX / blocksize && 0 <= skip_via_lseek (file, fdesc, offset, SEEK_CUR)) { if (fdesc == STDIN_FILENO) @@ -1332,7 +1341,6 @@ dd_copy (void) size_t partread = 0; int exit_status = EXIT_SUCCESS; - size_t page_size = getpagesize (); size_t n_bytes_read; /* Leave at least one extra byte at the beginning and end of `ibuf' @@ -1352,9 +1360,7 @@ dd_copy (void) It is necessary when accessing raw (i.e. character special) disk devices on Unixware or other SVR4-derived system. */ - real_buf = xmalloc (input_blocksize - + 2 * SWAB_ALIGN_OFFSET - + 2 * page_size - 1); + real_buf = xmalloc (input_blocksize + INPUT_BLOCK_SLOP); ibuf = real_buf; ibuf += SWAB_ALIGN_OFFSET; /* allow space for swab */ @@ -1363,7 +1369,7 @@ dd_copy (void) if (conversions_mask & C_TWOBUFS) { /* Page-align the output buffer, too. */ - real_obuf = xmalloc (output_blocksize + page_size - 1); + real_obuf = xmalloc (output_blocksize + OUTPUT_BLOCK_SLOP); obuf = ptr_align (real_obuf, page_size); } else @@ -1587,6 +1593,8 @@ main (int argc, char **argv) /* Arrange to close stdout if parse_long_options exits. */ atexit (close_stdout); + page_size = getpagesize (); + parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION, usage, AUTHORS, (char const *) NULL); if (getopt_long (argc, argv, "", NULL, NULL) != -1) @@ -1643,28 +1651,30 @@ main (int argc, char **argv) #if HAVE_FTRUNCATE if (seek_records != 0 && !(conversions_mask & C_NOTRUNC)) { - struct stat stdout_stat; - off_t o = seek_records * output_blocksize; - uintmax_t uo = o; - if (uo / output_blocksize != seek_records) - error (EXIT_FAILURE, 0, _("file offset out of range")); - - if (fstat (STDOUT_FILENO, &stdout_stat) != 0) - error (EXIT_FAILURE, errno, _("cannot fstat %s"), - quote (output_file)); - - /* Complain only when ftruncate fails on a regular file, a - directory, or a shared memory object, as - POSIX 1003.1-2004 specifies ftruncate's behavior only for these - file types. For example, do not complain when Linux 2.4 - ftruncate fails on /dev/fd0. */ - if (ftruncate (STDOUT_FILENO, o) != 0 - && (S_ISREG (stdout_stat.st_mode) + uintmax_t size = seek_records * output_blocksize; + + if (OFF_T_MAX / output_blocksize < seek_records) + error (EXIT_FAILURE, EOVERFLOW, "seek=%"PRIuMAX"", seek_records); + + if (ftruncate (STDOUT_FILENO, size) != 0) + { + /* Complain only when ftruncate fails on a regular file, a + directory, or a shared memory object, as POSIX 1003.1-2004 + specifies ftruncate's behavior only for these file types. + For example, do not complain when Linux 2.4 ftruncate + fails on /dev/fd0. */ + int ftruncate_errno = errno; + struct stat stdout_stat; + if (fstat (STDOUT_FILENO, &stdout_stat) != 0) + error (EXIT_FAILURE, errno, _("cannot fstat %s"), + quote (output_file)); + if (S_ISREG (stdout_stat.st_mode) || S_ISDIR (stdout_stat.st_mode) - || S_TYPEISSHM (&stdout_stat))) - error (EXIT_FAILURE, errno, - _("advancing past %"PRIuMAX" bytes in output file %s"), - uo, quote (output_file)); + || S_TYPEISSHM (&stdout_stat)) + error (EXIT_FAILURE, ftruncate_errno, + _("truncating at %"PRIuMAX" bytes in output file %s"), + size, quote (output_file)); + } } #endif } |