diff mbox series

elf: Add elf checks for main executable

Message ID 20211119150329.2200675-1-adhemerval.zanella@linaro.org
State New
Delegated to: H.J. Lu
Headers show
Series elf: Add elf checks for main executable | expand

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent
dj/TryBot-32bit success Build for i686

Commit Message

Adhemerval Zanella Nov. 19, 2021, 3:03 p.m. UTC
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

Comments

H.J. Lu Nov. 19, 2021, 3:33 p.m. UTC | #1
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

Is it possible to check __ehdr_start?

> 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
>
H.J. Lu Nov. 19, 2021, 4:05 p.m. UTC | #2
On Fri, Nov 19, 2021 at 7:33 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> 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
>
> Is it possible to check __ehdr_start?

Probably not.   I added this to binutils master branch:

$ elfedit
...
 --output-abiversion [0-255]
                              Set output ABIVERSION

Please use it for both executable and shared library tests.

> > 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
> >
>
>
> --
> H.J.
Adhemerval Zanella Nov. 19, 2021, 5:06 p.m. UTC | #3
On 19/11/2021 13:05, H.J. Lu wrote:
> On Fri, Nov 19, 2021 at 7:33 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>>
>> 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
>>
>> Is it possible to check __ehdr_start?
> 
> Probably not.   I added this to binutils master branch:

I forgot about __ehdr_start, but at rtld.c context the __ehdr_start is the
loader one (not really what we want).  We might try to get the symbol from
the main application (with the extra complication it is a hidden one), but
since we already parse the programs headers it seems simpler to get from
PT_LOAD.

> 
> $ elfedit
> ...
>  --output-abiversion [0-255]
>                               Set output ABIVERSION
> 
> Please use it for both executable and shared library tests.

Is this really an improvement? It will need to either copy it to testroot
along with all its depedencies and/or make multiples copies, elfedit them,
and copy them on testroot.  It seems that edit the ELF directly seem
simpler, since it is only the header that require adjustments.

> 
>>> 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
>>>
>>
>>
>> --
>> H.J.
> 
> 
>
H.J. Lu Dec. 6, 2021, 7:03 p.m. UTC | #4
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.
Florian Weimer Dec. 6, 2021, 7:09 p.m. UTC | #5
* Adhemerval Zanella:

> +static void
> +modify_abiversion (ElfW(Ehdr) *ehdr)
> +{
> +  ehdr->e_ident[EI_ABIVERSION] = LIBC_ABI_MAX;
> +}

So this is eventually controlled by the libc-abis file, right?

I *thought* that the consensus was that binutils should bump version if
absolute symbols are used.  But I don't see that in the absolute symbol
tests.

Is this really doing anything?

Thanks,
Florian
H.J. Lu Dec. 6, 2021, 7:22 p.m. UTC | #6
On Mon, Dec 6, 2021 at 11:09 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Adhemerval Zanella:
>
> > +static void
> > +modify_abiversion (ElfW(Ehdr) *ehdr)
> > +{
> > +  ehdr->e_ident[EI_ABIVERSION] = LIBC_ABI_MAX;
> > +}
>
> So this is eventually controlled by the libc-abis file, right?
>
> I *thought* that the consensus was that binutils should bump version if
> absolute symbols are used.  But I don't see that in the absolute symbol
> tests.
>
> Is this really doing anything?
>

EI_ABIVERSION check works on executables created by the new linker
which bumps EI_ABIVERSION.  This complements the existing
EI_ABIVERSION check on DSOs.  This is orthogonal to the ABI version
check for existing ld.so binaries which needs an ABIVERSION version.
Adhemerval Zanella Dec. 6, 2021, 8:31 p.m. UTC | #7
On 06/12/2021 16:22, H.J. Lu wrote:
> On Mon, Dec 6, 2021 at 11:09 AM Florian Weimer <fweimer@redhat.com> wrote:
>>
>> * Adhemerval Zanella:
>>
>>> +static void
>>> +modify_abiversion (ElfW(Ehdr) *ehdr)
>>> +{
>>> +  ehdr->e_ident[EI_ABIVERSION] = LIBC_ABI_MAX;
>>> +}
>>
>> So this is eventually controlled by the libc-abis file, right?
>>
>> I *thought* that the consensus was that binutils should bump version if
>> absolute symbols are used.  But I don't see that in the absolute symbol
>> tests.
>>
>> Is this really doing anything?
>>
> 
> EI_ABIVERSION check works on executables created by the new linker
> which bumps EI_ABIVERSION.  This complements the existing
> EI_ABIVERSION check on DSOs.  This is orthogonal to the ABI version
> check for existing ld.so binaries which needs an ABIVERSION version.
> 

Currently only mips does actually set and checks different EI_ABIVERSION
through VALID_ELF_ABIVERSION. For instance, -Wl,--hash-style=gnu with
mips64 will set EI_ABIVERSION to 5.
Florian Weimer Dec. 6, 2021, 8:37 p.m. UTC | #8
* Adhemerval Zanella:

> On 06/12/2021 16:22, H.J. Lu wrote:
>> On Mon, Dec 6, 2021 at 11:09 AM Florian Weimer <fweimer@redhat.com> wrote:
>>>
>>> * Adhemerval Zanella:
>>>
>>>> +static void
>>>> +modify_abiversion (ElfW(Ehdr) *ehdr)
>>>> +{
>>>> +  ehdr->e_ident[EI_ABIVERSION] = LIBC_ABI_MAX;
>>>> +}
>>>
>>> So this is eventually controlled by the libc-abis file, right?
>>>
>>> I *thought* that the consensus was that binutils should bump version if
>>> absolute symbols are used.  But I don't see that in the absolute symbol
>>> tests.
>>>
>>> Is this really doing anything?
>>>
>> 
>> EI_ABIVERSION check works on executables created by the new linker
>> which bumps EI_ABIVERSION.  This complements the existing
>> EI_ABIVERSION check on DSOs.  This is orthogonal to the ABI version
>> check for existing ld.so binaries which needs an ABIVERSION version.
>> 
>
> Currently only mips does actually set and checks different EI_ABIVERSION
> through VALID_ELF_ABIVERSION. For instance, -Wl,--hash-style=gnu with
> mips64 will set EI_ABIVERSION to 5.

What surprised me is that binutils does not seem to *generate* the bump
for ABSOLUTE.  Not so much the lack of checking in glibc.

Thanks,
Florian
Adhemerval Zanella Dec. 6, 2021, 9:07 p.m. UTC | #9
On 06/12/2021 17:37, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>> On 06/12/2021 16:22, H.J. Lu wrote:
>>> On Mon, Dec 6, 2021 at 11:09 AM Florian Weimer <fweimer@redhat.com> wrote:
>>>>
>>>> * Adhemerval Zanella:
>>>>
>>>>> +static void
>>>>> +modify_abiversion (ElfW(Ehdr) *ehdr)
>>>>> +{
>>>>> +  ehdr->e_ident[EI_ABIVERSION] = LIBC_ABI_MAX;
>>>>> +}
>>>>
>>>> So this is eventually controlled by the libc-abis file, right?
>>>>
>>>> I *thought* that the consensus was that binutils should bump version if
>>>> absolute symbols are used.  But I don't see that in the absolute symbol
>>>> tests.
>>>>
>>>> Is this really doing anything?
>>>>
>>>
>>> EI_ABIVERSION check works on executables created by the new linker
>>> which bumps EI_ABIVERSION.  This complements the existing
>>> EI_ABIVERSION check on DSOs.  This is orthogonal to the ABI version
>>> check for existing ld.so binaries which needs an ABIVERSION version.
>>>
>>
>> Currently only mips does actually set and checks different EI_ABIVERSION
>> through VALID_ELF_ABIVERSION. For instance, -Wl,--hash-style=gnu with
>> mips64 will set EI_ABIVERSION to 5.
> 
> What surprised me is that binutils does not seem to *generate* the bump
> for ABSOLUTE.  Not so much the lack of checking in glibc.

It does, but only for specific configurations.  bintutils does have a
testcase for it, pr21375*, but not all configurations does bump because
they do not require ABS relocations (for instance, for n64 -mmicromips
--defsym hidn=1 does set the ABI version to 4).
Florian Weimer Dec. 7, 2021, 3:45 p.m. UTC | #10
* Adhemerval Zanella:

> It does, but only for specific configurations.  bintutils does have a
> testcase for it, pr21375*, but not all configurations does bump because
> they do not require ABS relocations (for instance, for n64 -mmicromips
> --defsym hidn=1 does set the ABI version to 4).

Should this tell us something at DT_RELR?  That binutils doesn't help us
to prevent crashes for other features?

Thanks,
Florian
Adhemerval Zanella Dec. 7, 2021, 5:35 p.m. UTC | #11
On 07/12/2021 12:45, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>> It does, but only for specific configurations.  bintutils does have a
>> testcase for it, pr21375*, but not all configurations does bump because
>> they do not require ABS relocations (for instance, for n64 -mmicromips
>> --defsym hidn=1 does set the ABI version to 4).
> 
> Should this tell us something at DT_RELR?  That binutils doesn't help us
> to prevent crashes for other features?

I think it tell us that binutils support for DT_RELR or any other potential
abi disruptive feature will need a better ABI enforce for all Linux or
affected ABI.  It seems that binutils support are not really unified with
the multiples architectures and ABI. 

But I think it should be doable on linker side.
Fāng-ruì Sòng Dec. 8, 2021, 12:01 a.m. UTC | #12
On Tue, Dec 7, 2021 at 12:35 PM Adhemerval Zanella via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
>
>
> On 07/12/2021 12:45, Florian Weimer wrote:
> > * Adhemerval Zanella:
> >
> >> It does, but only for specific configurations.  bintutils does have a
> >> testcase for it, pr21375*, but not all configurations does bump because
> >> they do not require ABS relocations (for instance, for n64 -mmicromips
> >> --defsym hidn=1 does set the ABI version to 4).
> >
> > Should this tell us something at DT_RELR?  That binutils doesn't help us
> > to prevent crashes for other features?
>
> I think it tell us that binutils support for DT_RELR or any other potential
> abi disruptive feature will need a better ABI enforce for all Linux or
> affected ABI.  It seems that binutils support are not really unified with
> the multiples architectures and ABI.
>
> But I think it should be doable on linker side.

For DT_RELR, you may see
https://maskray.me/blog/2021-10-31-relative-relocations-and-relr#ei_abiversion
Many Linux executables (STB_GNU_UNIQUE/STT_GNU_IFUNC are not used) use
ELFOSABI_NONE and the linker does not and should not bump
EI_ABIVERSION.
diff mbox series

Patch

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