bug-tar
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-tar] [PATCH] tar: don't assume size of a sparse file chunk fits in


From: Paul Eggert
Subject: [Bug-tar] [PATCH] tar: don't assume size of a sparse file chunk fits in size_t
Date: Tue, 24 Aug 2010 16:57:14 -0700
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.11) Gecko/20100713 Thunderbird/3.0.6

I found this problem by code inspection, and installed the following patch.


>From f004d10f933a0858e881558ab2fbd3209b37c4c5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <address@hidden>
Date: Tue, 24 Aug 2010 16:50:31 -0700
Subject: [PATCH] tar: don't assume size of a sparse file chunk fits in size_t

* src/tar.h (struct sp_array): Change numbytes from size_t to off_t.
All uses changed.
* scripts/xsparse.c (struct sp_array): Likewise.
Include <stdint.h>, for SIZE_MAX.
(expand_sparse): Don't try to allocate a buffer bigger than
SIZE_MAX bytes.
* src/common.h (SIZE_TO_CHARS, size_to_chars, SIZE_FROM_HEADER):
(size_from_header): Remove decls.
* src/create.c (size_to_chars): Remove.
* src/list.c (size_from_header): Remove.
* src/sparse.c (sparse_extract_region, check_data_region):
(oldgnu_add_sparse, oldgnu_store_sparse_info, pax_decode_header):
Don't assume chunk sizes fit in size_t.
(oldgnu_add_sparse): Check for off_t overflow.
* src/xheader.c (sparse_numbytes_decoder, sparse_map_decoder):
Likewise.
---
 scripts/xsparse.c |   22 +++++++++++++---------
 src/common.h      |    4 ----
 src/create.c      |    6 ------
 src/list.c        |    7 -------
 src/sparse.c      |   16 +++++++++-------
 src/tar.h         |    2 +-
 src/xheader.c     |    4 ++--
 7 files changed, 25 insertions(+), 36 deletions(-)

diff --git a/scripts/xsparse.c b/scripts/xsparse.c
index fd91b48..14d5658 100644
--- a/scripts/xsparse.c
+++ b/scripts/xsparse.c
@@ -1,7 +1,7 @@
 /* xsparse - expands compressed sparse file images extracted from GNU tar
    archives.
 
-   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2007, 2010 Free Software Foundation, Inc.
 
    Written by Sergey Poznyakoff
 
@@ -20,6 +20,7 @@
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include <stdlib.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
@@ -39,7 +40,7 @@
 struct sp_array
 {
   off_t offset;
-  size_t numbytes;
+  off_t numbytes;
 };
 
 char *progname;
@@ -226,7 +227,7 @@ read_xheader (char *name)
        }
       else if (strcmp (kw, "numbytes") == 0)
        {
-         sparse_map[i++].numbytes = string_to_size (val, NULL);
+         sparse_map[i++].numbytes = string_to_off (val, NULL);
        }
       else if (strcmp (kw, "map") == 0)
        {
@@ -236,7 +237,7 @@ read_xheader (char *name)
              if (*val != ',')
                die (1, "bad GNU.sparse.map: expected `,' but found `%c'",
                     *val);
-             sparse_map[i].numbytes = string_to_size (val+1, &val);
+             sparse_map[i].numbytes = string_to_off (val+1, &val);
              if (*val != ',')
                {
                  if (!(*val == 0 && i == sparse_map_size-1))
@@ -277,7 +278,7 @@ read_map (FILE *ifp)
       get_line (nbuf, sizeof nbuf, ifp);
       sparse_map[i].offset = string_to_off (nbuf, NULL);
       get_line (nbuf, sizeof nbuf, ifp);
-      sparse_map[i].numbytes = string_to_size (nbuf, NULL);
+      sparse_map[i].numbytes = string_to_off (nbuf, NULL);
     }
 
   fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
@@ -288,12 +289,15 @@ void
 expand_sparse (FILE *sfp, int ofd)
 {
   size_t i;
-  size_t maxbytes = 0;
+  off_t max_numbytes = 0;
+  size_t maxbytes;
   char *buffer;
 
   for (i = 0; i < sparse_map_size; i++)
-    if (maxbytes < sparse_map[i].numbytes)
-      maxbytes = sparse_map[i].numbytes;
+    if (max_numbytes < sparse_map[i].numbytes)
+      max_numbytes = sparse_map[i].numbytes;
+
+  maxbytes = max_numbytes < SIZE_MAX ? max_numbytes : SIZE_MAX;
 
   for (buffer = malloc (maxbytes); !buffer; maxbytes /= 2)
     if (maxbytes == 0)
@@ -301,7 +305,7 @@ expand_sparse (FILE *sfp, int ofd)
 
   for (i = 0; i < sparse_map_size; i++)
     {
-      size_t size = sparse_map[i].numbytes;
+      off_t size = sparse_map[i].numbytes;
 
       if (size == 0)
        ftruncate (ofd, sparse_map[i].offset);
diff --git a/src/common.h b/src/common.h
index 9d0b779..f6f3844 100644
--- a/src/common.h
+++ b/src/common.h
@@ -467,11 +467,9 @@ enum exclusion_tag_type check_exclusion_tags (const char 
*dirname,
                                              const char **tag_file_name);
 
 #define OFF_TO_CHARS(val, where) off_to_chars (val, where, sizeof (where))
-#define SIZE_TO_CHARS(val, where) size_to_chars (val, where, sizeof (where))
 #define TIME_TO_CHARS(val, where) time_to_chars (val, where, sizeof (where))
 
 bool off_to_chars (off_t off, char *buf, size_t size);
-bool size_to_chars (size_t v, char *buf, size_t size);
 bool time_to_chars (time_t t, char *buf, size_t size);
 
 /* Module diffarch.c.  */
@@ -547,11 +545,9 @@ void decode_header (union block *header, struct 
tar_stat_info *stat_info,
 char const *tartime (struct timespec t, bool full_time);
 
 #define OFF_FROM_HEADER(where) off_from_header (where, sizeof (where))
-#define SIZE_FROM_HEADER(where) size_from_header (where, sizeof (where))
 #define UINTMAX_FROM_HEADER(where) uintmax_from_header (where, sizeof (where))
 
 off_t off_from_header (const char *buf, size_t size);
-size_t size_from_header (const char *buf, size_t size);
 uintmax_t uintmax_from_header (const char *buf, size_t size);
 
 void list_archive (void);
diff --git a/src/create.c b/src/create.c
index 0a6bacf..2ca1d6e 100644
--- a/src/create.c
+++ b/src/create.c
@@ -441,12 +441,6 @@ off_to_chars (off_t v, char *p, size_t s)
 }
 
 bool
-size_to_chars (size_t v, char *p, size_t s)
-{
-  return to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");
-}
-
-bool
 time_to_chars (time_t v, char *p, size_t s)
 {
   return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "time_t");
diff --git a/src/list.c b/src/list.c
index f0d3d5a..e1e5bf2 100644
--- a/src/list.c
+++ b/src/list.c
@@ -946,13 +946,6 @@ off_from_header (const char *p, size_t s)
                      (uintmax_t) TYPE_MAXIMUM (off_t), false, false);
 }
 
-size_t
-size_from_header (const char *p, size_t s)
-{
-  return from_header (p, s, "size_t", (uintmax_t) 0,
-                     (uintmax_t) TYPE_MAXIMUM (size_t), false, false);
-}
-
 static time_t
 time_from_header (const char *p, size_t s)
 {
diff --git a/src/sparse.c b/src/sparse.c
index 67e5a85..8af4b47 100644
--- a/src/sparse.c
+++ b/src/sparse.c
@@ -1,6 +1,7 @@
 /* Functions for dealing with sparse files
 
-   Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 Free Software
+   Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
@@ -333,7 +334,7 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
 static bool
 sparse_extract_region (struct tar_sparse_file *file, size_t i)
 {
-  size_t write_size;
+  off_t write_size;
 
   if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
@@ -508,7 +509,7 @@ check_sparse_region (struct tar_sparse_file *file, off_t 
beg, off_t end)
 static bool
 check_data_region (struct tar_sparse_file *file, size_t i)
 {
-  size_t size_left;
+  off_t size_left;
 
   if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
@@ -625,8 +626,9 @@ oldgnu_add_sparse (struct tar_sparse_file *file, struct 
sparse *s)
   if (s->numbytes[0] == '\0')
     return add_finish;
   sp.offset = OFF_FROM_HEADER (s->offset);
-  sp.numbytes = SIZE_FROM_HEADER (s->numbytes);
+  sp.numbytes = OFF_FROM_HEADER (s->numbytes);
   if (sp.offset < 0
+      || sp.offset + sp.numbytes < 0
       || file->stat_info->stat.st_size < sp.offset + sp.numbytes
       || file->stat_info->archive_file_size < 0)
     return add_fail;
@@ -695,8 +697,8 @@ oldgnu_store_sparse_info (struct tar_sparse_file *file, 
size_t *pindex,
     {
       OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].offset,
                    sp->offset);
-      SIZE_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
-                    sp->numbytes);
+      OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
+                   sp->numbytes);
     }
 }
 
@@ -1147,7 +1149,7 @@ pax_decode_header (struct tar_sparse_file *file)
            }
          sp.offset = u;
          COPY_BUF (blk,nbuf,p);
-         if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
+         if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
            {
              ERROR ((0, 0, _("%s: malformed sparse archive member"),
                      file->stat_info->orig_file_name));
diff --git a/src/tar.h b/src/tar.h
index 08ccab3..fddc83f 100644
--- a/src/tar.h
+++ b/src/tar.h
@@ -265,7 +265,7 @@ enum archive_format
 struct sp_array
 {
   off_t offset;
-  size_t numbytes;
+  off_t numbytes;
 };
 
 struct xheader
diff --git a/src/xheader.c b/src/xheader.c
index 42656bf..2284e97 100644
--- a/src/xheader.c
+++ b/src/xheader.c
@@ -1308,7 +1308,7 @@ sparse_numbytes_decoder (struct tar_stat_info *st,
                         size_t size __attribute__((unused)))
 {
   uintmax_t u;
-  if (decode_num (&u, arg, SIZE_MAX, keyword))
+  if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword))
     {
       if (st->sparse_map_avail < st->sparse_map_size)
        st->sparse_map[st->sparse_map_avail++].numbytes = u;
@@ -1356,7 +1356,7 @@ sparse_map_decoder (struct tar_stat_info *st,
          e.numbytes = u;
          if (!(u == e.numbytes && errno != ERANGE))
            {
-             out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (size_t));
+             out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t));
              return;
            }
          if (st->sparse_map_avail < st->sparse_map_size)
-- 
1.7.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]