DF-2810 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | --- 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 |