DF-2772 / 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -48,6 +48,14 @@ #include <vm/pmap.h> #include <vm/vm_map.h> +/* + * No legitimate KLD section comes anywhere near this size. Capping + * sh_size keeps the mapsize accumulator in link_elf_obj_load_file from + * wrapping 2^64 on crafted modules (which would size the KVA mapping + * far below the sum of the per-section bzero()/read lengths). + */ +#define LINKER_MAX_SECSIZE (1LL << 44) + static int link_elf_obj_preload_file(const char *, linker_file_t *); static int link_elf_obj_preload_finish(linker_file_t); static int link_elf_obj_load_file(const char *, linker_file_t *); @@ -63,7 +71,7 @@ static int link_elf_obj_lookup_set(linker_file_t, const char *, void ***, void ***, int *); -static void link_elf_obj_reloc_local(linker_file_t lf); +static int link_elf_obj_reloc_local(linker_file_t lf); static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *); static struct linker_class_ops link_elf_obj_class_ops = { @@ -137,6 +145,27 @@ static int relocate_file(linker_file_t lf); /* + * A REL/RELA section must name (via sh_info) a section that actually + * gets a progtab entry, otherwise findbase() fails later and the loader + * panics ("lost base for reltab/relatab") instead of rejecting the + * file. skip_empty is set for the file case, where size-0 sections are + * never entered into progtab. + */ +static int +elf_obj_reloc_target_ok(const Elf_Shdr *shdr, int e_shnum, + Elf_Size sh_info, int skip_empty) +{ + if (sh_info >= (Elf_Size)e_shnum) + return (0); + if (shdr[sh_info].sh_type != SHT_PROGBITS && + shdr[sh_info].sh_type != SHT_NOBITS) + return (0); + if (skip_empty && shdr[sh_info].sh_size == 0) + return (0); + return (1); +} + +/* * The kernel symbol table starts here. */ extern struct _dynamic _DYNAMIC; @@ -232,9 +261,19 @@ symstrindex = shdr[i].sh_link; break; case SHT_REL: + if (!elf_obj_reloc_target_ok(shdr, hdr->e_shnum, + shdr[i].sh_info, 0)) { + error = ENOEXEC; + goto out; + } ef->nreltab++; break; case SHT_RELA: + if (!elf_obj_reloc_target_ok(shdr, hdr->e_shnum, + shdr[i].sh_info, 0)) { + error = ENOEXEC; + goto out; + } ef->nrelatab++; break; } @@ -365,7 +404,9 @@ panic("lost relatab"); /* Local intra-module relocations */ - link_elf_obj_reloc_local(lf); + error = link_elf_obj_reloc_local(lf); + if (error) + goto out; *result = lf; return (0); @@ -528,9 +569,19 @@ symstrindex = shdr[i].sh_link; break; case SHT_REL: + if (!elf_obj_reloc_target_ok(shdr, hdr->e_shnum, + shdr[i].sh_info, 1)) { + error = ENOEXEC; + goto out; + } ef->nreltab++; break; case SHT_RELA: + if (!elf_obj_reloc_target_ok(shdr, hdr->e_shnum, + shdr[i].sh_info, 1)) { + error = ENOEXEC; + goto out; + } ef->nrelatab++; break; case SHT_STRTAB: @@ -624,6 +675,12 @@ switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: + if (shdr[i].sh_size > (Elf_Size)LINKER_MAX_SECSIZE) { + link_elf_obj_error(filename, + "section too large"); + error = ENOEXEC; + goto out; + } alignmask = shdr[i].sh_addralign - 1; mapsize += alignmask; mapsize &= ~alignmask; @@ -815,7 +872,9 @@ (vm_offset_t) ef->address + mapsize); /* Local intra-module relocations */ - link_elf_obj_reloc_local(lf); + error = link_elf_obj_reloc_local(lf); + if (error) + goto out; /* Pull in dependencies */ error = linker_load_dependencies(lf); @@ -884,12 +943,19 @@ return; } - for (i = 0; i < ef->nreltab; i++) - if (ef->reltab[i].rel) - kfree(ef->reltab[i].rel, M_LINKER); - for (i = 0; i < ef->nrelatab; i++) - if (ef->relatab[i].rela) - kfree(ef->relatab[i].rela, M_LINKER); + /* + * The counters may be non-zero from the scan loop even though the + * load aborted before the tables were allocated (e.g. "file has no + * contents"), so guard the arrays themselves. + */ + if (ef->reltab) + for (i = 0; i < ef->nreltab; i++) + if (ef->reltab[i].rel) + kfree(ef->reltab[i].rel, M_LINKER); + if (ef->relatab) + for (i = 0; i < ef->nrelatab; i++) + if (ef->relatab[i].rela) + kfree(ef->relatab[i].rela, M_LINKER); if (ef->reltab) kfree(ef->reltab, M_LINKER); if (ef->relatab) @@ -974,7 +1040,7 @@ rellim = rel + ef->reltab[i].nrel; base = findbase(ef, ef->reltab[i].sec); if (base == 0) - panic("lost base for reltab"); + return (ENOEXEC); for ( ; rel < rellim; rel++) { symidx = ELF_R_SYM(rel->r_info); if (symidx >= ef->ddbsymcnt) @@ -1001,7 +1067,7 @@ relalim = rela + ef->relatab[i].nrela; base = findbase(ef, ef->relatab[i].sec); if (base == 0) - panic("lost base for relatab"); + return (ENOEXEC); for ( ; rela < relalim; rela++) { symidx = ELF_R_SYM(rela->r_info); if (symidx >= ef->ddbsymcnt) @@ -1219,7 +1285,7 @@ } } -static void +static int link_elf_obj_reloc_local(linker_file_t lf) { elf_file_t ef = lf->priv; @@ -1242,7 +1308,7 @@ rellim = rel + ef->reltab[i].nrel; base = findbase(ef, ef->reltab[i].sec); if (base == 0) - panic("lost base for reltab"); + return (ENOEXEC); for ( ; rel < rellim; rel++) { symidx = ELF_R_SYM(rel->r_info); if (symidx >= ef->ddbsymcnt) @@ -1264,7 +1330,7 @@ relalim = rela + ef->relatab[i].nrela; base = findbase(ef, ef->relatab[i].sec); if (base == 0) - panic("lost base for relatab"); + return (ENOEXEC); for ( ; rela < relalim; rela++) { symidx = ELF_R_SYM(rela->r_info); if (symidx >= ef->ddbsymcnt) @@ -1277,4 +1343,6 @@ elf_obj_lookup); } } + + return (0); } |