--- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -48,6 +48,14 @@ #include #include +/* + * 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); }