mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-24 20:28:28 +08:00
Archive sanity checks
Adds some sanity checking to size values read from file. * archive.c (do_slurp_bsd_armap): Increase minimum parsed_size, and bfd_set_error on failing test. Don't bother changing bfd_error on file read error. Check symdef_count is multiple of BSD_SYMDEF_SIZE. Check sym name is within string buffer. Use size_t for some vars. (do_slurp_coff_armap): Use size_t for some variables, fix size of int_buf. Don't change bfd_error on file read error. Use _bfd_mul_overflow when calculating carsym buffer size. Reorder calculations to catch overflows before they occur. malloc and free raw armap rather than using bfd_alloc. Read raw armap before allocating carsym+strings buffer. (_bfd_slurp_extended_name_table): Localize variables. Check name size against file size.
This commit is contained in:
@ -1,3 +1,18 @@
|
|||||||
|
2020-02-26 Alan Modra <amodra@gmail.com>
|
||||||
|
|
||||||
|
* archive.c (do_slurp_bsd_armap): Increase minimum parsed_size, and
|
||||||
|
bfd_set_error on failing test. Don't bother changing bfd_error on
|
||||||
|
file read error. Check symdef_count is multiple of BSD_SYMDEF_SIZE.
|
||||||
|
Check sym name is within string buffer. Use size_t for some vars.
|
||||||
|
(do_slurp_coff_armap): Use size_t for some variables, fix size of
|
||||||
|
int_buf. Don't change bfd_error on file read error. Use
|
||||||
|
_bfd_mul_overflow when calculating carsym buffer size. Reorder
|
||||||
|
calculations to catch overflows before they occur. malloc and
|
||||||
|
free raw armap rather than using bfd_alloc. Read raw armap before
|
||||||
|
allocating carsym+strings buffer.
|
||||||
|
(_bfd_slurp_extended_name_table): Localize variables. Check
|
||||||
|
name size against file size.
|
||||||
|
|
||||||
2020-02-26 Alan Modra <amodra@gmail.com>
|
2020-02-26 Alan Modra <amodra@gmail.com>
|
||||||
|
|
||||||
* vms-lib.c (vms_lib_read_index): Release correct buffer.
|
* vms-lib.c (vms_lib_read_index): Release correct buffer.
|
||||||
|
156
bfd/archive.c
156
bfd/archive.c
@ -951,11 +951,12 @@ static bfd_boolean
|
|||||||
do_slurp_bsd_armap (bfd *abfd)
|
do_slurp_bsd_armap (bfd *abfd)
|
||||||
{
|
{
|
||||||
struct areltdata *mapdata;
|
struct areltdata *mapdata;
|
||||||
unsigned int counter;
|
size_t counter;
|
||||||
bfd_byte *raw_armap, *rbase;
|
bfd_byte *raw_armap, *rbase;
|
||||||
struct artdata *ardata = bfd_ardata (abfd);
|
struct artdata *ardata = bfd_ardata (abfd);
|
||||||
char *stringbase;
|
char *stringbase;
|
||||||
bfd_size_type parsed_size, amt;
|
bfd_size_type parsed_size;
|
||||||
|
size_t amt, string_size;
|
||||||
carsym *set;
|
carsym *set;
|
||||||
|
|
||||||
mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
|
mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
|
||||||
@ -965,44 +966,51 @@ do_slurp_bsd_armap (bfd *abfd)
|
|||||||
free (mapdata);
|
free (mapdata);
|
||||||
/* PR 17512: file: 883ff754. */
|
/* PR 17512: file: 883ff754. */
|
||||||
/* PR 17512: file: 0458885f. */
|
/* PR 17512: file: 0458885f. */
|
||||||
if (parsed_size < 4)
|
if (parsed_size < BSD_SYMDEF_COUNT_SIZE + BSD_STRING_COUNT_SIZE)
|
||||||
return FALSE;
|
{
|
||||||
|
bfd_set_error (bfd_error_malformed_archive);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
raw_armap = (bfd_byte *) _bfd_alloc_and_read (abfd, parsed_size, parsed_size);
|
raw_armap = (bfd_byte *) _bfd_alloc_and_read (abfd, parsed_size, parsed_size);
|
||||||
if (raw_armap == NULL)
|
if (raw_armap == NULL)
|
||||||
{
|
return FALSE;
|
||||||
if (bfd_get_error () != bfd_error_system_call)
|
|
||||||
bfd_set_error (bfd_error_malformed_archive);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ardata->symdef_count = H_GET_32 (abfd, raw_armap) / BSD_SYMDEF_SIZE;
|
parsed_size -= BSD_SYMDEF_COUNT_SIZE + BSD_STRING_COUNT_SIZE;
|
||||||
if (ardata->symdef_count * BSD_SYMDEF_SIZE >
|
amt = H_GET_32 (abfd, raw_armap);
|
||||||
parsed_size - BSD_SYMDEF_COUNT_SIZE)
|
if (amt > parsed_size
|
||||||
|
|| amt % BSD_SYMDEF_SIZE != 0)
|
||||||
{
|
{
|
||||||
/* Probably we're using the wrong byte ordering. */
|
/* Probably we're using the wrong byte ordering. */
|
||||||
bfd_set_error (bfd_error_wrong_format);
|
bfd_set_error (bfd_error_wrong_format);
|
||||||
bfd_release (abfd, raw_armap);
|
goto release_armap;
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rbase = raw_armap + BSD_SYMDEF_COUNT_SIZE;
|
rbase = raw_armap + BSD_SYMDEF_COUNT_SIZE;
|
||||||
stringbase = ((char *) rbase
|
stringbase = (char *) rbase + amt + BSD_STRING_COUNT_SIZE;
|
||||||
+ ardata->symdef_count * BSD_SYMDEF_SIZE
|
string_size = parsed_size - amt;
|
||||||
+ BSD_STRING_COUNT_SIZE);
|
|
||||||
amt = ardata->symdef_count * sizeof (carsym);
|
ardata->symdef_count = amt / BSD_SYMDEF_SIZE;
|
||||||
|
if (_bfd_mul_overflow (ardata->symdef_count, sizeof (carsym), &amt))
|
||||||
|
{
|
||||||
|
bfd_set_error (bfd_error_no_memory);
|
||||||
|
goto release_armap;
|
||||||
|
}
|
||||||
ardata->symdefs = (struct carsym *) bfd_alloc (abfd, amt);
|
ardata->symdefs = (struct carsym *) bfd_alloc (abfd, amt);
|
||||||
if (!ardata->symdefs)
|
if (!ardata->symdefs)
|
||||||
{
|
goto release_armap;
|
||||||
bfd_release (abfd, raw_armap);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (counter = 0, set = ardata->symdefs;
|
for (counter = 0, set = ardata->symdefs;
|
||||||
counter < ardata->symdef_count;
|
counter < ardata->symdef_count;
|
||||||
counter++, set++, rbase += BSD_SYMDEF_SIZE)
|
counter++, set++, rbase += BSD_SYMDEF_SIZE)
|
||||||
{
|
{
|
||||||
set->name = H_GET_32 (abfd, rbase) + stringbase;
|
unsigned nameoff = H_GET_32 (abfd, rbase);
|
||||||
|
if (nameoff >= string_size)
|
||||||
|
{
|
||||||
|
bfd_set_error (bfd_error_malformed_archive);
|
||||||
|
goto release_armap;
|
||||||
|
}
|
||||||
|
set->name = stringbase + nameoff;
|
||||||
set->file_offset = H_GET_32 (abfd, rbase + BSD_SYMDEF_OFFSET_SIZE);
|
set->file_offset = H_GET_32 (abfd, rbase + BSD_SYMDEF_OFFSET_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1014,6 +1022,12 @@ do_slurp_bsd_armap (bfd *abfd)
|
|||||||
to be allocated on an objalloc anyway... */
|
to be allocated on an objalloc anyway... */
|
||||||
abfd->has_armap = TRUE;
|
abfd->has_armap = TRUE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
release_armap:
|
||||||
|
ardata->symdef_count = 0;
|
||||||
|
ardata->symdefs = NULL;
|
||||||
|
bfd_release (abfd, raw_armap);
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read a COFF archive symbol table. Returns FALSE on error, TRUE
|
/* Read a COFF archive symbol table. Returns FALSE on error, TRUE
|
||||||
@ -1029,12 +1043,12 @@ do_slurp_coff_armap (bfd *abfd)
|
|||||||
char *stringend;
|
char *stringend;
|
||||||
bfd_size_type stringsize;
|
bfd_size_type stringsize;
|
||||||
bfd_size_type parsed_size;
|
bfd_size_type parsed_size;
|
||||||
|
ufile_ptr filesize;
|
||||||
|
size_t nsymz, carsym_size, ptrsize, i;
|
||||||
carsym *carsyms;
|
carsym *carsyms;
|
||||||
bfd_size_type nsymz; /* Number of symbols in armap. */
|
|
||||||
bfd_vma (*swap) (const void *);
|
bfd_vma (*swap) (const void *);
|
||||||
char int_buf[sizeof (long)];
|
char int_buf[4];
|
||||||
bfd_size_type carsym_size, ptrsize;
|
struct areltdata *tmp;
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
|
mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
|
||||||
if (mapdata == NULL)
|
if (mapdata == NULL)
|
||||||
@ -1043,28 +1057,33 @@ do_slurp_coff_armap (bfd *abfd)
|
|||||||
free (mapdata);
|
free (mapdata);
|
||||||
|
|
||||||
if (bfd_bread (int_buf, 4, abfd) != 4)
|
if (bfd_bread (int_buf, 4, abfd) != 4)
|
||||||
{
|
return FALSE;
|
||||||
if (bfd_get_error () != bfd_error_system_call)
|
|
||||||
bfd_set_error (bfd_error_malformed_archive);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* It seems that all numeric information in a coff archive is always
|
/* It seems that all numeric information in a coff archive is always
|
||||||
in big endian format, nomatter the host or target. */
|
in big endian format, no matter the host or target. */
|
||||||
swap = bfd_getb32;
|
swap = bfd_getb32;
|
||||||
nsymz = bfd_getb32 (int_buf);
|
nsymz = bfd_getb32 (int_buf);
|
||||||
stringsize = parsed_size - (4 * nsymz) - 4;
|
|
||||||
|
|
||||||
/* The coff armap must be read sequentially. So we construct a
|
/* The coff armap must be read sequentially. So we construct a
|
||||||
bsd-style one in core all at once, for simplicity. */
|
bsd-style one in core all at once, for simplicity. */
|
||||||
|
|
||||||
if (nsymz > ~ (bfd_size_type) 0 / sizeof (carsym))
|
if (_bfd_mul_overflow (nsymz, sizeof (carsym), &carsym_size))
|
||||||
{
|
{
|
||||||
bfd_set_error (bfd_error_no_memory);
|
bfd_set_error (bfd_error_no_memory);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
carsym_size = (nsymz * sizeof (carsym));
|
filesize = bfd_get_file_size (abfd);
|
||||||
ptrsize = (4 * nsymz);
|
ptrsize = 4 * nsymz;
|
||||||
|
if ((filesize != 0 && parsed_size > filesize)
|
||||||
|
|| parsed_size < 4
|
||||||
|
|| parsed_size - 4 < ptrsize)
|
||||||
|
{
|
||||||
|
bfd_set_error (bfd_error_malformed_archive);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringsize = parsed_size - ptrsize - 4;
|
||||||
|
|
||||||
if (carsym_size + stringsize + 1 <= carsym_size)
|
if (carsym_size + stringsize + 1 <= carsym_size)
|
||||||
{
|
{
|
||||||
@ -1072,22 +1091,20 @@ do_slurp_coff_armap (bfd *abfd)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Allocate and read in the raw offsets. */
|
||||||
|
raw_armap = (int *) _bfd_malloc_and_read (abfd, ptrsize, ptrsize);
|
||||||
|
if (raw_armap == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
ardata->symdefs = (struct carsym *) bfd_alloc (abfd,
|
ardata->symdefs = (struct carsym *) bfd_alloc (abfd,
|
||||||
carsym_size + stringsize + 1);
|
carsym_size + stringsize + 1);
|
||||||
if (ardata->symdefs == NULL)
|
if (ardata->symdefs == NULL)
|
||||||
return FALSE;
|
goto free_armap;
|
||||||
carsyms = ardata->symdefs;
|
carsyms = ardata->symdefs;
|
||||||
stringbase = ((char *) ardata->symdefs) + carsym_size;
|
stringbase = ((char *) ardata->symdefs) + carsym_size;
|
||||||
|
|
||||||
/* Allocate and read in the raw offsets. */
|
if (bfd_bread (stringbase, stringsize, abfd) != stringsize)
|
||||||
raw_armap = (int *) _bfd_alloc_and_read (abfd, ptrsize, ptrsize);
|
goto release_symdefs;
|
||||||
if (raw_armap == NULL
|
|
||||||
|| (bfd_bread (stringbase, stringsize, abfd) != stringsize))
|
|
||||||
{
|
|
||||||
if (bfd_get_error () != bfd_error_system_call)
|
|
||||||
bfd_set_error (bfd_error_malformed_archive);
|
|
||||||
goto release_symdefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OK, build the carsyms. */
|
/* OK, build the carsyms. */
|
||||||
stringend = stringbase + stringsize;
|
stringend = stringbase + stringsize;
|
||||||
@ -1107,32 +1124,29 @@ do_slurp_coff_armap (bfd *abfd)
|
|||||||
ardata->first_file_filepos = bfd_tell (abfd);
|
ardata->first_file_filepos = bfd_tell (abfd);
|
||||||
/* Pad to an even boundary if you have to. */
|
/* Pad to an even boundary if you have to. */
|
||||||
ardata->first_file_filepos += (ardata->first_file_filepos) % 2;
|
ardata->first_file_filepos += (ardata->first_file_filepos) % 2;
|
||||||
|
if (bfd_seek (abfd, ardata->first_file_filepos, SEEK_SET) != 0)
|
||||||
|
goto release_symdefs;
|
||||||
|
|
||||||
abfd->has_armap = TRUE;
|
abfd->has_armap = TRUE;
|
||||||
bfd_release (abfd, raw_armap);
|
free (raw_armap);
|
||||||
|
|
||||||
/* Check for a second archive header (as used by PE). */
|
/* Check for a second archive header (as used by PE). */
|
||||||
{
|
tmp = (struct areltdata *) _bfd_read_ar_hdr (abfd);
|
||||||
struct areltdata *tmp;
|
if (tmp != NULL)
|
||||||
|
{
|
||||||
bfd_seek (abfd, ardata->first_file_filepos, SEEK_SET);
|
if (tmp->arch_header[0] == '/'
|
||||||
tmp = (struct areltdata *) _bfd_read_ar_hdr (abfd);
|
&& tmp->arch_header[1] == ' ')
|
||||||
if (tmp != NULL)
|
ardata->first_file_filepos
|
||||||
{
|
+= (tmp->parsed_size + sizeof (struct ar_hdr) + 1) & ~(unsigned) 1;
|
||||||
if (tmp->arch_header[0] == '/'
|
free (tmp);
|
||||||
&& tmp->arch_header[1] == ' ')
|
}
|
||||||
{
|
|
||||||
ardata->first_file_filepos +=
|
|
||||||
(tmp->parsed_size + sizeof (struct ar_hdr) + 1) & ~(unsigned) 1;
|
|
||||||
}
|
|
||||||
free (tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
release_symdefs:
|
release_symdefs:
|
||||||
bfd_release (abfd, (ardata)->symdefs);
|
bfd_release (abfd, (ardata)->symdefs);
|
||||||
|
free_armap:
|
||||||
|
free (raw_armap);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1209,8 +1223,6 @@ bfd_boolean
|
|||||||
_bfd_slurp_extended_name_table (bfd *abfd)
|
_bfd_slurp_extended_name_table (bfd *abfd)
|
||||||
{
|
{
|
||||||
char nextname[17];
|
char nextname[17];
|
||||||
struct areltdata *namedata;
|
|
||||||
bfd_size_type amt;
|
|
||||||
|
|
||||||
/* FIXME: Formatting sucks here, and in case of failure of BFD_READ,
|
/* FIXME: Formatting sucks here, and in case of failure of BFD_READ,
|
||||||
we probably don't want to return TRUE. */
|
we probably don't want to return TRUE. */
|
||||||
@ -1219,6 +1231,10 @@ _bfd_slurp_extended_name_table (bfd *abfd)
|
|||||||
|
|
||||||
if (bfd_bread (nextname, 16, abfd) == 16)
|
if (bfd_bread (nextname, 16, abfd) == 16)
|
||||||
{
|
{
|
||||||
|
struct areltdata *namedata;
|
||||||
|
bfd_size_type amt;
|
||||||
|
ufile_ptr filesize;
|
||||||
|
|
||||||
if (bfd_seek (abfd, (file_ptr) -16, SEEK_CUR) != 0)
|
if (bfd_seek (abfd, (file_ptr) -16, SEEK_CUR) != 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -1234,9 +1250,13 @@ _bfd_slurp_extended_name_table (bfd *abfd)
|
|||||||
if (namedata == NULL)
|
if (namedata == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
filesize = bfd_get_file_size (abfd);
|
||||||
amt = namedata->parsed_size;
|
amt = namedata->parsed_size;
|
||||||
if (amt + 1 == 0)
|
if (amt + 1 == 0 || (filesize != 0 && amt > filesize))
|
||||||
goto byebye;
|
{
|
||||||
|
bfd_set_error (bfd_error_malformed_archive);
|
||||||
|
goto byebye;
|
||||||
|
}
|
||||||
|
|
||||||
bfd_ardata (abfd)->extended_names_size = amt;
|
bfd_ardata (abfd)->extended_names_size = amt;
|
||||||
bfd_ardata (abfd)->extended_names = (char *) bfd_alloc (abfd, amt + 1);
|
bfd_ardata (abfd)->extended_names = (char *) bfd_alloc (abfd, amt + 1);
|
||||||
|
Reference in New Issue
Block a user