From 3e6aa7751ab86fdc2f2762ed8a5bce41b22be56e Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Mon, 6 Jan 2020 10:01:55 +1030 Subject: [PATCH] Basic error checking for mach-o Fixes lots of places the fuzzers are going to find, and the one they already hit. * mach-o.c (bfd_mach_o_read_dylinker): Don't read past end of command. Check name offset is within command. (bfd_mach_o_read_dylib, bfd_mach_o_read_prebound_dylib), (bfd_mach_o_read_prebind_cksum, bfd_mach_o_read_twolevel_hints), (bfd_mach_o_read_fvmlib, bfd_mach_o_read_dysymtab), (bfd_mach_o_read_symtab, bfd_mach_o_read_uuid), (bfd_mach_o_read_linkedit, bfd_mach_o_read_str), (bfd_mach_o_read_dyld_info, bfd_mach_o_read_version_min), (bfd_mach_o_read_encryption_info, bfd_mach_o_read_source_version), (bfd_mach_o_read_encryption_info_64, bfd_mach_o_read_main), (bfd_mach_o_read_note, bfd_mach_o_read_build_version), (bfd_mach_o_read_segment): Similarly. (bfd_mach_o_read_thread): Properly bound check thread struct. Don't repeat checks on second loop. (bfd_mach_o_read_command): Fail on invalid command length. --- bfd/ChangeLog | 18 +++++++++++ bfd/mach-o.c | 83 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 148de794bc..1d2b346060 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,21 @@ +2020-01-06 Alan Modra + + * mach-o.c (bfd_mach_o_read_dylinker): Don't read past end of + command. Check name offset is within command. + (bfd_mach_o_read_dylib, bfd_mach_o_read_prebound_dylib), + (bfd_mach_o_read_prebind_cksum, bfd_mach_o_read_twolevel_hints), + (bfd_mach_o_read_fvmlib, bfd_mach_o_read_dysymtab), + (bfd_mach_o_read_symtab, bfd_mach_o_read_uuid), + (bfd_mach_o_read_linkedit, bfd_mach_o_read_str), + (bfd_mach_o_read_dyld_info, bfd_mach_o_read_version_min), + (bfd_mach_o_read_encryption_info, bfd_mach_o_read_source_version), + (bfd_mach_o_read_encryption_info_64, bfd_mach_o_read_main), + (bfd_mach_o_read_note, bfd_mach_o_read_build_version), + (bfd_mach_o_read_segment): Similarly. + (bfd_mach_o_read_thread): Properly bound check thread struct. + Don't repeat checks on second loop. + (bfd_mach_o_read_command): Fail on invalid command length. + 2020-01-04 Alan Modra * format.c (bfd_check_format_matches): Add preserve_match. diff --git a/bfd/mach-o.c b/bfd/mach-o.c index 3b6fbb5788..c1ef64eff0 100644 --- a/bfd/mach-o.c +++ b/bfd/mach-o.c @@ -3998,10 +3998,14 @@ bfd_mach_o_read_dylinker (bfd *abfd, bfd_mach_o_load_command *command) unsigned int nameoff; unsigned int namelen; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; nameoff = bfd_h_get_32 (abfd, raw.str); + if (nameoff > command->len) + return FALSE; cmd->name_offset = nameoff; namelen = command->len - nameoff; @@ -4024,6 +4028,8 @@ bfd_mach_o_read_dylib (bfd *abfd, bfd_mach_o_load_command *command) unsigned int nameoff; unsigned int namelen; + if (command->len < sizeof (raw) + 8) + return FALSE; switch (command->type) { case BFD_MACH_O_LC_LOAD_DYLIB: @@ -4042,6 +4048,8 @@ bfd_mach_o_read_dylib (bfd *abfd, bfd_mach_o_load_command *command) return FALSE; nameoff = bfd_h_get_32 (abfd, raw.name); + if (nameoff > command->len) + return FALSE; cmd->timestamp = bfd_h_get_32 (abfd, raw.timestamp); cmd->current_version = bfd_h_get_32 (abfd, raw.current_version); cmd->compatibility_version = bfd_h_get_32 (abfd, raw.compatibility_version); @@ -4068,6 +4076,8 @@ bfd_mach_o_read_prebound_dylib (bfd *abfd, unsigned int str_len; unsigned char *str; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4099,6 +4109,8 @@ bfd_mach_o_read_prebind_cksum (bfd *abfd, bfd_mach_o_prebind_cksum_command *cmd = &command->command.prebind_cksum; struct mach_o_prebind_cksum_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4113,6 +4125,8 @@ bfd_mach_o_read_twolevel_hints (bfd *abfd, bfd_mach_o_twolevel_hints_command *cmd = &command->command.twolevel_hints; struct mach_o_twolevel_hints_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4129,10 +4143,14 @@ bfd_mach_o_read_fvmlib (bfd *abfd, bfd_mach_o_load_command *command) unsigned int nameoff; unsigned int namelen; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; nameoff = bfd_h_get_32 (abfd, raw.name); + if (nameoff > command->len) + return FALSE; fvm->minor_version = bfd_h_get_32 (abfd, raw.minor_version); fvm->header_addr = bfd_h_get_32 (abfd, raw.header_addr); @@ -4155,6 +4173,7 @@ bfd_mach_o_read_thread (bfd *abfd, bfd_mach_o_load_command *command) unsigned int offset; unsigned int nflavours; unsigned int i; + struct mach_o_thread_command_external raw; BFD_ASSERT ((command->type == BFD_MACH_O_LC_THREAD) || (command->type == BFD_MACH_O_LC_UNIXTHREAD)); @@ -4162,24 +4181,27 @@ bfd_mach_o_read_thread (bfd *abfd, bfd_mach_o_load_command *command) /* Count the number of threads. */ offset = 8; nflavours = 0; - while (offset != command->len) + while (offset + sizeof (raw) <= command->len) { - struct mach_o_thread_command_external raw; - - if (offset >= command->len) - return FALSE; + unsigned int count; if (bfd_seek (abfd, command->offset + offset, SEEK_SET) != 0 || bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; - offset += sizeof (raw) + bfd_h_get_32 (abfd, raw.count) * 4; + count = bfd_h_get_32 (abfd, raw.count); + if (count > (unsigned) -1 / 4 + || command->len - (offset + sizeof (raw)) < count * 4) + return FALSE; + offset += sizeof (raw) + count * 4; nflavours++; } + if (nflavours == 0 || offset != command->len) + return FALSE; /* Allocate threads. */ - cmd->flavours = bfd_alloc2 - (abfd, nflavours, sizeof (bfd_mach_o_thread_flavour)); + cmd->flavours = bfd_alloc2 (abfd, nflavours, + sizeof (bfd_mach_o_thread_flavour)); if (cmd->flavours == NULL) return FALSE; cmd->nflavours = nflavours; @@ -4188,14 +4210,6 @@ bfd_mach_o_read_thread (bfd *abfd, bfd_mach_o_load_command *command) nflavours = 0; while (offset != command->len) { - struct mach_o_thread_command_external raw; - - if (offset >= command->len) - return FALSE; - - if (nflavours >= cmd->nflavours) - return FALSE; - if (bfd_seek (abfd, command->offset + offset, SEEK_SET) != 0 || bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4271,6 +4285,8 @@ bfd_mach_o_read_dysymtab (bfd *abfd, bfd_mach_o_load_command *command) { struct mach_o_dysymtab_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4447,6 +4463,8 @@ bfd_mach_o_read_symtab (bfd *abfd, bfd_mach_o_load_command *command) BFD_ASSERT (command->type == BFD_MACH_O_LC_SYMTAB); + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4473,6 +4491,8 @@ bfd_mach_o_read_uuid (bfd *abfd, bfd_mach_o_load_command *command) BFD_ASSERT (command->type == BFD_MACH_O_LC_UUID); + if (command->len < 16 + 8) + return FALSE; if (bfd_bread (cmd->uuid, 16, abfd) != 16) return FALSE; @@ -4485,6 +4505,8 @@ bfd_mach_o_read_linkedit (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_linkedit_command *cmd = &command->command.linkedit; struct mach_o_linkedit_data_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4500,10 +4522,15 @@ bfd_mach_o_read_str (bfd *abfd, bfd_mach_o_load_command *command) struct mach_o_str_command_external raw; unsigned long off; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; off = bfd_get_32 (abfd, raw.str); + if (off > command->len) + return FALSE; + cmd->stroff = command->offset + off; cmd->str_len = command->len - off; cmd->str = bfd_alloc (abfd, cmd->str_len); @@ -4586,6 +4613,8 @@ bfd_mach_o_read_dyld_info (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_dyld_info_command *cmd = &command->command.dyld_info; struct mach_o_dyld_info_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4613,6 +4642,8 @@ bfd_mach_o_read_version_min (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_version_min_command *cmd = &command->command.version_min; struct mach_o_version_min_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4627,6 +4658,8 @@ bfd_mach_o_read_encryption_info (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_encryption_info_command *cmd = &command->command.encryption_info; struct mach_o_encryption_info_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4642,6 +4675,8 @@ bfd_mach_o_read_encryption_info_64 (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_encryption_info_command *cmd = &command->command.encryption_info; struct mach_o_encryption_info_64_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4657,6 +4692,8 @@ bfd_mach_o_read_main (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_main_command *cmd = &command->command.main; struct mach_o_entry_point_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4672,6 +4709,8 @@ bfd_mach_o_read_source_version (bfd *abfd, bfd_mach_o_load_command *command) struct mach_o_source_version_command_external raw; bfd_uint64_t ver; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4697,6 +4736,8 @@ bfd_mach_o_read_note (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_note_command *cmd = &command->command.note; struct mach_o_note_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4712,6 +4753,8 @@ bfd_mach_o_read_build_version (bfd *abfd, bfd_mach_o_load_command *command) bfd_mach_o_build_version_command *cmd = &command->command.build_version; struct mach_o_build_version_command_external raw; + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4736,6 +4779,8 @@ bfd_mach_o_read_segment (bfd *abfd, BFD_ASSERT (command->type == BFD_MACH_O_LC_SEGMENT_64); + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4757,6 +4802,8 @@ bfd_mach_o_read_segment (bfd *abfd, BFD_ASSERT (command->type == BFD_MACH_O_LC_SEGMENT); + if (command->len < sizeof (raw) + 8) + return FALSE; if (bfd_bread (&raw, sizeof (raw), abfd) != sizeof (raw)) return FALSE; @@ -4815,9 +4862,11 @@ bfd_mach_o_read_command (bfd *abfd, bfd_mach_o_load_command *command) return FALSE; cmd = bfd_h_get_32 (abfd, raw.cmd); - command->type = cmd & ~BFD_MACH_O_LC_REQ_DYLD; + command->type = cmd & ~BFD_MACH_O_LC_REQ_DYLD; command->type_required = cmd & BFD_MACH_O_LC_REQ_DYLD ? TRUE : FALSE; command->len = bfd_h_get_32 (abfd, raw.cmdsize); + if (command->len < 8 || command->len % 4 != 0) + return FALSE; switch (command->type) { -- 2.34.1