[PATCH] elf: Add elf checks for main executable
Adhemerval Zanella
adhemerval.zanella@linaro.org
Fri Nov 19 17:06:34 GMT 2021
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.
>
>
>
More information about the Libc-alpha
mailing list