USE_MMAP fuzzed object file attacks

If mmap is used without sanity checking, then we'll get a SIGBUS if
an access is done to the mmap'd memory corresponding to a page past
end of file.

	* aoutx.h (aout_get_external_symbols): Check that mmap regions
	are within file contents.  Catch stringsize overflow.
	(some_aout_object_p): Don't clear already zeroed fields.  Tidy.
	* pdp11.c: As for aoutx.h.  Copy some fixes too.
This commit is contained in:
Alan Modra
2024-04-04 07:51:47 +10:30
parent 7e217ee2c0
commit b86d3af60f
2 changed files with 84 additions and 77 deletions

View File

@@ -498,9 +498,9 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
{ {
struct aout_data_struct *rawptr, *oldrawptr; struct aout_data_struct *rawptr, *oldrawptr;
bfd_cleanup result; bfd_cleanup result;
size_t amt = sizeof (* rawptr); size_t amt = sizeof (*rawptr);
rawptr = (struct aout_data_struct *) bfd_zalloc (abfd, amt); rawptr = bfd_zalloc (abfd, amt);
if (rawptr == NULL) if (rawptr == NULL)
return NULL; return NULL;
@@ -551,7 +551,6 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
abfd->start_address = execp->a_entry; abfd->start_address = execp->a_entry;
obj_aout_symbols (abfd) = NULL;
abfd->symcount = execp->a_syms / sizeof (struct external_nlist); abfd->symcount = execp->a_syms / sizeof (struct external_nlist);
/* The default relocation entry size is that of traditional V7 Unix. */ /* The default relocation entry size is that of traditional V7 Unix. */
@@ -564,9 +563,6 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
bfd_init_window (&obj_aout_sym_window (abfd)); bfd_init_window (&obj_aout_sym_window (abfd));
bfd_init_window (&obj_aout_string_window (abfd)); bfd_init_window (&obj_aout_string_window (abfd));
#endif #endif
obj_aout_external_syms (abfd) = NULL;
obj_aout_external_strings (abfd) = NULL;
obj_aout_sym_hashes (abfd) = NULL;
if (! NAME (aout, make_sections) (abfd)) if (! NAME (aout, make_sections) (abfd))
goto error_ret; goto error_ret;
@@ -594,7 +590,6 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
/* Call back to the format-dependent code to fill in the rest of the /* Call back to the format-dependent code to fill in the rest of the
fields and do any further cleanup. Things that should be filled fields and do any further cleanup. Things that should be filled
in by the callback: */ in by the callback: */
struct exec *execp = exec_hdr (abfd); struct exec *execp = exec_hdr (abfd);
obj_textsec (abfd)->size = N_TXTSIZE (execp); obj_textsec (abfd)->size = N_TXTSIZE (execp);
@@ -618,18 +613,13 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
obj_sym_filepos (abfd) = N_SYMOFF (execp); obj_sym_filepos (abfd) = N_SYMOFF (execp);
/* Determine the architecture and machine type of the object file. */ /* Determine the architecture and machine type of the object file. */
switch (N_MACHTYPE (exec_hdr (abfd))) abfd->obj_arch = bfd_arch_obscure;
{
default:
abfd->obj_arch = bfd_arch_obscure;
break;
}
adata (abfd)->page_size = TARGET_PAGE_SIZE; adata (abfd)->page_size = TARGET_PAGE_SIZE;
adata (abfd)->segment_size = SEGMENT_SIZE; adata (abfd)->segment_size = SEGMENT_SIZE;
adata (abfd)->exec_bytes_size = EXEC_BYTES_SIZE; adata (abfd)->exec_bytes_size = EXEC_BYTES_SIZE;
return _bfd_no_cleanup return _bfd_no_cleanup;
/* The architecture is encoded in various ways in various a.out variants, /* The architecture is encoded in various ways in various a.out variants,
or is not encoded at all in some of them. The relocation size depends or is not encoded at all in some of them. The relocation size depends
@@ -639,7 +629,7 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
Formats such as b.out, which have additional fields in the a.out Formats such as b.out, which have additional fields in the a.out
header, should cope with them in this callback as well. */ header, should cope with them in this callback as well. */
#endif /* DOCUMENTATION */ #endif /* DOCUMENTATION */
result = (*callback_to_real_object_p) (abfd); result = (*callback_to_real_object_p) (abfd);
@@ -1311,30 +1301,41 @@ NAME (aout, set_section_contents) (bfd *abfd,
static bool static bool
aout_get_external_symbols (bfd *abfd) aout_get_external_symbols (bfd *abfd)
{ {
#ifdef USE_MMAP
ufile_ptr filesize = bfd_get_file_size (abfd);
#endif
if (obj_aout_external_syms (abfd) == NULL) if (obj_aout_external_syms (abfd) == NULL)
{ {
bfd_size_type count; bfd_size_type count;
struct external_nlist *syms; struct external_nlist *syms = NULL;
bfd_size_type amt = exec_hdr (abfd)->a_syms; bfd_size_type amt = exec_hdr (abfd)->a_syms;
count = amt / EXTERNAL_NLIST_SIZE; count = amt / EXTERNAL_NLIST_SIZE;
if (count == 0) if (count == 0)
return true; /* Nothing to do. */ return true;
#ifdef USE_MMAP #ifdef USE_MMAP
if (! bfd_get_file_window (abfd, obj_sym_filepos (abfd), amt, if (filesize >= (ufile_ptr) obj_sym_filepos (abfd)
&obj_aout_sym_window (abfd), true)) && filesize - obj_sym_filepos (abfd) >= amt)
return false; {
syms = (struct external_nlist *) obj_aout_sym_window (abfd).data; if (! bfd_get_file_window (abfd, obj_sym_filepos (abfd), amt,
&obj_aout_sym_window (abfd), true))
return false;
syms = (struct external_nlist *) obj_aout_sym_window (abfd).data;
}
else
#else #else
/* We allocate using malloc to make the values easy to free {
later on. If we put them on the objalloc it might not be /* We allocate using malloc to make the values easy to free
possible to free them. */ later on. If we put them on the objalloc it might not be
if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0) possible to free them. */
return false; if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0)
syms = (struct external_nlist *) _bfd_malloc_and_read (abfd, amt, amt); return false;
if (syms == NULL) syms = (struct external_nlist *) _bfd_malloc_and_read (abfd, amt, amt);
return false; if (syms == NULL)
return false;
}
#endif #endif
obj_aout_external_syms (abfd) = syms; obj_aout_external_syms (abfd) = syms;
@@ -1356,7 +1357,7 @@ aout_get_external_symbols (bfd *abfd)
stringsize = GET_WORD (abfd, string_chars); stringsize = GET_WORD (abfd, string_chars);
if (stringsize == 0) if (stringsize == 0)
stringsize = 1; stringsize = 1;
else if (stringsize < BYTES_IN_WORD else if (stringsize + 1 < BYTES_IN_WORD + 1
|| (size_t) stringsize != stringsize) || (size_t) stringsize != stringsize)
{ {
bfd_set_error (bfd_error_bad_value); bfd_set_error (bfd_error_bad_value);
@@ -1364,7 +1365,9 @@ aout_get_external_symbols (bfd *abfd)
} }
#ifdef USE_MMAP #ifdef USE_MMAP
if (stringsize >= BYTES_IN_WORD) if (stringsize >= BYTES_IN_WORD
&& filesize >= (ufile_ptr) obj_str_filepos (abfd)
&& filesize - obj_str_filepos (abfd) >= stringsize + 1)
{ {
if (! bfd_get_file_window (abfd, obj_str_filepos (abfd), stringsize + 1, if (! bfd_get_file_window (abfd, obj_str_filepos (abfd), stringsize + 1,
&obj_aout_string_window (abfd), true)) &obj_aout_string_window (abfd), true))

View File

@@ -534,12 +534,12 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
bfd_cleanup (*callback_to_real_object_p) (bfd *)) bfd_cleanup (*callback_to_real_object_p) (bfd *))
{ {
struct aout_data_struct *rawptr, *oldrawptr; struct aout_data_struct *rawptr, *oldrawptr;
bfd_cleanup cleanup; bfd_cleanup result;
size_t amt = sizeof (struct aout_data_struct); size_t amt = sizeof (*rawptr);
rawptr = bfd_zalloc (abfd, amt); rawptr = bfd_zalloc (abfd, amt);
if (rawptr == NULL) if (rawptr == NULL)
return 0; return NULL;
oldrawptr = abfd->tdata.aout_data; oldrawptr = abfd->tdata.aout_data;
abfd->tdata.aout_data = rawptr; abfd->tdata.aout_data = rawptr;
@@ -549,7 +549,8 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
*abfd->tdata.aout_data = *oldrawptr; *abfd->tdata.aout_data = *oldrawptr;
abfd->tdata.aout_data->a.hdr = &rawptr->e; abfd->tdata.aout_data->a.hdr = &rawptr->e;
*(abfd->tdata.aout_data->a.hdr) = *execp; /* Copy in the internal_exec struct. */ /* Copy in the internal_exec struct. */
*(abfd->tdata.aout_data->a.hdr) = *execp;
execp = abfd->tdata.aout_data->a.hdr; execp = abfd->tdata.aout_data->a.hdr;
/* Set the file flags. */ /* Set the file flags. */
@@ -585,7 +586,6 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
abfd->start_address = execp->a_entry; abfd->start_address = execp->a_entry;
obj_aout_symbols (abfd) = NULL;
abfd->symcount = execp->a_syms / sizeof (struct external_nlist); abfd->symcount = execp->a_syms / sizeof (struct external_nlist);
/* The default relocation entry size is that of traditional V7 Unix. */ /* The default relocation entry size is that of traditional V7 Unix. */
@@ -599,12 +599,8 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
bfd_init_window (&obj_aout_string_window (abfd)); bfd_init_window (&obj_aout_string_window (abfd));
#endif #endif
obj_aout_external_syms (abfd) = NULL;
obj_aout_external_strings (abfd) = NULL;
obj_aout_sym_hashes (abfd) = NULL;
if (! NAME (aout, make_sections) (abfd)) if (! NAME (aout, make_sections) (abfd))
return NULL; goto error_ret;
obj_datasec (abfd)->size = execp->a_data; obj_datasec (abfd)->size = execp->a_data;
obj_bsssec (abfd)->size = execp->a_bss; obj_bsssec (abfd)->size = execp->a_bss;
@@ -654,9 +650,9 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
/* Determine the architecture and machine type of the object file. */ /* Determine the architecture and machine type of the object file. */
abfd->obj_arch = bfd_arch_obscure; abfd->obj_arch = bfd_arch_obscure;
adata(abfd)->page_size = TARGET_PAGE_SIZE; adata (abfd)->page_size = TARGET_PAGE_SIZE;
adata(abfd)->segment_size = SEGMENT_SIZE; adata (abfd)->segment_size = SEGMENT_SIZE;
adata(abfd)->exec_bytes_size = EXEC_BYTES_SIZE; adata (abfd)->exec_bytes_size = EXEC_BYTES_SIZE;
return _bfd_no_cleanup; return _bfd_no_cleanup;
@@ -670,7 +666,7 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
header, should cope with them in this callback as well. */ header, should cope with them in this callback as well. */
#endif /* DOCUMENTATION */ #endif /* DOCUMENTATION */
cleanup = (*callback_to_real_object_p)(abfd); result = (*callback_to_real_object_p) (abfd);
/* Now that the segment addresses have been worked out, take a better /* Now that the segment addresses have been worked out, take a better
guess at whether the file is executable. If the entry point guess at whether the file is executable. If the entry point
@@ -685,7 +681,7 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
To fix this, we now accept any non-zero entry point as an indication of To fix this, we now accept any non-zero entry point as an indication of
executability. This will work most of the time, since only the linker executability. This will work most of the time, since only the linker
sets the entry point, and that is likely to be non-zero for most systems. */ sets the entry point, and that is likely to be non-zero for most systems. */
if (execp->a_entry != 0 if (execp->a_entry != 0
|| (execp->a_entry >= obj_textsec (abfd)->vma || (execp->a_entry >= obj_textsec (abfd)->vma
@@ -708,18 +704,19 @@ NAME (aout, some_aout_object_p) (bfd *abfd,
issue. Many kernels are loaded at non standard addresses. */ issue. Many kernels are loaded at non standard addresses. */
if (abfd->iostream != NULL if (abfd->iostream != NULL
&& (abfd->flags & BFD_IN_MEMORY) == 0 && (abfd->flags & BFD_IN_MEMORY) == 0
&& (fstat(fileno((FILE *) (abfd->iostream)), &stat_buf) == 0) && (fstat (fileno ((FILE *) (abfd->iostream)), &stat_buf) == 0)
&& ((stat_buf.st_mode & 0111) != 0)) && ((stat_buf.st_mode & 0111) != 0))
abfd->flags |= EXEC_P; abfd->flags |= EXEC_P;
} }
#endif /* STAT_FOR_EXEC */ #endif /* STAT_FOR_EXEC */
if (!cleanup) if (result)
{ return result;
free (rawptr);
abfd->tdata.aout_data = oldrawptr; error_ret:
} bfd_release (abfd, rawptr);
return cleanup; abfd->tdata.aout_data = oldrawptr;
return NULL;
} }
/* Initialize ABFD for use with a.out files. */ /* Initialize ABFD for use with a.out files. */
@@ -1279,38 +1276,43 @@ NAME (aout, set_section_contents) (bfd *abfd,
static bool static bool
aout_get_external_symbols (bfd *abfd) aout_get_external_symbols (bfd *abfd)
{ {
#ifdef USE_MMAP
ufile_ptr filesize = bfd_get_file_size (abfd);
#endif
if (obj_aout_external_syms (abfd) == NULL) if (obj_aout_external_syms (abfd) == NULL)
{ {
bfd_size_type count; bfd_size_type count;
struct external_nlist *syms; struct external_nlist *syms = NULL;
bfd_size_type amt = exec_hdr (abfd)->a_syms;
count = exec_hdr (abfd)->a_syms / EXTERNAL_NLIST_SIZE; count = amt / EXTERNAL_NLIST_SIZE;
/* PR 17512: file: 011f5a08. */ /* PR 17512: file: 011f5a08. */
if (count == 0) if (count == 0)
{ return true;
obj_aout_external_syms (abfd) = NULL;
obj_aout_external_sym_count (abfd) = count;
return true;
}
#ifdef USE_MMAP #ifdef USE_MMAP
if (! bfd_get_file_window (abfd, obj_sym_filepos (abfd), if (filesize >= (ufile_ptr) obj_sym_filepos (abfd)
exec_hdr (abfd)->a_syms, && filesize - obj_sym_filepos (abfd) >= amt)
&obj_aout_sym_window (abfd), true)) {
return false; if (! bfd_get_file_window (abfd, obj_sym_filepos (abfd), amt,
syms = (struct external_nlist *) obj_aout_sym_window (abfd).data; &obj_aout_sym_window (abfd), true))
return false;
syms = (struct external_nlist *) obj_aout_sym_window (abfd).data;
}
else
#else #else
/* We allocate using malloc to make the values easy to free {
later on. If we put them on the objalloc it might not be /* We allocate using malloc to make the values easy to free
possible to free them. */ later on. If we put them on the objalloc it might not be
if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0) possible to free them. */
return false; if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0)
syms = (struct external_nlist *) return false;
_bfd_malloc_and_read (abfd, count * EXTERNAL_NLIST_SIZE, syms = (struct external_nlist *) _bfd_malloc_and_read (abfd, amt, amt);
count * EXTERNAL_NLIST_SIZE); if (syms == NULL)
if (syms == NULL) return false;
return false; }
#endif #endif
obj_aout_external_syms (abfd) = syms; obj_aout_external_syms (abfd) = syms;
@@ -1332,7 +1334,7 @@ aout_get_external_symbols (bfd *abfd)
stringsize = H_GET_32 (abfd, string_chars); stringsize = H_GET_32 (abfd, string_chars);
if (stringsize == 0) if (stringsize == 0)
stringsize = 1; stringsize = 1;
else if (stringsize < BYTES_IN_LONG else if (stringsize + 1 < BYTES_IN_LONG + 1
|| (size_t) stringsize != stringsize) || (size_t) stringsize != stringsize)
{ {
bfd_set_error (bfd_error_bad_value); bfd_set_error (bfd_error_bad_value);
@@ -1340,7 +1342,9 @@ aout_get_external_symbols (bfd *abfd)
} }
#ifdef USE_MMAP #ifdef USE_MMAP
if (stringsize >= BYTES_IN_LONG) if (stringsize >= BYTES_IN_LONG
&& filesize >= (ufile_ptr) obj_str_filepos (abfd)
&& filesize - obj_str_filepos (abfd) >= stringsize + 1)
{ {
if (! bfd_get_file_window (abfd, obj_str_filepos (abfd), stringsize + 1, if (! bfd_get_file_window (abfd, obj_str_filepos (abfd), stringsize + 1,
&obj_aout_string_window (abfd), true)) &obj_aout_string_window (abfd), true))