libelf: treat phdr and shdr similarly Just like elf_shdr_count(), elf_phdr_count() better bounds checks the value. Add table entry size checks to elf_init(). Also both program and section headers are optional, and hence their checking better is done conditionally only when any such headers are present. Signed-off-by: Jan Beulich --- a/xen/common/libelf/libelf-loader.c +++ b/xen/common/libelf/libelf-loader.c @@ -52,24 +52,45 @@ elf_errorstatus elf_init(struct elf_bina elf->class = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_CLASS]); elf->data = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_DATA]); - /* Sanity check phdr. */ - offset = elf_uval(elf, elf->ehdr, e_phoff) + - elf_uval(elf, elf->ehdr, e_phentsize) * elf_phdr_count(elf); - if ( offset > elf->size ) + /* Sanity check phdr if present. */ + count = elf_phdr_count(elf); + if ( count ) { - elf_err(elf, "ELF: phdr overflow (off %" PRIx64 " > size %lx)\n", - offset, (unsigned long)elf->size); - return -1; + if ( elf_uval(elf, elf->ehdr, e_phentsize) < + elf_size(elf, ELF_HANDLE_DECL(elf_phdr)) ) + { + elf_err(elf, "ELF: phdr too small (%" PRIu64 ")\n", + elf_uval(elf, elf->ehdr, e_phentsize)); + return -1; + } + offset = elf_uval(elf, elf->ehdr, e_phoff) + + elf_uval(elf, elf->ehdr, e_phentsize) * count; + if ( offset > elf->size ) + { + elf_err(elf, "ELF: phdr overflow (off %" PRIx64 " > size %lx)\n", + offset, (unsigned long)elf->size); + return -1; + } } - /* Sanity check shdr. */ - offset = elf_uval(elf, elf->ehdr, e_shoff) + - elf_uval(elf, elf->ehdr, e_shentsize) * elf_shdr_count(elf); - if ( offset > elf->size ) + /* Sanity check shdr if present. */ + count = elf_shdr_count(elf); + if ( count ) { - elf_err(elf, "ELF: shdr overflow (off %" PRIx64 " > size %lx)\n", - offset, (unsigned long)elf->size); - return -1; + if ( elf_uval(elf, elf->ehdr, e_shentsize) < elf_size(elf, shdr) ) + { + elf_err(elf, "ELF: shdr too small (%" PRIu64 ")\n", + elf_uval(elf, elf->ehdr, e_shentsize)); + return -1; + } + offset = elf_uval(elf, elf->ehdr, e_shoff) + + elf_uval(elf, elf->ehdr, e_shentsize) * count; + if ( offset > elf->size ) + { + elf_err(elf, "ELF: shdr overflow (off %" PRIx64 " > size %lx)\n", + offset, (unsigned long)elf->size); + return -1; + } } /* Find section string table. */ @@ -79,7 +100,6 @@ elf_errorstatus elf_init(struct elf_bina elf->sec_strtab = elf_section_start(elf, shdr); /* Find symbol table and symbol string table. */ - count = elf_shdr_count(elf); for ( i = 1; i < count; i++ ) { shdr = elf_shdr_by_index(elf, i); --- a/xen/common/libelf/libelf-tools.c +++ b/xen/common/libelf/libelf-tools.c @@ -130,8 +130,11 @@ uint64_t elf_round_up(struct elf_binary unsigned elf_shdr_count(struct elf_binary *elf) { unsigned count = elf_uval(elf, elf->ehdr, e_shnum); - uint64_t max = elf->size / sizeof(Elf32_Shdr); + uint64_t max; + if ( !count ) + return 0; + max = elf->size / elf_uval(elf, elf->ehdr, e_shentsize); if ( max > UINT_MAX ) max = UINT_MAX; if ( count > max ) @@ -144,7 +147,20 @@ unsigned elf_shdr_count(struct elf_binar unsigned elf_phdr_count(struct elf_binary *elf) { - return elf_uval(elf, elf->ehdr, e_phnum); + unsigned count = elf_uval(elf, elf->ehdr, e_phnum); + uint64_t max; + + if ( !count ) + return 0; + max = elf->size / elf_uval(elf, elf->ehdr, e_phentsize); + if ( max > UINT_MAX ) + max = UINT_MAX; + if ( count > max ) + { + elf_mark_broken(elf, "far too many program headers"); + count = max; + } + return count; } ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_name(struct elf_binary *elf, const char *name)