Module Name:    src
Committed By:   christos
Date:           Fri Mar 19 22:08:13 UTC 2010

Modified Files:
        src/sys/kern: exec_elf.c

Log Message:
- Make maximum memory limits for various things #define constants and use the
  consistently across the code.
- Re-do note parsing code to read the section headers instead of the program
  headers because the new binutils merge all the note sections in one program
  header. This fixes all the pax note parsing which has been broken for all
  binaries built with the new binutils.
- Add diagnostics to the note parsing code to detect malformed binaries.
- Allocate and free note scratch space only once, not once per note.


To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/sys/kern/exec_elf.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/exec_elf.c
diff -u src/sys/kern/exec_elf.c:1.14 src/sys/kern/exec_elf.c:1.15
--- src/sys/kern/exec_elf.c:1.14	Mon Mar 15 16:35:20 2010
+++ src/sys/kern/exec_elf.c	Fri Mar 19 18:08:13 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec_elf.c,v 1.14 2010/03/15 20:35:20 christos Exp $	*/
+/*	$NetBSD: exec_elf.c,v 1.15 2010/03/19 22:08:13 christos Exp $	*/
 
 /*-
  * Copyright (c) 1994, 2000, 2005 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.14 2010/03/15 20:35:20 christos Exp $");
+__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.15 2010/03/19 22:08:13 christos Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_pax.h"
@@ -109,7 +109,12 @@
 #define	ELF_ROUND(a, b)		(((a) + (b) - 1) & ~((b) - 1))
 #define	ELF_TRUNC(a, b)		((a) & ~((b) - 1))
 
-#define MAXPHNUM	50
+/*
+ * Arbitrary limits to avoid DoS for excessive memory allocation.
+ */
+#define MAXPHNUM	128
+#define MAXSHNUM	32768
+#define MAXNOTESIZE	1024
 
 #ifdef PAX_ASLR
 /*
@@ -119,33 +124,35 @@
 pax_aslr_elf(struct lwp *l, struct exec_package *epp, Elf_Ehdr *eh,
     Elf_Phdr *ph)
 {
-	size_t pax_align = 0, pax_offset, i;
-	uint32_t r;
+	size_t pax_offset, i;
 
-	if (!pax_aslr_active(l))
-		return;
+	if (pax_aslr_active(l)) {
+		size_t pax_align, l2, delta;
+		uint32_t r;
 
-	/*
-	 * find align XXX: not all sections might have the same
-	 * alignment
-	 */
-	for (i = 0; i < eh->e_phnum; i++)
-		if (ph[i].p_type == PT_LOAD) {
-			pax_align = ph[i].p_align;
-			break;
-		}
+		/*
+		 * find align XXX: not all sections might have the same
+		 * alignment
+		 */
+		for (pax_align = i = 0; i < eh->e_phnum; i++)
+			if (ph[i].p_type == PT_LOAD) {
+				pax_align = ph[i].p_align;
+				break;
+			}
 
-	r = arc4random();
+		r = arc4random();
 
-	if (pax_align == 0)
-		pax_align = PGSHIFT;
+		if (pax_align == 0)
+			pax_align = PGSHIFT;
+		l2 = ilog2(pax_align);
+		delta = PAX_ASLR_DELTA(r, l2, PAX_ASLR_DELTA_EXEC_LEN);
 #ifdef PAX_ASLR_DEBUG
-	uprintf("r=0x%x a=0x%x p=0x%x Delta=0x%lx\n", r,
-	    ilog2(pax_align), PGSHIFT, PAX_ASLR_DELTA(r,
-		ilog2(pax_align), PAX_ASLR_DELTA_EXEC_LEN));
+		uprintf("r=0x%x a=0x%x p=0x%x Delta=0x%lx\n", r, l2, PGSHIFT,
+		    delta);
 #endif
-	pax_offset = ELF_TRUNC(PAX_ASLR_DELTA(r,
-	    ilog2(pax_align), PAX_ASLR_DELTA_EXEC_LEN), pax_align) + PAGE_SIZE;
+		pax_offset = ELF_TRUNC(delta, pax_align) + PAGE_SIZE;
+	} else
+		pax_offset = PAGE_SIZE;
 
 	for (i = 0; i < eh->e_phnum; i++)
 		ph[i].p_vaddr += pax_offset;
@@ -303,7 +310,7 @@
 	if (eh->e_type != type)
 		return ENOEXEC;
 
-	if (eh->e_shnum > 32768 || eh->e_phnum > 128)
+	if (eh->e_shnum > MAXSHNUM || eh->e_phnum > MAXPHNUM)
 		return ENOEXEC;
 
 	return 0;
@@ -845,36 +852,36 @@
     Elf_Ehdr *eh)
 {
 	size_t i;
-	Elf_Phdr *ph;
-	size_t phsize;
+	Elf_Shdr *sh;
+	Elf_Nhdr *np;
+	size_t shsize;
 	int error;
 	int isnetbsd = 0;
 	char *ndata;
 
 	epp->ep_pax_flags = 0;
-	if (eh->e_phnum > MAXPHNUM || eh->e_phnum == 0)
+	if (eh->e_shnum > MAXSHNUM || eh->e_shnum == 0)
 		return ENOEXEC;
 
-	phsize = eh->e_phnum * sizeof(Elf_Phdr);
-	ph = kmem_alloc(phsize, KM_SLEEP);
-	error = exec_read_from(l, epp->ep_vp, eh->e_phoff, ph, phsize);
+	shsize = eh->e_shnum * sizeof(Elf_Shdr);
+	sh = kmem_alloc(shsize, KM_SLEEP);
+	error = exec_read_from(l, epp->ep_vp, eh->e_shoff, sh, shsize);
 	if (error)
 		goto out;
 
-	for (i = 0; i < eh->e_phnum; i++) {
-		Elf_Phdr *ephp = &ph[i];
-		Elf_Nhdr *np;
-
-		if (ephp->p_type != PT_NOTE ||
-		    ephp->p_filesz > 1024 ||
-		    ephp->p_filesz < sizeof(Elf_Nhdr) + ELF_NOTE_NETBSD_NAMESZ)
+	np = kmem_alloc(MAXNOTESIZE, KM_SLEEP);
+	for (i = 0; i < eh->e_shnum; i++) {
+		Elf_Shdr *shp = &sh[i];
+
+		if (shp->sh_type != SHT_NOTE ||
+		    shp->sh_size > MAXNOTESIZE ||
+		    shp->sh_size < sizeof(Elf_Nhdr) + ELF_NOTE_NETBSD_NAMESZ)
 			continue;
 
-		np = kmem_alloc(ephp->p_filesz, KM_SLEEP);
-		error = exec_read_from(l, epp->ep_vp, ephp->p_offset, np,
-		    ephp->p_filesz);
+		error = exec_read_from(l, epp->ep_vp, shp->sh_offset, np,
+		    shp->sh_size);
 		if (error)
-			goto next;
+			continue;
 
 		ndata = (char *)(np + 1);
 		switch (np->n_type) {
@@ -883,7 +890,7 @@
 			    np->n_descsz != ELF_NOTE_NETBSD_DESCSZ ||
 			    memcmp(ndata, ELF_NOTE_NETBSD_NAME,
 			    ELF_NOTE_NETBSD_NAMESZ))
-				goto next;
+				goto bad;
 			isnetbsd = 1;
 			break;
 
@@ -891,25 +898,42 @@
 			if (np->n_namesz != ELF_NOTE_PAX_NAMESZ ||
 			    np->n_descsz != ELF_NOTE_PAX_DESCSZ ||
 			    memcmp(ndata, ELF_NOTE_PAX_NAME,
-			    ELF_NOTE_PAX_NAMESZ))
-				goto next;
+			    ELF_NOTE_PAX_NAMESZ)) {
+bad:
+#ifdef DIAGNOSTIC
+				printf("%s: bad tag %d: "
+				    "[%d %d, %d %d, %*.*s %*.*s]\n",
+				    epp->ep_name,
+				    np->n_type
+				    np->n_namesz, ELF_NOTE_PAX_NAMESZ,
+				    np->n_descsz, ELF_NOTE_PAX_DESCSZ,
+				    ELF_NOTE_PAX_NAMESZ,
+				    ELF_NOTE_PAX_NAMESZ,
+				    ndata,
+				    ELF_NOTE_PAX_NAMESZ,
+				    ELF_NOTE_PAX_NAMESZ,
+				    ELF_NOTE_PAX_NAME);
+#endif
+				continue;
+			}
 			(void)memcpy(&epp->ep_pax_flags,
 			    ndata + ELF_NOTE_PAX_NAMESZ,
 			    sizeof(epp->ep_pax_flags));
 			break;
 
 		default:
+#ifdef DIAGNOSTIC
+			printf("%s: unknown note type %d\n", epp->ep_name,
+			    np->n_type);
+#endif
 			break;
 		}
-
-next:
-		kmem_free(np, ephp->p_filesz);
-		continue;
 	}
+	kmem_free(np, MAXNOTESIZE);
 
 	error = isnetbsd ? 0 : ENOEXEC;
 out:
-	kmem_free(ph, phsize);
+	kmem_free(sh, shsize);
 	return error;
 }
 

Reply via email to