Module Name:    src
Committed By:   uwe
Date:           Sun Feb 12 21:52:46 UTC 2017

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

Log Message:
netbsd_elf_signature - look at note segments (phdrs) not note
sections.  They point to the same data in the file, but sections are
for linkers and are not necessarily present in an executable.

The original switch from phdrs to shdrs seems to be just a cop-out to
avoid parsing multiple notes per segment, which doesn't really avoid
the problem b/c sections also can contain multiple notes.


To generate a diff of this commit:
cvs rdiff -u -r1.87 -r1.88 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.87 src/sys/kern/exec_elf.c:1.88
--- src/sys/kern/exec_elf.c:1.87	Thu Sep 15 18:40:34 2016
+++ src/sys/kern/exec_elf.c	Sun Feb 12 21:52:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec_elf.c,v 1.87 2016/09/15 18:40:34 christos Exp $	*/
+/*	$NetBSD: exec_elf.c,v 1.88 2017/02/12 21:52:46 uwe Exp $	*/
 
 /*-
  * Copyright (c) 1994, 2000, 2005, 2015 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.87 2016/09/15 18:40:34 christos Exp $");
+__KERNEL_RCSID(1, "$NetBSD: exec_elf.c,v 1.88 2017/02/12 21:52:46 uwe Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_pax.h"
@@ -93,6 +93,7 @@ extern struct emul emul_netbsd;
 #define elf_load_psection	ELFNAME(load_psection)
 #define exec_elf_makecmds	ELFNAME2(exec,makecmds)
 #define netbsd_elf_signature	ELFNAME2(netbsd,signature)
+#define netbsd_elf_note       	ELFNAME2(netbsd,note)
 #define netbsd_elf_probe	ELFNAME2(netbsd,probe)
 #define	coredump		ELFNAMEEND(coredump)
 #define	elf_free_emul_arg	ELFNAME(free_emul_arg)
@@ -105,6 +106,8 @@ elf_load_psection(struct exec_vmcmd_set 
     Elf_Addr *, u_long *, int);
 
 int	netbsd_elf_signature(struct lwp *, struct exec_package *, Elf_Ehdr *);
+int	netbsd_elf_note(struct exec_package *, const Elf_Nhdr *, const char *,
+	    const char *);
 int	netbsd_elf_probe(struct lwp *, struct exec_package *, void *, char *,
 	    vaddr_t *);
 
@@ -851,195 +854,209 @@ netbsd_elf_signature(struct lwp *l, stru
     Elf_Ehdr *eh)
 {
 	size_t i;
-	Elf_Shdr *sh;
-	Elf_Nhdr *np;
-	size_t shsize, nsize;
+	Elf_Phdr *ph;
+	size_t phsize;
+	char *nbuf;
 	int error;
 	int isnetbsd = 0;
-	char *ndata, *ndesc;
-	
-#ifdef DIAGNOSTIC
-	const char *badnote;
-#define BADNOTE(n) badnote = (n)
-#else
-#define BADNOTE(n)
-#endif
 
 	epp->ep_pax_flags = 0;
-	if (eh->e_shnum > ELF_MAXSHNUM || eh->e_shnum == 0) {
-		DPRINTF("no signature %#x", eh->e_shnum);
+
+	if (eh->e_phnum > ELF_MAXPHNUM || eh->e_phnum == 0) {
+		DPRINTF("no signature %#x", eh->e_phnum);
 		return ENOEXEC;
 	}
 
-	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);
+	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);
 	if (error)
 		goto out;
 
-	np = kmem_alloc(ELF_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 > ELF_MAXNOTESIZE ||
-		    shp->sh_size < sizeof(Elf_Nhdr) + ELF_NOTE_NETBSD_NAMESZ)
+	nbuf = kmem_alloc(ELF_MAXNOTESIZE, KM_SLEEP);
+	for (i = 0; i < eh->e_phnum; i++) {
+		const char *nptr;
+		size_t nlen;
+
+		if (ph[i].p_type != PT_NOTE ||
+		    ph[i].p_filesz > ELF_MAXNOTESIZE)
 			continue;
 
-		error = exec_read_from(l, epp->ep_vp, shp->sh_offset, np,
-		    shp->sh_size);
+		nlen = ph[i].p_filesz;
+		error = exec_read_from(l, epp->ep_vp, ph[i].p_offset,
+				       nbuf, nlen);
 		if (error)
 			continue;
 
-		/* Point to the note, skip the header */
-		ndata = (char *)(np + 1);
-
-		/*
-		 * Padding is present if necessary to ensure 4-byte alignment.
-		 * The actual section size is therefore:
-		 *    header size + 4-byte aligned name + 4-byte aligned desc
-		 * Ensure this size is consistent with what is indicated
-		 * in sh_size. The first check avoids integer overflows.
-		 *
-		 * Binaries from before NetBSD 1.6 have two notes in the same
-		 * note section.  The second note was never used, so as long as
-		 * the section is at least as big as it should be, it's ok.
-		 * These binaries also have a second note section with a note of
-		 * type ELF_NOTE_TYPE_NETBSD_TAG, which can be ignored as well.
-		 */
-		if (np->n_namesz > shp->sh_size || np->n_descsz > shp->sh_size) {
-			BADNOTE("note size limit");
-			goto bad;
-		}
-		nsize = sizeof(*np) + roundup(np->n_namesz, 4) +
-		    roundup(np->n_descsz, 4);
-		if (nsize > shp->sh_size) {
-			BADNOTE("note size");
-			goto bad;
-		}
-		ndesc = ndata + roundup(np->n_namesz, 4);
-
-		switch (np->n_type) {
-		case ELF_NOTE_TYPE_NETBSD_TAG:
-			/* It is us */
-			if (np->n_namesz == ELF_NOTE_NETBSD_NAMESZ &&
-			    np->n_descsz == ELF_NOTE_NETBSD_DESCSZ &&
-			    memcmp(ndata, ELF_NOTE_NETBSD_NAME,
-			    ELF_NOTE_NETBSD_NAMESZ) == 0) {
-				memcpy(&epp->ep_osversion, ndesc,
-				    ELF_NOTE_NETBSD_DESCSZ);
-				isnetbsd = 1;
+		nptr = nbuf;
+		while (nlen > 0) {
+			const Elf_Nhdr *np;
+			const char *ndata, *ndesc;
+
+			/* note header */
+			np = (const Elf_Nhdr *)nptr;
+			if (nlen < sizeof(*np)) {
 				break;
 			}
+			nptr += sizeof(*np);
+			nlen -= sizeof(*np);
 
-			/*
-			 * Ignore SuSE tags; SuSE's n_type is the same the
-			 * NetBSD one.
-			 */
-			if (np->n_namesz == ELF_NOTE_SUSE_NAMESZ &&
-			    memcmp(ndata, ELF_NOTE_SUSE_NAME,
-			    ELF_NOTE_SUSE_NAMESZ) == 0)
-				break;
-			/*
-			 * Ignore old GCC
-			 */
-			if (np->n_namesz == ELF_NOTE_OGCC_NAMESZ &&
-			    memcmp(ndata, ELF_NOTE_OGCC_NAME,
-			    ELF_NOTE_OGCC_NAMESZ) == 0)
-				break;
-			BADNOTE("NetBSD tag");
-			goto bad;
-
-		case ELF_NOTE_TYPE_PAX_TAG:
-			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) == 0) {
-				uint32_t flags;
-				memcpy(&flags, ndesc, sizeof(flags));
-				/* Convert the flags and insert them into
-				 * the exec package. */
-				pax_setup_elf_flags(epp, flags);
+			/* note name */
+			ndata = nptr;
+			if (nlen < roundup(np->n_namesz, 4)) {
 				break;
 			}
-			BADNOTE("PaX tag");
-			goto bad;
+			nptr += roundup(np->n_namesz, 4);
+			nlen -= roundup(np->n_namesz, 4);
 
-		case ELF_NOTE_TYPE_MARCH_TAG:
-			/* Copy the machine arch into the package. */
-			if (np->n_namesz == ELF_NOTE_MARCH_NAMESZ
-			    && memcmp(ndata, ELF_NOTE_MARCH_NAME,
-				    ELF_NOTE_MARCH_NAMESZ) == 0) {
-				/* Do not truncate the buffer */
-				if (np->n_descsz > sizeof(epp->ep_machine_arch)) {
-					BADNOTE("description size limit");
-					goto bad;
-				}
-				/*
-				 * Ensure ndesc is NUL-terminated and of the
-				 * expected length.
-				 */
-				if (strnlen(ndesc, np->n_descsz) + 1 !=
-				    np->n_descsz) {
-					BADNOTE("description size");
-					goto bad;
-				}
-				strlcpy(epp->ep_machine_arch, ndesc,
-				    sizeof(epp->ep_machine_arch));
+			/* note description */
+			ndesc = nptr;
+			if (nlen < roundup(np->n_descsz, 4)) {
 				break;
 			}
-			BADNOTE("march tag");
-			goto bad;
+			nptr += roundup(np->n_descsz, 4);
+			nlen -= roundup(np->n_descsz, 4);
 
-		case ELF_NOTE_TYPE_MCMODEL_TAG:
-			/* arch specific check for code model */
-#ifdef ELF_MD_MCMODEL_CHECK
-			if (np->n_namesz == ELF_NOTE_MCMODEL_NAMESZ
-			    && memcmp(ndata, ELF_NOTE_MCMODEL_NAME,
-				    ELF_NOTE_MCMODEL_NAMESZ) == 0) {
-				ELF_MD_MCMODEL_CHECK(epp, ndesc, np->n_descsz);
-				break;
-			}
-			BADNOTE("mcmodel tag");
-			goto bad;
+			isnetbsd |= netbsd_elf_note(epp, np, ndata, ndesc);
+		}
+	}
+	kmem_free(nbuf, ELF_MAXNOTESIZE);
+
+	error = isnetbsd ? 0 : ENOEXEC;
+#ifdef DEBUG_ELF
+	if (error)
+		DPRINTF("not netbsd");
+#endif
+out:
+	kmem_free(ph, phsize);
+	return error;
+}
+
+int
+netbsd_elf_note(struct exec_package *epp,
+		const Elf_Nhdr *np, const char *ndata, const char *ndesc)
+{
+	int isnetbsd = 0;
+
+#ifdef DIAGNOSTIC
+	const char *badnote;
+#define BADNOTE(n) badnote = (n)
+#else
+#define BADNOTE(n)
 #endif
+
+	switch (np->n_type) {
+	case ELF_NOTE_TYPE_NETBSD_TAG:
+		/* It is us */
+		if (np->n_namesz == ELF_NOTE_NETBSD_NAMESZ &&
+		    np->n_descsz == ELF_NOTE_NETBSD_DESCSZ &&
+		    memcmp(ndata, ELF_NOTE_NETBSD_NAME,
+		    ELF_NOTE_NETBSD_NAMESZ) == 0) {
+			memcpy(&epp->ep_osversion, ndesc,
+			    ELF_NOTE_NETBSD_DESCSZ);
+			isnetbsd = 1;
 			break;
+		}
 
-		case ELF_NOTE_TYPE_SUSE_VERSION_TAG:
+		/*
+		 * Ignore SuSE tags; SuSE's n_type is the same the
+		 * NetBSD one.
+		 */
+		if (np->n_namesz == ELF_NOTE_SUSE_NAMESZ &&
+		    memcmp(ndata, ELF_NOTE_SUSE_NAME,
+		    ELF_NOTE_SUSE_NAMESZ) == 0)
+			break;
+		/*
+		 * Ignore old GCC
+		 */
+		if (np->n_namesz == ELF_NOTE_OGCC_NAMESZ &&
+		    memcmp(ndata, ELF_NOTE_OGCC_NAME,
+		    ELF_NOTE_OGCC_NAMESZ) == 0)
 			break;
+		BADNOTE("NetBSD tag");
+		goto bad;
 
-		case ELF_NOTE_TYPE_GO_BUILDID_TAG:
+	case ELF_NOTE_TYPE_PAX_TAG:
+		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) == 0) {
+			uint32_t flags;
+			memcpy(&flags, ndesc, sizeof(flags));
+			/* Convert the flags and insert them into
+			 * the exec package. */
+			pax_setup_elf_flags(epp, flags);
 			break;
+		}
+		BADNOTE("PaX tag");
+		goto bad;
 
-		default:
-			BADNOTE("unknown tag");
+	case ELF_NOTE_TYPE_MARCH_TAG:
+		/* Copy the machine arch into the package. */
+		if (np->n_namesz == ELF_NOTE_MARCH_NAMESZ
+		    && memcmp(ndata, ELF_NOTE_MARCH_NAME,
+			    ELF_NOTE_MARCH_NAMESZ) == 0) {
+			/* Do not truncate the buffer */
+			if (np->n_descsz > sizeof(epp->ep_machine_arch)) {
+				BADNOTE("description size limit");
+				goto bad;
+			}
+			/*
+			 * Ensure ndesc is NUL-terminated and of the
+			 * expected length.
+			 */
+			if (strnlen(ndesc, np->n_descsz) + 1 !=
+			    np->n_descsz) {
+				BADNOTE("description size");
+				goto bad;
+			}
+			strlcpy(epp->ep_machine_arch, ndesc,
+			    sizeof(epp->ep_machine_arch));
+			break;
+		}
+		BADNOTE("march tag");
+		goto bad;
+
+	case ELF_NOTE_TYPE_MCMODEL_TAG:
+		/* arch specific check for code model */
+#ifdef ELF_MD_MCMODEL_CHECK
+		if (np->n_namesz == ELF_NOTE_MCMODEL_NAMESZ
+		    && memcmp(ndata, ELF_NOTE_MCMODEL_NAME,
+			    ELF_NOTE_MCMODEL_NAMESZ) == 0) {
+			ELF_MD_MCMODEL_CHECK(epp, ndesc, np->n_descsz);
+			break;
+		}
+		BADNOTE("mcmodel tag");
+		goto bad;
+#endif
+		break;
+
+	case ELF_NOTE_TYPE_SUSE_VERSION_TAG:
+		break;
+
+	case ELF_NOTE_TYPE_GO_BUILDID_TAG:
+		break;
+
+	default:
+		BADNOTE("unknown tag");
 bad:
 #ifdef DIAGNOSTIC
-			/* Ignore GNU tags */
-			if (np->n_namesz == ELF_NOTE_GNU_NAMESZ &&
-			    memcmp(ndata, ELF_NOTE_GNU_NAME,
-			    ELF_NOTE_GNU_NAMESZ) == 0)
-			    break;
-
-			int ns = MIN(np->n_namesz, shp->sh_size - sizeof(*np));
-			printf("%s: Unknown elf note type %d (%s): "
-			    "[namesz=%d, descsz=%d name=%-*.*s]\n",
-			    epp->ep_kname, np->n_type, badnote, np->n_namesz,
-			    np->n_descsz, ns, ns, ndata);
+		/* Ignore GNU tags */
+		if (np->n_namesz == ELF_NOTE_GNU_NAMESZ &&
+		    memcmp(ndata, ELF_NOTE_GNU_NAME,
+		    ELF_NOTE_GNU_NAMESZ) == 0)
+		    break;
+
+		int ns = (int)np->n_namesz;
+		printf("%s: Unknown elf note type %d (%s): "
+		    "[namesz=%d, descsz=%d name=%-*.*s]\n",
+		    epp->ep_kname, np->n_type, badnote, np->n_namesz,
+		    np->n_descsz, ns, ns, ndata);
 #endif
-			break;
-		}
+		break;
 	}
-	kmem_free(np, ELF_MAXNOTESIZE);
 
-	error = isnetbsd ? 0 : ENOEXEC;
-#ifdef DEBUG_ELF
-	if (error)
-		DPRINTF("not netbsd");
-#endif
-out:
-	kmem_free(sh, shsize);
-	return error;
+	return isnetbsd;
 }
 
 int

Reply via email to