diff --git a/smex/elf.c b/smex/elf.c index eb0994530c6b..7a2e66f14f6f 100644 --- a/smex/elf.c +++ b/smex/elf.c @@ -44,6 +44,24 @@ static int elf_read_sections(struct elf_module *module, bool verbose) return count > 0 ? -ENODATA : -errno; } + /* the string-table section index comes from the ELF header and is used + * to index section[]; reject an out-of-range value before dereferencing + */ + if (hdr->shstrndx >= hdr->shnum) { + fprintf(stderr, "error: %s invalid shstrndx %u >= shnum %u\n", + module->elf_file, hdr->shstrndx, hdr->shnum); + return -EINVAL; + } + + /* a zero-size string section leaves module->strings unusable and would + * break later string lookups; reject it explicitly + */ + if (section[hdr->shstrndx].size == 0) { + fprintf(stderr, "error: %s has zero-size string section\n", + module->elf_file); + return -EINVAL; + } + /* read in strings */ module->strings = calloc(1, section[hdr->shstrndx].size); if (!module->strings) { @@ -67,6 +85,18 @@ static int elf_read_sections(struct elf_module *module, bool verbose) return count > 0 ? -ENODATA : -errno; } + /* every section name is used as an offset into the string table; make + * sure each stays within it so later "module->strings + name" reads + * cannot run past the table + */ + for (i = 0; i < hdr->shnum; i++) { + if (section[i].name >= section[hdr->shstrndx].size) { + fprintf(stderr, "error: %s section %d name offset %u out of range\n", + module->elf_file, i, section[i].name); + return -EINVAL; + } + } + module->bss_index = elf_find_section(module, ".bss"); if (module->bss_index < 0) { fprintf(stderr, "Can't find .bss section in %s", @@ -392,8 +422,22 @@ int elf_find_section(const struct elf_module *module, const char *name) return -EINVAL; } + if (hdr->shstrndx >= hdr->shnum) { + fprintf(stderr, "error: invalid shstrndx %u >= shnum %u\n", + hdr->shstrndx, hdr->shnum); + return -EINVAL; + } + section = &module->section[hdr->shstrndx]; + /* a zero-size string section would make the buffer[size - 1] + * NUL-termination below write before the allocation + */ + if (section->size == 0) { + fprintf(stderr, "error: zero-size string section\n"); + return -EINVAL; + } + /* alloc data data */ buffer = calloc(1, section->size); if (!buffer) @@ -482,6 +526,10 @@ int elf_read_section(const struct elf_module *module, const char *section_name, error: free(*dst_buff); + /* clear the caller's pointer so a caller cleanup path (e.g. ldc.c's + * "if (buffer) free(buffer)") does not free the same buffer again + */ + *dst_buff = NULL; return ret; } diff --git a/smex/ldc.c b/smex/ldc.c index 4eccdb1c0918..e9f8b75d5983 100644 --- a/smex/ldc.c +++ b/smex/ldc.c @@ -26,6 +26,13 @@ static int fw_version_copy(const struct elf_module *src, if (section_size < 0) return section_size; + if ((size_t)section_size < sizeof(struct sof_ipc_fw_ready)) { + fprintf(stderr, "error: .fw_ready section too small: %d\n", + section_size); + free(buffer); + return -EINVAL; + } + memcpy(&header->version, &((struct sof_ipc_fw_ready *)buffer)->version, sizeof(header->version)); @@ -50,13 +57,22 @@ static int fw_version_copy(const struct elf_module *src, return section_size; ext_hdr = (struct ext_man_elem_header *)buffer; - while ((uintptr_t)ext_hdr < (uintptr_t)buffer + section_size) { + while ((uintptr_t)ext_hdr + sizeof(*ext_hdr) <= + (uintptr_t)buffer + section_size) { if (ext_hdr->type == EXT_MAN_ELEM_DBG_ABI) { header->version.abi_version = ((struct ext_man_dbg_abi *) ext_hdr)->dbg_abi.abi_dbg_version; break; } + /* elem_size must advance the cursor and keep the next header + * within the section; otherwise stop instead of looping + * forever (elem_size == 0) or reading past the section + */ + if (ext_hdr->elem_size == 0 || + (uintptr_t)ext_hdr + ext_hdr->elem_size > + (uintptr_t)buffer + section_size) + break; //move to the next entry ext_hdr = (struct ext_man_elem_header *) ((uint8_t *)ext_hdr + ext_hdr->elem_size);