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.
This commit is contained in:
Paul Eggert
2010-08-24 16:50:31 -07:00
parent 57b11473b0
commit a59c819beb
7 changed files with 25 additions and 36 deletions

View File

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

View File

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

View File

@@ -440,12 +440,6 @@ off_to_chars (off_t v, char *p, size_t s)
return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");
}
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)
{

View File

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

View File

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

View File

@@ -265,7 +265,7 @@ enum archive_format
struct sp_array
{
off_t offset;
size_t numbytes;
off_t numbytes;
};
struct xheader

View File

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