]> git.ipfire.org Git - thirdparty/tar.git/commitdiff
Handle enormous record sizes better
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 8 Aug 2024 17:51:39 +0000 (10:51 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 15 Aug 2024 06:25:46 +0000 (23:25 -0700)
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.

src/buffer.c
src/common.h
src/delete.c
src/system.c
src/tar.c

index 92d187c5455f7ad4d1b7f1f956e490b654d03e0d..e7f1e26483ff2b80d3cbde254f24d15a9cc7506b 100644 (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));
         }
index 135e6f3e3f25a1822b7a8a1246ff036c52ff56fa..0c1b139e9414b89d3616d3e2bad02922dbc1f0b1 100644 (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;
 
index 4223f95c033ee3a89dfa589b7bb7e51c8f3546cf..069c47fdbf165f04c351f9e7c4dd0c8a7504265f 100644 (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);
index 3ec4e993b2ffc5f51698ed8f383caae1875220a8..b7976a89cedf0930434516ba790086fd17526ba3 100644 (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",
index 595ec8b7eb43796c36ebb1f36a4a07f845080b87..ca8860bbbf5712ec9c3e639fcd86eefdd8386593 100644 (file)
--- 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)