Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 29 Jan 2016 21:51:31 +0000
From:      bugzilla-noreply@freebsd.org
To:        freebsd-bugs@FreeBSD.org
Subject:   [Bug 206749] Lack of checks on values in ELF headers in kernel linker
Message-ID:  <bug-206749-8@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D206749

            Bug ID: 206749
           Summary: Lack of checks on values in ELF headers in kernel
                    linker
           Product: Base System
           Version: 11.0-CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs@FreeBSD.org
          Reporter: cturt@hardenedbsd.org

There are a lack of checks performed on the `e_shnum` and `e_shentsize` val=
ues
read from input files in the kernel's ELF handling code.

For example, in `link_elf_ctf_get`:

static int
link_elf_ctf_get(linker_file_t lf, linker_ctf_t *lc)
{
        ...
        int nbytes;

        ...

        /* Allocate memory for the FLF header. */
        if ((hdr =3D malloc(sizeof(*hdr), M_LINKER, M_WAITOK)) =3D=3D NULL)=
 {
                error =3D ENOMEM;
                goto out;
        }

        /* Read the ELF header. */
        if ((error =3D vn_rdwr(UIO_READ, nd.ni_vp, hdr, sizeof(*hdr),
            0, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
            td)) !=3D 0)
                goto out;

        /* Sanity check. */
        if (!IS_ELF(*hdr)) {
                error =3D ENOEXEC;
                goto out;
        }

        nbytes =3D hdr->e_shnum * hdr->e_shentsize;
        if (nbytes =3D=3D 0 || hdr->e_shoff =3D=3D 0 ||
            hdr->e_shentsize !=3D sizeof(Elf_Shdr)) {
                error =3D ENOEXEC;
                goto out;
        }

        /* Allocate memory for all the section headers */
        if ((shdr =3D malloc(nbytes, M_LINKER, M_WAITOK)) =3D=3D NULL) {
                error =3D ENOMEM;
                goto out;
        }

        ...
}

The `e_shnum` and `e_shentsize` fields are declared as `Elf32_Half` (a
`typedef` for `uint16_t`) in the `Elf_Ehdr` structure (`sys/sys/elf32.h`):

           typedef struct {
                   unsigned char   e_ident[EI_NIDENT];
                   Elf32_Half      e_type;
                   Elf32_Half      e_machine;
                   Elf32_Word      e_version;
                   Elf32_Addr      e_entry;
                   Elf32_Off       e_phoff;
                   Elf32_Off       e_shoff;
                   Elf32_Word      e_flags;
                   Elf32_Half      e_ehsize;
                   Elf32_Half      e_phentsize;
                   Elf32_Half      e_phnum;
                   Elf32_Half      e_shentsize;
                   Elf32_Half      e_shnum;
                   Elf32_Half      e_shstrndx;
           } Elf32_Ehdr;

So consider if `0xffff` and `0x8001` are supplied for `e_shentsize` and
`e_shnum`:

    nbytes =3D hdr->e_shnum * hdr->e_shentsize;
    nbytes =3D 0xffff * 0x8001 =3D 0x80007FFF;

Since `nbytes` is `signed`, this will of course wrap around to `-0x7FFF8001=
`.

When `-0x7FFF8001` is passed to `malloc` it will be converted to an unsigned
64bit integer, giving an oversized allocation of `0xFFFFFFFF80007FFF`.

This poses no immediate security threat since the result from `malloc` is
checked, and will return `ENOMEM` for an overflown size.

This bug only applies to the `e_shnum` and `e_shentsize` members; the `e_ph=
num`
and `e_phentsize` members are correctly checked multiple times before use:

        if (!((hdr->e_phentsize =3D=3D sizeof(Elf_Phdr)) &&
              (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <=3D PAGE_SIZE)=
 &&
              (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <=3D nbytes)))
                link_elf_error(filename, "Unreadable program headers");

        ...

        /*    (multiplication of two Elf_Half fields will not overflow) */
        if ((hdr->e_phoff > PAGE_SIZE) ||
            (hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE - hdr->e_phoff) {
                error =3D ENOEXEC;
                goto fail;
        }

However, the same code to handle `e_shnum` and `e_shentsize` is reused mult=
iple
times, and so multiple functions have the same bug, for example
`link_elf_load_file` in `sys/kern/link_elf.c`:

static int
link_elf_load_file(linker_class_t cls, const char* filename,
    linker_file_t* result)
{
        ...
        int nbytes;

        ...

        /*
         * Read the elf header from the file.
         */
        firstpage =3D malloc(PAGE_SIZE, M_LINKER, M_WAITOK);
        hdr =3D (Elf_Ehdr *)firstpage;
        error =3D vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0,
            UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
            &resid, td);

        ...

        /*
         * Try and load the symbol table if it's present.  (you can
         * strip it!)
         */
        nbytes =3D hdr->e_shnum * hdr->e_shentsize;
        if (nbytes =3D=3D 0 || hdr->e_shoff =3D=3D 0)
                goto nosyms;
        shdr =3D malloc(nbytes, M_LINKER, M_WAITOK | Me_shnum_ZERO);
        error =3D vn_rdwr(UIO_READ, nd.ni_vp,
            (caddr_t)shdr, nbytes, hdr->e_shoff,
            UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
            &resid, td);
        if (error !=3D 0)
                goto out;
        symtabindex =3D -1;
        symstrindex =3D -1;
        for (i =3D 0; i < hdr->e_shnum; i++) {
                if (shdr[i].sh_type =3D=3D SHT_SYMTAB) {
                        symtabindex =3D i;
                        symstrindex =3D shdr[i].sh_link;
                }
        }e_shnum

        ...
}

And `link_elf_load_file` from `sys/kern/link_elf_obj.c`.

--=20
You are receiving this mail because:
You are the assignee for the bug.=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-206749-8>