From patchwork Fri Nov 19 15:03:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 47939 X-Patchwork-Delegate: hjl.tools@gmail.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D7BE8385BF83 for ; Fri, 19 Nov 2021 15:04:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D7BE8385BF83 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1637334243; bh=hKlWD+CHK0p5SzwQQEP3foGo8RzroxRe9tFjZWrKsSs=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=HFg4b15X3UCERsyAb69+Lvmqd78m0nVlDichN/8pom9txlJ5XtqBVMyG15iiCK2tV 4w7FaCccHevDx1meMl+bbboGEQvZAuAbM3i7C9P3nRbukHcpjfRDc81+RHXD2KGCJc MDA1ZDpTAp3iqdp9AOKM8qQdX4P4vMm0KuAPLWMo= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-vk1-xa2c.google.com (mail-vk1-xa2c.google.com [IPv6:2607:f8b0:4864:20::a2c]) by sourceware.org (Postfix) with ESMTPS id 02049385800C for ; Fri, 19 Nov 2021 15:03:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 02049385800C Received: by mail-vk1-xa2c.google.com with SMTP id b192so6071549vkf.3 for ; Fri, 19 Nov 2021 07:03:34 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=hKlWD+CHK0p5SzwQQEP3foGo8RzroxRe9tFjZWrKsSs=; b=G6G0tQVgAT2lS+nH+3X3seQk2+YUbxwaB4nXo7xqAtiGVbR123NjOk6oJvvWA1KoLB lvampIj6GfJoSRoj1dlM98Bv6O3y7sbrxYEMZ2QJVYedATP4sqTvFpjEOMx3KQwS3dNg BAXZAeUX+mJbCgK6rD8M+T6bwTjPOwzql0xDfM697yxkKcyQxaBq5Bz2E/GHx3itf6pY SiZZbNWp/YRugQdqEubmdDQgman57XuRgyUKeks/cTlvU0l8QBl68xNPabB747+ZN3n3 aS9m91BIfDmmmhCDgRBfm2uVa6n4Fc93Gn8xxdFeT64gxxpYIYZfZ6d7blteUAx384z9 DstA== X-Gm-Message-State: AOAM532reVbu6yIDxwMeNmJ67gbIdc+XjxLo3yu7wImgOplWskNRfkrV 3MHyV65TczwwPERRfrcvSKaMbRquKDrCoA== X-Google-Smtp-Source: ABdhPJxcrlp/G1fv6wxHJN3Amyq9g1cID94xu5VK0DVBn/+Ue4/PNNgO8gv0Bk9TyyOcZqMttipnTw== X-Received: by 2002:a05:6122:997:: with SMTP id g23mr119648487vkd.15.1637334213716; Fri, 19 Nov 2021 07:03:33 -0800 (PST) Received: from birita.. ([2804:431:c7ca:66dc:bc2c:d33d:22b3:25d7]) by smtp.gmail.com with ESMTPSA id m15sm192vkl.40.2021.11.19.07.03.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Nov 2021 07:03:33 -0800 (PST) To: libc-alpha@sourceware.org, "H . J . Lu" , Florian Weimer Subject: [PATCH] elf: Add elf checks for main executable Date: Fri, 19 Nov 2021 12:03:29 -0300 Message-Id: <20211119150329.2200675-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Netto Reply-To: Adhemerval Zanella Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" 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. Reviewed-by: H.J. Lu --- 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 + . */ + +#include +#include +#include +#include +#include + +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 + . */ + +#ifndef _DL_OPENCHECK_H +#define _DL_OPENCHECK_H + +#include + +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 #include #include +#include #include -#include -#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 #include #include +#include #include @@ -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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 + . */ + +#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 + . */ + +#ifndef _DL_ELF_CHECK_H +#define _DL_ELF_CHECK_H + +#include + +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