public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: "H.J. Lu" <hjl.tools@gmail.com>
To: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Cc: GNU C Library <libc-alpha@sourceware.org>,
	Florian Weimer <fweimer@redhat.com>
Subject: Re: [PATCH] elf: Add elf checks for main executable
Date: Mon, 6 Dec 2021 11:03:55 -0800	[thread overview]
Message-ID: <CAMe9rOprcRWXG4Eab0FWfN3WNTU2R2AYdh=asMBgsNg-exqZGg@mail.gmail.com> (raw)
In-Reply-To: <20211119150329.2200675-1-adhemerval.zanella@linaro.org>

On Fri, Nov 19, 2021 at 7:03 AM Adhemerval Zanella
<adhemerval.zanella@linaro.org> wrote:
>
> The ELF header integrity check is only done on open_verify(), i.e,
> for objects explicitly loaded.  For main executable (issued with
> execve() for the binary) only kernel checks are done, which does
> not check EI_ABIVERSION.
>
> To enable it, the loader needs to find where the ELF header is placed
> at program start, however Linux auxiliary vectors only provides
> the program header table (AT_EHDR).  To avoid require upstream
> kernel support, the ELF header is implicitly obtained from the PT_LOAD
> values by checking for a segment with offset 0 and memory size
> different than 0 (For Linux it is start the ELF file issued by
> execve()).
>
> Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
> ---
>  elf/Makefile                           |   8 +-
>  elf/dl-check-err.h                     |  14 ++
>  elf/dl-check.c                         | 151 ++++++++++++++++++
>  elf/dl-check.h                         |  45 ++++++
>  elf/dl-load.c                          | 114 ++------------
>  elf/rtld.c                             |   8 +
>  elf/tst-elf-check.c                    | 209 +++++++++++++++++++++++++
>  sysdeps/generic/dl-elf-check.h         |  28 ++++
>  sysdeps/unix/sysv/linux/dl-elf-check.h |  32 ++++
>  9 files changed, 507 insertions(+), 102 deletions(-)
>  create mode 100644 elf/dl-check-err.h
>  create mode 100644 elf/dl-check.c
>  create mode 100644 elf/dl-check.h
>  create mode 100644 elf/tst-elf-check.c
>  create mode 100644 sysdeps/generic/dl-elf-check.h
>  create mode 100644 sysdeps/unix/sysv/linux/dl-elf-check.h
>
> diff --git a/elf/Makefile b/elf/Makefile
> index 4723c159cb..f09fc5c6ec 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -36,7 +36,8 @@ dl-routines   = $(addprefix dl-,load lookup object reloc deps \
>                                   exception sort-maps lookup-direct \
>                                   call-libc-early-init write \
>                                   thread_gscope_wait tls_init_tp \
> -                                 debug-symbols minimal-malloc)
> +                                 debug-symbols minimal-malloc \
> +                                 check)
>  ifeq (yes,$(use-ldconfig))
>  dl-routines += dl-cache
>  endif
> @@ -238,7 +239,8 @@ tests-internal += loadtest unload unload2 circleload1 \
>          tst-ptrguard1 tst-stackguard1 \
>          tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
>  tests-container += tst-pldd tst-dlopen-tlsmodid-container \
> -  tst-dlopen-self-container tst-preload-pthread-libc
> +  tst-dlopen-self-container tst-preload-pthread-libc \
> +  tst-elf-check
>  test-srcs = tst-pathopt
>  selinux-enabled := $(shell cat /selinux/enforce 2> /dev/null)
>  ifneq ($(selinux-enabled),1)
> @@ -494,6 +496,8 @@ tests-special += $(objpfx)order-cmp.out $(objpfx)tst-array1-cmp.out \
>                  $(objpfx)tst-unused-dep-cmp.out
>  endif
>
> +tst-elf-check-ARGS = -- $(host-test-program-cmd)
> +
>  ifndef avoid-generated
>  # DSO sorting tests:
>  # The dso-ordering-test.py script generates testcase source files in $(objpfx),
> diff --git a/elf/dl-check-err.h b/elf/dl-check-err.h
> new file mode 100644
> index 0000000000..6ca5246eb8
> --- /dev/null
> +++ b/elf/dl-check-err.h
> @@ -0,0 +1,14 @@
> +_S(DL_ELFHDR_OK, "")
> +_S(DL_ELFHDR_ERR_ELFMAG,     N_("invalid ELF header"))
> +_S(DL_ELFHDR_ERR_CLASS32,    N_("wrong ELF class: ELFCLASS32"))
> +_S(DL_ELFHDR_ERR_CLASS64,    N_("wrong ELF class: ELFCLASS64"))
> +_S(DL_ELFHDR_ERR_BENDIAN,    N_("ELF file data encoding not big-endian"))
> +_S(DL_ELFHDR_ERR_LENDIAN,    N_("ELF file data encoding not little-endian"))
> +_S(DL_ELFHDR_ERR_EIVERSION,  N_("ELF file version ident does not match current one"))
> +_S(DL_ELFHDR_ERR_OSABI,      N_("ELF file OS ABI invalid"))
> +_S(DL_ELFHDR_ERR_ABIVERSION, N_("ELF file ABI version invalid"))
> +_S(DL_ELFHDR_ERR_PAD,        N_("nonzero padding in e_ident"))
> +_S(DL_ELFHDR_ERR_VERSION,    N_("ELF file version does not match current one"))
> +_S(DL_ELFHDR_ERR_TYPE,       N_("only ET_DYN and ET_EXEC can be loaded"))
> +_S(DL_ELFHDR_ERR_PHENTSIZE,  N_("ELF file's phentsize not the expected size"))
> +_S(DL_ELFHDR_ERR_INTERNAL,   N_("internal error"))
> diff --git a/elf/dl-check.c b/elf/dl-check.c
> new file mode 100644
> index 0000000000..ef1720df2a
> --- /dev/null
> +++ b/elf/dl-check.c
> @@ -0,0 +1,151 @@
> +/* ELF header consistency and ABI checks.
> +   Copyright (C) 1995-2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <array_length.h>
> +#include <dl-check.h>
> +#include <endian.h>
> +#include <ldsodefs.h>
> +#include <libintl.h>
> +
> +int
> +_dl_elfhdr_check (const ElfW(Ehdr) *ehdr)
> +{
> +#define ELF32_CLASS ELFCLASS32
> +#define ELF64_CLASS ELFCLASS64
> +#if BYTE_ORDER == BIG_ENDIAN
> +# define byteorder ELFDATA2MSB
> +#elif BYTE_ORDER == LITTLE_ENDIAN
> +# define byteorder ELFDATA2LSB
> +#else
> +# error "Unknown BYTE_ORDER " BYTE_ORDER
> +# define byteorder ELFDATANONE
> +#endif
> +  MORE_ELF_HEADER_DATA;
> +  static const unsigned char expected[EI_NIDENT] =
> +  {
> +    [EI_MAG0] = ELFMAG0,
> +    [EI_MAG1] = ELFMAG1,
> +    [EI_MAG2] = ELFMAG2,
> +    [EI_MAG3] = ELFMAG3,
> +    [EI_CLASS] = ELFW(CLASS),
> +    [EI_DATA] = byteorder,
> +    [EI_VERSION] = EV_CURRENT,
> +    [EI_OSABI] = ELFOSABI_SYSV,
> +    [EI_ABIVERSION] = 0
> +  };
> +
> +  /* See whether the ELF header is what we expect.  */
> +  if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected,
> +                                           EI_ABIVERSION)
> +                       || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
> +                                                 ehdr->e_ident[EI_ABIVERSION])
> +                       || memcmp (&ehdr->e_ident[EI_PAD],
> +                                  &expected[EI_PAD],
> +                                  EI_NIDENT - EI_PAD) != 0))
> +    {
> +      /* Something is wrong.  */
> +      const Elf32_Word *magp = (const void *) ehdr->e_ident;
> +      if (*magp !=
> +#if BYTE_ORDER == LITTLE_ENDIAN
> +         ((ELFMAG0 << (EI_MAG0 * 8))
> +          | (ELFMAG1 << (EI_MAG1 * 8))
> +          | (ELFMAG2 << (EI_MAG2 * 8))
> +          | (ELFMAG3 << (EI_MAG3 * 8)))
> +#else
> +         ((ELFMAG0 << (EI_MAG3 * 8))
> +          | (ELFMAG1 << (EI_MAG2 * 8))
> +          | (ELFMAG2 << (EI_MAG1 * 8))
> +          | (ELFMAG3 << (EI_MAG0 * 8)))
> +#endif
> +         )
> +       return DL_ELFHDR_ERR_ELFMAG;
> +      else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
> +       return ELFW(CLASS) == ELFCLASS32
> +              ? DL_ELFHDR_ERR_CLASS64
> +              : DL_ELFHDR_ERR_CLASS32;
> +      else if (ehdr->e_ident[EI_DATA] != byteorder)
> +       {
> +         if (BYTE_ORDER == BIG_ENDIAN)
> +           return DL_ELFHDR_ERR_BENDIAN;
> +         else
> +           return DL_ELFHDR_ERR_LENDIAN;
> +       }
> +      else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
> +       return DL_ELFHDR_ERR_EIVERSION;
> +      /* XXX We should be able so set system specific versions which are
> +        allowed here.  */
> +      else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
> +       return DL_ELFHDR_ERR_OSABI;
> +      else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
> +                                     ehdr->e_ident[EI_ABIVERSION]))
> +       return DL_ELFHDR_ERR_ABIVERSION;
> +      else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD],
> +                      EI_NIDENT - EI_PAD) != 0)
> +       return DL_ELFHDR_ERR_PAD;
> +      else
> +       return DL_ELFHDR_ERR_INTERNAL;
> +    }
> +
> +  if (__glibc_unlikely (ehdr->e_version != EV_CURRENT))
> +    return DL_ELFHDR_ERR_VERSION;
> +  else if (__glibc_unlikely (ehdr->e_type != ET_DYN
> +                            && ehdr->e_type != ET_EXEC))
> +    return DL_ELFHDR_ERR_TYPE;
> +  else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr))))
> +    return DL_ELFHDR_ERR_PHENTSIZE;
> +
> +  return DL_ELFHDR_OK;
> +}
> +
> +static const union elfhdr_errstr_t
> +{
> +  struct
> +  {
> +#define _S(n, s) char str##n[sizeof (s)];
> +#include "dl-check-err.h"
> +#undef _S
> +  };
> +  char str[0];
> +} elfhdr_errstr =
> +{
> +  {
> +#define _S(n, s) s,
> +#include "dl-check-err.h"
> +#undef _S
> +  }
> +};
> +
> +static const unsigned short elfhder_erridx[] =
> +{
> +#define _S(n, s) [n] = offsetof(union elfhdr_errstr_t, str##n),
> +#include "dl-check-err.h"
> +#undef _S
> +};
> +
> +const char *
> +_dl_elfhdr_errstr (int err)
> +{
> +#if 0
> +  if (err >= 0 && err < array_length (elfhdr_errstr))
> +    return elfhdr_errstr[err];
> +  return NULL;
> +#endif
> +  if (err < 0 || err >= array_length (elfhder_erridx))
> +    err = 0;
> +  return elfhdr_errstr.str + elfhder_erridx[err];
> +}
> diff --git a/elf/dl-check.h b/elf/dl-check.h
> new file mode 100644
> index 0000000000..5104a353ed
> --- /dev/null
> +++ b/elf/dl-check.h
> @@ -0,0 +1,45 @@
> +/* ELF header consistency and ABI checks.
> +   Copyright (C) 1995-2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef _DL_OPENCHECK_H
> +#define _DL_OPENCHECK_H
> +
> +#include <link.h>
> +
> +enum
> + {
> +   DL_ELFHDR_OK,
> +   DL_ELFHDR_ERR_ELFMAG,     /* Invalid ELFMAGX value.  */
> +   DL_ELFHDR_ERR_CLASS32,    /* Mismatched EI_CLASS.  */
> +   DL_ELFHDR_ERR_CLASS64,    /* Mismatched EI_CLASS.  */
> +   DL_ELFHDR_ERR_BENDIAN,    /* Mismatched EI_DATA (not big-endian).  */
> +   DL_ELFHDR_ERR_LENDIAN,    /* Mismatched EI_DATA (not little-endian).  */
> +   DL_ELFHDR_ERR_EIVERSION,  /* Invalid EI_VERSION.  */
> +   DL_ELFHDR_ERR_OSABI,      /* Invalid EI_OSABI.  */
> +   DL_ELFHDR_ERR_ABIVERSION, /* Invalid ABI vrsion.  */
> +   DL_ELFHDR_ERR_PAD,        /* Invalid EI_PAD value.  */
> +   DL_ELFHDR_ERR_VERSION,    /* Invalid e_version.  */
> +   DL_ELFHDR_ERR_TYPE,       /* Invalid e_type.  */
> +   DL_ELFHDR_ERR_PHENTSIZE,  /* Invalid e_phentsize.  */
> +   DL_ELFHDR_ERR_INTERNAL    /* Internal error.  */
> + };
> +
> +int _dl_elfhdr_check (const ElfW(Ehdr) *ehdr) attribute_hidden;
> +const char *_dl_elfhdr_errstr (int err) attribute_hidden;
> +
> +#endif
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index bf8957e73c..45266c3501 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -73,19 +73,9 @@ struct filebuf
>  #include <dl-machine-reject-phdr.h>
>  #include <dl-sysdep-open.h>
>  #include <dl-prop.h>
> +#include <dl-check.h>
>  #include <not-cancel.h>
>
> -#include <endian.h>
> -#if BYTE_ORDER == BIG_ENDIAN
> -# define byteorder ELFDATA2MSB
> -#elif BYTE_ORDER == LITTLE_ENDIAN
> -# define byteorder ELFDATA2LSB
> -#else
> -# error "Unknown BYTE_ORDER " BYTE_ORDER
> -# define byteorder ELFDATANONE
> -#endif
> -
> -#define STRING(x) __STRING (x)
>
>
>  int __stack_prot attribute_hidden attribute_relro
> @@ -1598,25 +1588,6 @@ open_verify (const char *name, int fd,
>    /* This is the expected ELF header.  */
>  #define ELF32_CLASS ELFCLASS32
>  #define ELF64_CLASS ELFCLASS64
> -#ifndef VALID_ELF_HEADER
> -# define VALID_ELF_HEADER(hdr,exp,size)        (memcmp (hdr, exp, size) == 0)
> -# define VALID_ELF_OSABI(osabi)                (osabi == ELFOSABI_SYSV)
> -# define VALID_ELF_ABIVERSION(osabi,ver) (ver == 0)
> -#elif defined MORE_ELF_HEADER_DATA
> -  MORE_ELF_HEADER_DATA;
> -#endif
> -  static const unsigned char expected[EI_NIDENT] =
> -  {
> -    [EI_MAG0] = ELFMAG0,
> -    [EI_MAG1] = ELFMAG1,
> -    [EI_MAG2] = ELFMAG2,
> -    [EI_MAG3] = ELFMAG3,
> -    [EI_CLASS] = ELFW(CLASS),
> -    [EI_DATA] = byteorder,
> -    [EI_VERSION] = EV_CURRENT,
> -    [EI_OSABI] = ELFOSABI_SYSV,
> -    [EI_ABIVERSION] = 0
> -  };
>    static const struct
>    {
>      ElfW(Word) vendorlen;
> @@ -1709,83 +1680,26 @@ open_verify (const char *name, int fd,
>         }
>
>        /* See whether the ELF header is what we expect.  */
> -      if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected,
> -                                               EI_ABIVERSION)
> -                           || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
> -                                                     ehdr->e_ident[EI_ABIVERSION])
> -                           || memcmp (&ehdr->e_ident[EI_PAD],
> -                                      &expected[EI_PAD],
> -                                      EI_NIDENT - EI_PAD) != 0))
> +      int err = _dl_elfhdr_check (ehdr);
> +      switch (err)
>         {
> -         /* Something is wrong.  */
> -         const Elf32_Word *magp = (const void *) ehdr->e_ident;
> -         if (*magp !=
> -#if BYTE_ORDER == LITTLE_ENDIAN
> -             ((ELFMAG0 << (EI_MAG0 * 8))
> -              | (ELFMAG1 << (EI_MAG1 * 8))
> -              | (ELFMAG2 << (EI_MAG2 * 8))
> -              | (ELFMAG3 << (EI_MAG3 * 8)))
> -#else
> -             ((ELFMAG0 << (EI_MAG3 * 8))
> -              | (ELFMAG1 << (EI_MAG2 * 8))
> -              | (ELFMAG2 << (EI_MAG1 * 8))
> -              | (ELFMAG3 << (EI_MAG0 * 8)))
> -#endif
> -             )
> -           errstring = N_("invalid ELF header");
> -         else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
> -           {
> -             /* This is not a fatal error.  On architectures where
> -                32-bit and 64-bit binaries can be run this might
> -                happen.  */
> -             *found_other_class = true;
> -             goto close_and_out;
> -           }
> -         else if (ehdr->e_ident[EI_DATA] != byteorder)
> -           {
> -             if (BYTE_ORDER == BIG_ENDIAN)
> -               errstring = N_("ELF file data encoding not big-endian");
> -             else
> -               errstring = N_("ELF file data encoding not little-endian");
> -           }
> -         else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
> -           errstring
> -             = N_("ELF file version ident does not match current one");
> -         /* XXX We should be able so set system specific versions which are
> -            allowed here.  */
> -         else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
> -           errstring = N_("ELF file OS ABI invalid");
> -         else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
> -                                         ehdr->e_ident[EI_ABIVERSION]))
> -           errstring = N_("ELF file ABI version invalid");
> -         else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD],
> -                          EI_NIDENT - EI_PAD) != 0)
> -           errstring = N_("nonzero padding in e_ident");
> -         else
> -           /* Otherwise we don't know what went wrong.  */
> -           errstring = N_("internal error");
> +       case DL_ELFHDR_OK:
> +         break;
>
> -         goto lose;
> -       }
> +       case DL_ELFHDR_ERR_CLASS32:
> +       case DL_ELFHDR_ERR_CLASS64:
> +         /* This is not a fatal error.  On architectures where 32-bit and
> +            64-bit binaries can be run this might happen.  */
> +         *found_other_class = true;
> +         goto close_and_out;
>
> -      if (__glibc_unlikely (ehdr->e_version != EV_CURRENT))
> -       {
> -         errstring = N_("ELF file version does not match current one");
> +       default:
> +         errstring = _dl_elfhdr_errstr (err);
>           goto lose;
>         }
> +
>        if (! __glibc_likely (elf_machine_matches_host (ehdr)))
>         goto close_and_out;
> -      else if (__glibc_unlikely (ehdr->e_type != ET_DYN
> -                                && ehdr->e_type != ET_EXEC))
> -       {
> -         errstring = N_("only ET_DYN and ET_EXEC can be loaded");
> -         goto lose;
> -       }
> -      else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr))))
> -       {
> -         errstring = N_("ELF file's phentsize not the expected size");
> -         goto lose;
> -       }
>
>        maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
>        if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 847141e21d..89b3157f31 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -50,6 +50,7 @@
>  #include <gnu/lib-names.h>
>  #include <dl-tunables.h>
>  #include <get-dynamic-info.h>
> +#include <dl-elf-check.h>
>
>  #include <assert.h>
>
> @@ -1112,6 +1113,7 @@ dl_main (const ElfW(Phdr) *phdr,
>          ElfW(Addr) *user_entry,
>          ElfW(auxv_t) *auxv)
>  {
> +  const ElfW(Ehdr) *ehdr = NULL;
>    const ElfW(Phdr) *ph;
>    struct link_map *main_map;
>    size_t file_size;
> @@ -1518,6 +1520,9 @@ dl_main (const ElfW(Phdr) *phdr,
>           ElfW(Addr) mapstart;
>           ElfW(Addr) allocend;
>
> +         if (ph->p_offset == 0 && ph->p_memsz > 0)
> +           ehdr = (void *) ph->p_vaddr;
> +
>           /* Remember where the main program starts in memory.  */
>           mapstart = (main_map->l_addr
>                       + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)));
> @@ -1577,6 +1582,9 @@ dl_main (const ElfW(Phdr) *phdr,
>         break;
>        }
>
> +  if (ehdr != NULL)
> +    _dl_check_ehdr (ehdr);
> +
>    /* Adjust the address of the TLS initialization image in case
>       the executable is actually an ET_DYN object.  */
>    if (main_map->l_tls_initimage != NULL)
> diff --git a/elf/tst-elf-check.c b/elf/tst-elf-check.c
> new file mode 100644
> index 0000000000..175ba6fb5a
> --- /dev/null
> +++ b/elf/tst-elf-check.c
> @@ -0,0 +1,209 @@
> +/* Check ELF header error paths.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <elf.h>
> +#include <link.h>
> +#include <libc-abis.h>
> +#include <fcntl.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/capture_subprocess.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +#include <support/temp_file.h>
> +
> +static char *spargv[6];
> +static char *tmpbin;
> +
> +static void
> +do_prepare (int argc, char *argv[])
> +{
> +  int fdin = xopen (argv[0], O_RDONLY | O_LARGEFILE, 0);
> +  struct stat64 st;
> +  xfstat (fdin, &st);
> +  int fdout = create_temp_file ("tst-elf-check-", &tmpbin);
> +  xfchmod (fdout, S_IXUSR | S_IRUSR | S_IWUSR);
> +  TEST_VERIFY_EXIT (fdout >= 0);
> +  xcopy_file_range (fdin, NULL, fdout, NULL, st.st_size, 0);
> +  xclose (fdin);
> +  xclose (fdout);
> +}
> +
> +static void
> +run_test_expect_failure (void (*modify)(ElfW(Ehdr) *), const char *errmsg)
> +{
> +  int fd = xopen (tmpbin, O_RDWR | O_LARGEFILE, 0);
> +  ElfW(Ehdr) orig_hdr;
> +  if (read (fd, &orig_hdr, sizeof (orig_hdr)) != sizeof (orig_hdr))
> +    FAIL_EXIT1 ("read (%s): %m\n", tmpbin);
> +  ElfW(Ehdr) hdr = orig_hdr;
> +  modify (&hdr);
> +  if (lseek (fd, 0, SEEK_SET) != 0)
> +    FAIL_EXIT1 ("lseek: %m");
> +  xwrite (fd, &hdr, sizeof (hdr));
> +  xclose (fd);
> +
> +  struct support_capture_subprocess proc =
> +      support_capture_subprogram (spargv[0], spargv);
> +  support_capture_subprocess_check (&proc, "tst-elf-check", 127,
> +                                   sc_allow_stderr);
> +  TEST_VERIFY (strstr (proc.err.buffer, errmsg) != NULL);
> +  support_capture_subprocess_free (&proc);
> +
> +  /* Restore previous header.  */
> +  fd = xopen (tmpbin, O_RDWR | O_LARGEFILE, 0);
> +  xwrite (fd, &orig_hdr, sizeof (orig_hdr));
> +  xclose (fd);
> +}
> +
> +static void
> +modify_mag (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_MAG0] = EI_MAG3;
> +  ehdr->e_ident[EI_MAG1] = EI_MAG2;
> +  ehdr->e_ident[EI_MAG2] = EI_MAG1;
> +  ehdr->e_ident[EI_MAG3] = EI_MAG0;
> +}
> +
> +static void
> +modify_class (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_CLASS] = ELFCLASSNONE;
> +}
> +
> +static void
> +modify_endian (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_DATA] = ELFDATANUM;
> +}
> +
> +static void
> +modify_eiversion (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_VERSION] = EV_CURRENT + 1;
> +}
> +
> +static void
> +modify_osabi (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_OSABI] = ELFOSABI_STANDALONE;
> +}
> +
> +static void
> +modify_abiversion (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_ABIVERSION] = LIBC_ABI_MAX;
> +}
> +
> +static void
> +modify_pad (ElfW(Ehdr) *ehdr)
> +{
> +  memset (&ehdr->e_ident[EI_PAD], 0xff, EI_NIDENT - EI_PAD);
> +}
> +
> +static void
> +modify_version (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_version = EV_NONE;
> +}
> +
> +static void
> +modify_type (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_type = ET_NONE;
> +}
> +
> +static void
> +modify_phentsize (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_phentsize = sizeof (ElfW(Phdr)) + 1;
> +}
> +
> +static void
> +do_test_kernel (void)
> +{
> +  run_test_expect_failure (modify_mag,
> +                          "invalid ELF header");
> +  run_test_expect_failure (modify_type,
> +                          "only ET_DYN and ET_EXEC can be loaded");
> +  run_test_expect_failure (modify_phentsize,
> +                          "ELF file's phentsize not the expected size");
> +  run_test_expect_failure (modify_class,
> +                          "wrong ELF class");
> +}
> +
> +static void
> +do_test_common (void)
> +{
> +  run_test_expect_failure (modify_endian,
> +                          "ELF file data encoding not");
> +  run_test_expect_failure (modify_eiversion,
> +                          "ELF file version ident does not match current one");
> +  run_test_expect_failure (modify_pad,
> +                          "nonzero padding in e_ident");
> +  run_test_expect_failure (modify_osabi,
> +                          "ELF file OS ABI invalid");
> +  run_test_expect_failure (modify_abiversion,
> +                          "ELF file ABI version invalid");
> +  run_test_expect_failure (modify_version,
> +                          "ELF file version does not match current one");
> +}
> +
> +static int
> +do_test (int argc, char *argv[])
> +{
> +  /* We must have one or four parameters:
> +     + argv[0]:   the application name
> +     + argv[1]:   path for ld.so        optional
> +     + argv[2]:   "--library-path"      optional
> +     + argv[3]:   the library path      optional
> +     + argv[4/1]: the application name  */
> +
> +  bool hardpath = argc == 2;
> +
> +  int i;
> +  for (i = 0; i < argc - 2; i++)
> +    spargv[i] = argv[i+1];
> +  spargv[i++] = tmpbin;
> +  spargv[i++] = (char *) "--direct";
> +  spargv[i] = NULL;
> +
> +  /* Some fields are checked by the kernel results in a execve failure, so skip
> +     them for --enable-hardcoded-path-in-tests.  */
> +  if (!hardpath)
> +    do_test_kernel ();
> +  do_test_common ();
> +
> +  /* Also run the tests without issuing the loader.  */
> +  if (hardpath)
> +    return 0;
> +
> +  spargv[0] = tmpbin;
> +  spargv[1] = (char *) "--direct";
> +  spargv[2] = NULL;
> +
> +  do_test_common ();
> +
> +  return 0;
> +}
> +
> +#define PREPARE do_prepare
> +#define TEST_FUNCTION_ARGV do_test
> +#include <support/test-driver.c>
> diff --git a/sysdeps/generic/dl-elf-check.h b/sysdeps/generic/dl-elf-check.h
> new file mode 100644
> index 0000000000..48eb82e9e7
> --- /dev/null
> +++ b/sysdeps/generic/dl-elf-check.h
> @@ -0,0 +1,28 @@
> +/* ELF header consistency and ABI checks.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef _DL_ELF_CHECK_H
> +#define _DL_ELF_CHECK_H
> +
> +/* Called from the loader just after the program headers are processed.  */
> +static inline void
> +_dl_check_ehdr (const ElfW(Ehdr) *ehdr)
> +{
> +}
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/dl-elf-check.h b/sysdeps/unix/sysv/linux/dl-elf-check.h
> new file mode 100644
> index 0000000000..9e4925c090
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/dl-elf-check.h
> @@ -0,0 +1,32 @@
> +/* ELF header consistency and ABI checks.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef _DL_ELF_CHECK_H
> +#define _DL_ELF_CHECK_H
> +
> +#include <dl-check.h>
> +
> +static inline void
> +_dl_check_ehdr (const ElfW(Ehdr) *ehdr)
> +{
> +  int err = _dl_elfhdr_check (ehdr);
> +  if (err != DL_ELFHDR_OK)
> +    _dl_fatal_printf ("program loading error: %s\n", _dl_elfhdr_errstr (err));
> +}
> +
> +#endif
> --
> 2.32.0
>

LGTM.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>

Thanks.

-- 
H.J.

  parent reply	other threads:[~2021-12-06 19:04 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-19 15:03 Adhemerval Zanella
2021-11-19 15:33 ` H.J. Lu
2021-11-19 16:05   ` H.J. Lu
2021-11-19 17:06     ` Adhemerval Zanella
2021-12-06 19:03 ` H.J. Lu [this message]
2021-12-06 19:09 ` Florian Weimer
2021-12-06 19:22   ` H.J. Lu
2021-12-06 20:31     ` Adhemerval Zanella
2021-12-06 20:37       ` Florian Weimer
2021-12-06 21:07         ` Adhemerval Zanella
2021-12-07 15:45           ` Florian Weimer
2021-12-07 17:35             ` Adhemerval Zanella
2021-12-08  0:01               ` Fāng-ruì Sòng
2021-12-08 10:19                 ` Florian Weimer
2021-12-14  0:17                   ` Fāng-ruì Sòng
2021-12-14  9:03                     ` Florian Weimer
2021-12-14  9:09                       ` Fāng-ruì Sòng
2021-12-14  9:18                         ` Florian Weimer
2021-12-14 19:03                           ` Fangrui Song
2021-12-14 12:28                         ` Adhemerval Zanella
2021-12-14 12:23                       ` Adhemerval Zanella
2021-12-14 19:24                         ` Fangrui Song
2021-12-14 21:07                           ` Adhemerval Zanella
2021-12-14 21:30                             ` Fangrui Song
2021-12-14 21:53                               ` Florian Weimer
2021-12-14 23:08                                 ` Fangrui Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAMe9rOprcRWXG4Eab0FWfN3WNTU2R2AYdh=asMBgsNg-exqZGg@mail.gmail.com' \
    --to=hjl.tools@gmail.com \
    --cc=adhemerval.zanella@linaro.org \
    --cc=fweimer@redhat.com \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).