From: Paul Eggert Date: Thu, 8 Aug 2024 17:51:39 +0000 (-0700) Subject: Handle enormous record sizes better X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3ffe2eb0738504aabee0d3b4d0c03e01739b9d6c;p=thirdparty%2Ftar.git Handle enormous record sizes better Formerly the code could misbehave when the user specified a record size greater than min (INT_MAX * 512 + 511, PTRDIFF_MAX, SSIZE_MAX). * src/delete.c (new_blocks, delete_archive_members): * src/system.c (sys_exec_info_script): * src/tar.c (blocking_factor, record_size): Don’t limit blocking factor to INT_MAX. Prefer signed type for record_size. Do not exceed IDX_MAX or SSIZE_MAX for record_size; the SSIZE_MAX limit is needed so that ‘read’ and ‘write’ calls behave sensibly. --- diff --git a/src/buffer.c b/src/buffer.c index 92d187c5..e7f1e264 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -985,11 +985,11 @@ short_read (size_t status) if (! read_full_records) { - unsigned long rest = record_size - left; + idx_t rest = record_size - left; FATAL_ERROR ((0, 0, - ngettext ("Unaligned block (%lu byte) in archive", - "Unaligned block (%lu bytes) in archive", + ngettext ("Unaligned block (%td byte) in archive", + "Unaligned block (%td bytes) in archive", rest), rest)); } diff --git a/src/common.h b/src/common.h index 135e6f3e..0c1b139e 100644 --- a/src/common.h +++ b/src/common.h @@ -98,8 +98,8 @@ extern enum archive_format archive_format; are always related, the second being BLOCKSIZE times the first. They do not have _option in their name, even if their values is derived from option decoding, as these are especially important in tar. */ -extern int blocking_factor; -extern size_t record_size; +extern idx_t blocking_factor; +extern idx_t record_size; extern bool absolute_names_option; diff --git a/src/delete.c b/src/delete.c index 4223f95c..069c47fd 100644 --- a/src/delete.c +++ b/src/delete.c @@ -23,7 +23,7 @@ #include static union block *new_record; -static int new_blocks; +static idx_t new_blocks; static bool acting_as_filter; /* The number of records skipped at the start of the archive, when @@ -363,11 +363,11 @@ delete_archive_members (void) /* Write the end of tape. FIXME: we can't use write_eot here, as it gets confused when the input is at end of file. */ - int total_zero_blocks = 0; + idx_t total_zero_blocks = 0; do { - int zero_blocks = blocking_factor - new_blocks; + idx_t zero_blocks = blocking_factor - new_blocks; memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks); total_zero_blocks += zero_blocks; write_record (total_zero_blocks < 2); diff --git a/src/system.c b/src/system.c index 3ec4e993..b7976a89 100644 --- a/src/system.c +++ b/src/system.c @@ -875,10 +875,10 @@ sys_exec_info_script (const char **archive_name, int volume_number) /* Child */ setenv ("TAR_VERSION", PACKAGE_VERSION, 1); setenv ("TAR_ARCHIVE", *archive_name, 1); - char intbuf[INT_BUFSIZE_BOUND (int)]; + char intbuf[INT_BUFSIZE_BOUND (intmax_t)]; sprintf (intbuf, "%d", volume_number); setenv ("TAR_VOLUME", intbuf, 1); - sprintf (intbuf, "%d", blocking_factor); + sprintf (intbuf, "%jd", blocking_factor); setenv ("TAR_BLOCKING_FACTOR", intbuf, 1); setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1); setenv ("TAR_FORMAT", @@ -922,7 +922,7 @@ sys_exec_checkpoint_script (const char *script_name, char intbuf[INT_BUFSIZE_BOUND (intmax_t)]; sprintf (intbuf, "%jd", checkpoint_number); setenv ("TAR_CHECKPOINT", intbuf, 1); - sprintf (intbuf, "%d", blocking_factor); + sprintf (intbuf, "%td", blocking_factor); setenv ("TAR_BLOCKING_FACTOR", intbuf, 1); setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1); setenv ("TAR_FORMAT", diff --git a/src/tar.c b/src/tar.c index 595ec8b7..ca8860bb 100644 --- a/src/tar.c +++ b/src/tar.c @@ -34,8 +34,8 @@ /* Define variables declared in common.h that belong to tar.c. */ enum subcommand subcommand_option; enum archive_format archive_format; -int blocking_factor; -size_t record_size; +idx_t blocking_factor; +idx_t record_size; bool absolute_names_option; bool utc_option; bool full_time_option; @@ -1502,7 +1502,8 @@ parse_opt (int key, char *arg, struct argp_state *state) if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK && !ckd_add (&blocking_factor, u, 0) && 0 < blocking_factor - && !ckd_mul (&record_size, u, BLOCKSIZE))) + && !ckd_mul (&record_size, u, BLOCKSIZE) + && record_size <= min (SSIZE_MAX, SIZE_MAX))) USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), _("Invalid blocking factor"))); } @@ -2124,7 +2125,8 @@ parse_opt (int key, char *arg, struct argp_state *state) uintmax_t u; if (! (xstrtoumax (arg, NULL, 10, &u, TAR_SIZE_SUFFIXES) == LONGINT_OK - && !ckd_add (&record_size, u, 0))) + && !ckd_add (&record_size, u, 0) + && record_size <= min (SSIZE_MAX, SIZE_MAX))) USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), _("Invalid record size"))); if (record_size % BLOCKSIZE != 0)