diff --git a/grub-core/kern/elf.c b/grub-core/kern/elf.c index 9d7149b38..66b69293e 100644 --- a/grub-core/kern/elf.c +++ b/grub-core/kern/elf.c @@ -167,11 +167,15 @@ grub_elf_open (const char *name, enum grub_file_type type) #define grub_elfXX_load_phdrs grub_elf32_load_phdrs #define ElfXX_Phdr Elf32_Phdr #define ElfXX_Ehdr Elf32_Ehdr +#define ElfXX_Shdr Elf32_Shdr +#define ElfXX_Word Elf32_Word +#define ElfXX_Shnum Elf32_Shnum #define grub_uintXX_t grub_uint32_t #define grub_swap_bytes_addrXX grub_swap_bytes32 #define grub_swap_bytes_offXX grub_swap_bytes32 #define grub_swap_bytes_XwordXX grub_swap_bytes32 #define grub_elfXX_check_endianess_and_bswap_ehdr grub_elf32_check_endianess_and_bswap_ehdr +#define grub_elfXX_get_shnum grub_elf32_get_shnum #include "elfXX.c" @@ -185,11 +189,15 @@ grub_elf_open (const char *name, enum grub_file_type type) #undef grub_elfXX_load_phdrs #undef ElfXX_Phdr #undef ElfXX_Ehdr +#undef ElfXX_Shdr +#undef ElfXX_Word +#undef ElfXX_Shnum #undef grub_uintXX_t #undef grub_swap_bytes_addrXX #undef grub_swap_bytes_offXX #undef grub_swap_bytes_XwordXX #undef grub_elfXX_check_endianess_and_bswap_ehdr +#undef grub_elfXX_get_shnum /* 64-bit */ @@ -203,10 +211,14 @@ grub_elf_open (const char *name, enum grub_file_type type) #define grub_elfXX_load_phdrs grub_elf64_load_phdrs #define ElfXX_Phdr Elf64_Phdr #define ElfXX_Ehdr Elf64_Ehdr +#define ElfXX_Shdr Elf64_Shdr +#define ElfXX_Word Elf64_Word +#define ElfXX_Shnum Elf64_Shnum #define grub_uintXX_t grub_uint64_t #define grub_swap_bytes_addrXX grub_swap_bytes64 #define grub_swap_bytes_offXX grub_swap_bytes64 #define grub_swap_bytes_XwordXX grub_swap_bytes64 #define grub_elfXX_check_endianess_and_bswap_ehdr grub_elf64_check_endianess_and_bswap_ehdr +#define grub_elfXX_get_shnum grub_elf64_get_shnum #include "elfXX.c" diff --git a/grub-core/kern/elfXX.c b/grub-core/kern/elfXX.c index 1859d1880..48b7745a5 100644 --- a/grub-core/kern/elfXX.c +++ b/grub-core/kern/elfXX.c @@ -205,3 +205,37 @@ grub_elfXX_check_endianess_and_bswap_ehdr (grub_elf_t elf) return 0; } + +grub_err_t +grub_elfXX_get_shnum (ElfXX_Ehdr *e, ElfXX_Shnum *shnum) +{ + ElfXX_Shdr *s; + + if (shnum == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for shnum")); + + /* Set *shnum to 0 so that shnum doesn't return junk on error */ + *shnum = 0; + + if (e == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header")); + + *shnum = e->e_shnum; + if (*shnum == SHN_UNDEF) + { + if (e->e_shoff == 0) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff")); + + s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff); + *shnum = s->sh_size; + if (*shnum < SHN_LORESERVE) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of section header table entries in sh_size: %" PRIuGRUB_UINT64_T), (grub_uint64_t) *shnum); + } + else + { + if (*shnum >= SHN_LORESERVE) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of section header table entries in e_shnum: %" PRIuGRUB_UINT64_T), (grub_uint64_t) *shnum); + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/loader/i386/bsdXX.c b/grub-core/loader/i386/bsdXX.c index e6edc6fb6..2d91fd44d 100644 --- a/grub-core/loader/i386/bsdXX.c +++ b/grub-core/loader/i386/bsdXX.c @@ -26,7 +26,10 @@ load (grub_file_t file, const char *filename, void *where, grub_off_t off, grub_ static inline grub_err_t read_headers (grub_file_t file, const char *filename, Elf_Ehdr *e, Elf_Shdr **shdr) { - if (grub_file_seek (file, 0) == (grub_off_t) -1) + Elf_Shnum shnum; + grub_err_t err; + + if (grub_file_seek (file, 0) == (grub_off_t) -1) return grub_errno; if (grub_file_read (file, (char *) e, sizeof (*e)) != sizeof (*e)) @@ -48,15 +51,19 @@ read_headers (grub_file_t file, const char *filename, Elf_Ehdr *e, Elf_Shdr **sh if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS)) return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); - *shdr = grub_calloc (e->e_shnum, e->e_shentsize); + err = grub_elf_get_shnum (e, &shnum); + if (err != GRUB_ERR_NONE) + return err; + + *shdr = grub_calloc (shnum, e->e_shentsize); if (! *shdr) return grub_errno; if (grub_file_seek (file, e->e_shoff) == (grub_off_t) -1) return grub_errno; - if (grub_file_read (file, *shdr, (grub_uint32_t) e->e_shnum * e->e_shentsize) - != (grub_ssize_t) ((grub_uint32_t) e->e_shnum * e->e_shentsize)) + if (grub_file_read (file, *shdr, shnum * e->e_shentsize) + != ((grub_ssize_t) shnum * e->e_shentsize)) { if (grub_errno) return grub_errno; @@ -78,6 +85,7 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, { Elf_Ehdr e; Elf_Shdr *s, *shdr = NULL; + Elf_Shnum shnum; grub_addr_t curload, module; grub_err_t err; grub_size_t chunk_size = 0; @@ -89,7 +97,11 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, if (err) goto out; - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + err = grub_elf_get_shnum (&e, &shnum); + if (err != GRUB_ERR_NONE) + goto out; + + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { if (s->sh_size == 0) @@ -111,7 +123,7 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, chunk_src = get_virtual_current_address (ch); } - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { if (s->sh_size == 0) @@ -154,7 +166,7 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, if (! err) err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SHDR, - shdr, e.e_shnum * e.e_shentsize); + shdr, shnum * e.e_shentsize); out: grub_free (shdr); @@ -170,6 +182,7 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator, { Elf_Ehdr e; Elf_Shdr *s, *shdr = NULL; + Elf_Shnum shnum; grub_addr_t curload, module; grub_err_t err; grub_size_t chunk_size = 0; @@ -181,7 +194,11 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator, if (err) goto out; - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + err = grub_elf_get_shnum (&e, &shnum); + if (err != GRUB_ERR_NONE) + goto out; + + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { if (s->sh_size == 0) @@ -196,7 +213,7 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator, if (chunk_size < sizeof (e)) chunk_size = sizeof (e); chunk_size += (grub_uint32_t) e.e_phnum * e.e_phentsize; - chunk_size += (grub_uint32_t) e.e_shnum * e.e_shentsize; + chunk_size += (grub_size_t) shnum * e.e_shentsize; { grub_relocator_chunk_t ch; @@ -209,7 +226,7 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator, chunk_src = get_virtual_current_address (ch); } - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { if (s->sh_size == 0) @@ -247,9 +264,9 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator, curload = module + sizeof (e); load (file, argv[0], (grub_uint8_t *) chunk_src + curload - *kern_end, e.e_shoff, - (grub_uint32_t) e.e_shnum * e.e_shentsize); + (grub_size_t) shnum * e.e_shentsize); e.e_shoff = curload - module; - curload += (grub_uint32_t) e.e_shnum * e.e_shentsize; + curload += (grub_addr_t) shnum * e.e_shentsize; load (file, argv[0], (grub_uint8_t *) chunk_src + curload - *kern_end, e.e_phoff, (grub_uint32_t) e.e_phnum * e.e_phentsize); @@ -279,6 +296,7 @@ SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator *relocator, grub_err_t err; Elf_Ehdr e; Elf_Shdr *s, *shdr = NULL; + Elf_Shnum shnum; unsigned symoff, stroff, symsize, strsize; grub_freebsd_addr_t symstart, symend, symentsize, dynamic; Elf_Sym *sym; @@ -293,17 +311,21 @@ SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator *relocator, if (err) goto out; + err = grub_elf_get_shnum (&e, &shnum); + if (err != GRUB_ERR_NONE) + goto out; + err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_ELFHDR, &e, sizeof (e)); if (err) goto out; - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) if (s->sh_type == SHT_SYMTAB) break; - if (s >= (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize)) + if (s >= (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize)) { err = grub_error (GRUB_ERR_BAD_OS, N_("no symbol table")); goto out; @@ -418,6 +440,7 @@ SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator *relocator, grub_err_t err; Elf_Ehdr e; Elf_Shdr *s, *symsh, *strsh, *shdr = NULL; + Elf_Shnum shnum; unsigned symsize, strsize; void *sym_chunk; grub_uint8_t *curload; @@ -433,11 +456,18 @@ SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator *relocator, return grub_errno; } - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + err = grub_elf_get_shnum (&e, &shnum); + if (err != GRUB_ERR_NONE) + { + grub_free (shdr); + return err; + } + + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) if (s->sh_type == SHT_SYMTAB) break; - if (s >= (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize)) + if (s >= (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize)) { grub_free (shdr); return GRUB_ERR_NONE; @@ -450,7 +480,7 @@ SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator *relocator, chunk_size = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t)) + ALIGN_UP (strsize, sizeof (grub_freebsd_addr_t)) - + sizeof (e) + (grub_uint32_t) e.e_shnum * e.e_shentsize; + + sizeof (e) + shnum * e.e_shentsize; symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t)); { @@ -478,17 +508,17 @@ SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator *relocator, curload += sizeof (e); - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { Elf_Shdr *s2; s2 = (Elf_Shdr *) curload; grub_memcpy (curload, s, e.e_shentsize); if (s == symsh) - s2->sh_offset = sizeof (e) + (grub_uint32_t) e.e_shnum * e.e_shentsize; + s2->sh_offset = sizeof (e) + shnum * e.e_shentsize; else if (s == strsh) s2->sh_offset = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t)) - + sizeof (e) + (grub_uint32_t) e.e_shnum * e.e_shentsize; + + sizeof (e) + shnum * e.e_shentsize; else s2->sh_offset = 0; s2->sh_addr = s2->sh_offset; @@ -552,6 +582,7 @@ SUFFIX(grub_openbsd_find_ramdisk) (grub_file_t file, grub_err_t err; Elf_Ehdr e; Elf_Shdr *s, *shdr = NULL; + Elf_Shnum shnum; err = read_headers (file, filename, &e, &shdr); if (err) @@ -560,11 +591,18 @@ SUFFIX(grub_openbsd_find_ramdisk) (grub_file_t file, return err; } - for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize); + err = grub_elf_get_shnum (&e, &shnum); + if (err != GRUB_ERR_NONE) + { + grub_free (shdr); + return err; + } + + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) if (s->sh_type == SHT_SYMTAB) break; - if (s >= (Elf_Shdr *) ((grub_uint8_t *) shdr + e.e_shnum * e.e_shentsize)) + if (s >= (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize)) { grub_free (shdr); return GRUB_ERR_NONE; diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index 6801f0ddf..ed5a4ae9d 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -17,19 +17,23 @@ */ #if defined(MULTIBOOT_LOAD_ELF32) -# define XX 32 -# define E_MACHINE MULTIBOOT_ELF32_MACHINE -# define ELFCLASSXX ELFCLASS32 -# define Elf_Ehdr Elf32_Ehdr -# define Elf_Phdr Elf32_Phdr -# define Elf_Shdr Elf32_Shdr +# define XX 32 +# define E_MACHINE MULTIBOOT_ELF32_MACHINE +# define ELFCLASSXX ELFCLASS32 +# define Elf_Ehdr Elf32_Ehdr +# define Elf_Phdr Elf32_Phdr +# define Elf_Shdr Elf32_Shdr +# define Elf_Shnum Elf32_Shnum +# define grub_multiboot_elf_get_shnum grub_elf32_get_shnum #elif defined(MULTIBOOT_LOAD_ELF64) -# define XX 64 -# define E_MACHINE MULTIBOOT_ELF64_MACHINE -# define ELFCLASSXX ELFCLASS64 -# define Elf_Ehdr Elf64_Ehdr -# define Elf_Phdr Elf64_Phdr -# define Elf_Shdr Elf64_Shdr +# define XX 64 +# define E_MACHINE MULTIBOOT_ELF64_MACHINE +# define ELFCLASSXX ELFCLASS64 +# define Elf_Ehdr Elf64_Ehdr +# define Elf_Phdr Elf64_Phdr +# define Elf_Shdr Elf64_Shdr +# define Elf_Shnum Elf64_Shnum +# define grub_multiboot_elf_get_shnum grub_elf64_get_shnum #else #error "I'm confused" #endif @@ -58,7 +62,8 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) grub_err_t err; grub_relocator_chunk_t ch; grub_uint32_t load_offset = 0, load_size; - int i; + Elf_Shnum shnum; + unsigned int i; void *source = NULL; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 @@ -75,6 +80,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) return grub_error (GRUB_ERR_UNKNOWN_OS, N_("this ELF file is not of the right type")); + err = grub_multiboot_elf_get_shnum (ehdr, &shnum); + if (err != GRUB_ERR_NONE) + return err; + /* FIXME: Should we support program headers at strange locations? */ if (ehdr->e_phoff + (grub_uint32_t) ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH) return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset"); @@ -213,11 +222,11 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) #error Please complete this #endif - if (ehdr->e_shnum) + if (shnum) { grub_uint8_t *shdr, *shdrptr; - shdr = grub_calloc (ehdr->e_shnum, ehdr->e_shentsize); + shdr = grub_calloc (shnum, ehdr->e_shentsize); if (!shdr) return grub_errno; @@ -227,8 +236,8 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) return grub_errno; } - if (grub_file_read (mld->file, shdr, (grub_uint32_t) ehdr->e_shnum * ehdr->e_shentsize) - != (grub_ssize_t) ehdr->e_shnum * ehdr->e_shentsize) + if (grub_file_read (mld->file, shdr, shnum * ehdr->e_shentsize) + != (grub_ssize_t) shnum * ehdr->e_shentsize) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), @@ -236,7 +245,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) return grub_errno; } - for (shdrptr = shdr, i = 0; i < ehdr->e_shnum; + for (shdrptr = shdr, i = 0; i < shnum; shdrptr += ehdr->e_shentsize, i++) { Elf_Shdr *sh = (Elf_Shdr *) shdrptr; @@ -281,7 +290,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) } sh->sh_addr = target; } - GRUB_MULTIBOOT (add_elfsyms) (ehdr->e_shnum, ehdr->e_shentsize, + GRUB_MULTIBOOT (add_elfsyms) (shnum, ehdr->e_shentsize, ehdr->e_shstrndx, shdr); } @@ -296,3 +305,5 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) #undef Elf_Ehdr #undef Elf_Phdr #undef Elf_Shdr +#undef Elf_Shnum +#undef grub_multiboot_elf_get_shnum diff --git a/include/grub/elf.h b/include/grub/elf.h index c478933ee..ce2f5609a 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -23,6 +23,7 @@ /* Standard ELF types. */ #include +#include /* Type for a 16-bit quantity. */ typedef grub_uint16_t Elf32_Half; @@ -56,6 +57,9 @@ typedef grub_uint16_t Elf64_Section; typedef Elf32_Half Elf32_Versym; typedef Elf64_Half Elf64_Versym; +/* Type for number of section header table entries */ +typedef Elf32_Word Elf32_Shnum; +typedef Elf64_Xword Elf64_Shnum; /* The ELF file header. This appears at the start of every ELF file. */ @@ -2531,6 +2535,10 @@ typedef Elf32_Addr Elf32_Conflict; #define R_RISCV_SET32 56 #define R_RISCV_32_PCREL 57 +extern grub_err_t grub_elf32_get_shnum (Elf32_Ehdr *e, Elf32_Shnum *shnum); + +extern grub_err_t grub_elf64_get_shnum (Elf64_Ehdr *e, Elf64_Shnum *shnum); + #ifdef GRUB_TARGET_WORDSIZE #if GRUB_TARGET_WORDSIZE == 32 @@ -2548,6 +2556,7 @@ typedef Elf32_Sword Elf_Sword; typedef Elf32_Sym Elf_Sym; typedef Elf32_Word Elf_Word; typedef Elf32_Xword Elf_Xword; +typedef Elf32_Shnum Elf_Shnum; #define ELF_ST_BIND(val) ELF32_ST_BIND(val) #define ELF_ST_TYPE(val) ELF32_ST_TYPE(val) @@ -2557,6 +2566,8 @@ typedef Elf32_Xword Elf_Xword; #define ELF_R_TYPE(val) ELF32_R_TYPE(val) #define ELF_R_INFO(sym, type) ELF32_R_INFO(sym, type) +#define grub_elf_get_shnum grub_elf32_get_shnum + #elif GRUB_TARGET_WORDSIZE == 64 typedef Elf64_Addr Elf_Addr; @@ -2573,6 +2584,7 @@ typedef Elf64_Sword Elf_Sword; typedef Elf64_Sym Elf_Sym; typedef Elf64_Word Elf_Word; typedef Elf64_Xword Elf_Xword; +typedef Elf64_Shnum Elf_Shnum; #define ELF_ST_BIND(val) ELF64_ST_BIND (val) #define ELF_ST_TYPE(val) ELF64_ST_TYPE (val) @@ -2581,6 +2593,8 @@ typedef Elf64_Xword Elf_Xword; #define ELF_R_TYPE(val) ELF64_R_TYPE(val) #define ELF_R_INFO(sym, type) ELF64_R_INFO(sym, type) +#define grub_elf_get_shnum grub_elf64_get_shnum + #endif /* GRUB_TARGET_WORDSIZE == 64 */ #endif