Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/kern netbsd_elf_signature - look at note segments (phdrs...



details:   https://anonhg.NetBSD.org/src/rev/89e59a6b0ce2
branches:  trunk
changeset: 821673:89e59a6b0ce2
user:      uwe <uwe%NetBSD.org@localhost>
date:      Sun Feb 12 21:52:46 2017 +0000

description:
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.

diffstat:

 sys/kern/exec_elf.c |  349 +++++++++++++++++++++++++++------------------------
 1 files changed, 183 insertions(+), 166 deletions(-)

diffs (truncated from 409 to 300 lines):

diff -r a28092c935fc -r 89e59a6b0ce2 sys/kern/exec_elf.c
--- a/sys/kern/exec_elf.c       Sun Feb 12 21:06:46 2017 +0000
+++ b/sys/kern/exec_elf.c       Sun Feb 12 21:52:46 2017 +0000
@@ -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 @@
 #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_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,13 +854,90 @@
     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;
-       
+
+       epp->ep_pax_flags = 0;
+
+       if (eh->e_phnum > ELF_MAXPHNUM || eh->e_phnum == 0) {
+               DPRINTF("no signature %#x", eh->e_phnum);
+               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);
+       if (error)
+               goto out;
+
+       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;
+
+               nlen = ph[i].p_filesz;
+               error = exec_read_from(l, epp->ep_vp, ph[i].p_offset,
+                                      nbuf, nlen);
+               if (error)
+                       continue;
+
+               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);
+
+                       /* note name */
+                       ndata = nptr;
+                       if (nlen < roundup(np->n_namesz, 4)) {
+                               break;
+                       }
+                       nptr += roundup(np->n_namesz, 4);
+                       nlen -= roundup(np->n_namesz, 4);
+
+                       /* note description */
+                       ndesc = nptr;
+                       if (nlen < roundup(np->n_descsz, 4)) {
+                               break;
+                       }
+                       nptr += roundup(np->n_descsz, 4);
+                       nlen -= roundup(np->n_descsz, 4);
+
+                       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)
@@ -865,181 +945,118 @@
 #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);
-               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);
-       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)
-                       continue;
-
-               error = exec_read_from(l, epp->ep_vp, shp->sh_offset, np,
-                   shp->sh_size);
-               if (error)
-                       continue;
-
-               /* Point to the note, skip the header */
-               ndata = (char *)(np + 1);
+       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;
+               }
 
                /*
-                * 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.
+                * Ignore SuSE tags; SuSE's n_type is the same the
+                * NetBSD one.
                 */
-               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);
+               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;
 
-               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;
-                       }
-
-                       /*
-                        * 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);
+                       break;
+               }
+               BADNOTE("PaX 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);
-                               break;
+       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;
                        }
-                       BADNOTE("PaX tag");
-                       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;
 



Home | Main Index | Thread Index | Old Index