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.
This commit is contained in:
Paul Eggert
2024-08-08 10:51:39 -07:00
parent eb9bb9bf80
commit 3ffe2eb073
5 changed files with 17 additions and 15 deletions

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -23,7 +23,7 @@
#include <rmt.h>
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);

View File

@@ -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",

View File

@@ -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)