DragonFlyBSD Kernel Audit
DF-2808 / fix.diff
← back to finding ↓ download raw
--- 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