--- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -115,6 +115,7 @@ caddr_t strbase; /* malloc'ed string base */ } *elf_file_t; +static void link_elf_error(const char *s); static int parse_dynamic(linker_file_t lf); static int relocate_file(linker_file_t lf); static int parse_module_symbols(linker_file_t lf); @@ -305,6 +306,18 @@ ef->pltrelsize = 0; } + /* + * Relocations and hashed lookups dereference ef->symtab/ef->strtab + * unconditionally; refuse modules that reference symbols without + * providing both tables. + */ + if ((ef->rel != NULL || ef->rela != NULL || ef->pltrel != NULL || + ef->pltrela != NULL || ef->buckets != NULL) && + (ef->strtab == NULL || ef->symtab == NULL)) { + link_elf_error("Missing DT_STRTAB/DT_SYMTAB for relocations"); + return ENOEXEC; + } + ef->ddbsymtab = ef->symtab; ef->ddbsymcnt = ef->nchains; ef->ddbstrtab = ef->strtab; @@ -489,7 +502,11 @@ if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) - link_elf_error("Unreadable program headers"); + { + link_elf_error("Unreadable program headers"); + error = ENOEXEC; /* the check was warning-only; enforce it */ + goto out; + } /* * Scan the program header entries, and save key information. @@ -533,14 +550,47 @@ error = ENOEXEC; goto out; } + if (nsegs != 2) { + link_elf_error("Expected exactly two PT_LOAD segments"); + error = ENOEXEC; + goto out; + } /* * Allocate the entire address space of the object, to stake out our * contiguous region, and to establish the base address for relocation. */ + for (i = 0; i < 2; i++) { + if (segs[i]->p_filesz > segs[i]->p_memsz) { + link_elf_error("Segment p_filesz greater than p_memsz"); + error = ENOEXEC; + goto out; + } + if (segs[i]->p_vaddr + segs[i]->p_memsz < segs[i]->p_vaddr) { + link_elf_error("Segment address wraparound"); + error = ENOEXEC; + goto out; + } + } + /* + * mapsize is derived from segs[0]'s start and segs[1]'s end only; + * require segs[0] to end no later than segs[1] ends, or its extent + * is never covered by the mapping (file-controlled heap overflow). + */ + if (segs[0]->p_vaddr + segs[0]->p_memsz > + segs[1]->p_vaddr + segs[1]->p_memsz) { + link_elf_error("Overlapping or out-of-order segments"); + error = ENOEXEC; + goto out; + } base_vaddr = trunc_page(segs[0]->p_vaddr); base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); mapsize = base_vlimit - base_vaddr; + if (mapsize == 0) { + link_elf_error("Empty mapping"); + error = ENOEXEC; + goto out; + } ef = kmalloc(sizeof(struct elf_file), M_LINKER, M_WAITOK | M_ZERO); ef->address = kmalloc(mapsize, M_LINKER, M_WAITOK); @@ -587,6 +637,11 @@ goto out; /* Try and load the symbol table if it's present. (you can strip it!) */ + if (hdr->e_shentsize != sizeof(Elf_Shdr)) { + link_elf_error("Bad e_shentsize"); + error = ENOEXEC; + goto out; + } nbytes = hdr->e_shnum * hdr->e_shentsize; if (nbytes == 0 || hdr->e_shoff == 0) goto nosyms; @@ -606,6 +661,21 @@ } if (symtabindex < 0 || symstrindex < 0) goto nosyms; + if (symstrindex >= hdr->e_shnum) { + link_elf_error("Bad symstrindex"); + error = ENOEXEC; + goto out; + } + /* + * symcnt/strcnt are int; reject sizes that do not fit so the + * truncation cannot produce negative kmalloc()/vn_rdwr() lengths. + */ + if (shdr[symtabindex].sh_size > INT_MAX || + shdr[symstrindex].sh_size > INT_MAX) { + link_elf_error("Symbol table too large"); + error = ENOEXEC; + goto out; + } symcnt = shdr[symtabindex].sh_size; ef->symbase = kmalloc(symcnt, M_LINKER, M_WAITOK); @@ -1004,7 +1074,8 @@ /* Force lookup failure when we have an insanity. */ if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) return (ENOENT); - return ((Elf_Addr) ef->address + sym->st_value); + *result = (Elf_Addr) ef->address + sym->st_value; + return (0); } /* * XXX we can avoid doing a hash table based lookup for global