From patchwork Sat Mar 18 16:51:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 1758523 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.a=rsa-sha256 header.s=default header.b=RND67+pV; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Pf6WD6kWSz1yWp for ; Sun, 19 Mar 2023 03:52:56 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E3AD0384D1A1 for ; Sat, 18 Mar 2023 16:52:54 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E3AD0384D1A1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1679158374; bh=eL/i2Lop1kqU70siUdpcql0T8V3cfsz/8XKeIhnkt4E=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=RND67+pVIIPW+AaD7KX86u8S88rXmCTQ85JVXrSOV1T+SeRHwG3vG1Zs7XECDMWCl JGZbXCPdKvK7+88UCX8FJ8/UDi4uhQvYrKZpnyEixzVJ7uhNJpqfAzK0FkPopsZ5ff b5Pt5z0MaTf2INzem1LgGdfOpwv39Dud+KiiCn6Y= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward201b.mail.yandex.net (forward201b.mail.yandex.net [IPv6:2a02:6b8:c02:900:1:45:d181:d201]) by sourceware.org (Postfix) with ESMTPS id F194F385023F for ; Sat, 18 Mar 2023 16:51:50 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org F194F385023F Received: from myt6-1289f562e823.qloud-c.yandex.net (myt6-1289f562e823.qloud-c.yandex.net [IPv6:2a02:6b8:c12:259d:0:640:1289:f562]) by forward201b.mail.yandex.net (Yandex) with ESMTP id 6EC1E4230E for ; Sat, 18 Mar 2023 19:51:49 +0300 (MSK) Received: by myt6-1289f562e823.qloud-c.yandex.net (smtp/Yandex) with ESMTPSA id cpp3fcrbC8c1-C7YamqaJ; Sat, 18 Mar 2023 19:51:48 +0300 X-Yandex-Fwd: 1 To: libc-alpha@sourceware.org Cc: Stas Sergeev Subject: [PATCH 05/13] elf: split open_verify() into reusable parts Date: Sat, 18 Mar 2023 21:51:02 +0500 Message-Id: <20230318165110.3672749-6-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230318165110.3672749-1-stsp2@yandex.ru> References: <20230318165110.3672749-1-stsp2@yandex.ru> MIME-Version: 1.0 X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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: Stas Sergeev via Libc-alpha From: stsp Reply-To: Stas Sergeev Errors-To: libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org Sender: "Libc-alpha" This is almost a mechanical refactoring that splits open_verify() into 2 parts. The test-suite was run on x86_64/64 and showed no regressions. Signed-off-by: Stas Sergeev --- elf/dl-load.c | 337 ++++++++++++++++++++++++++------------------------ 1 file changed, 175 insertions(+), 162 deletions(-) diff --git a/elf/dl-load.c b/elf/dl-load.c index 9e0e63b9a3..353dc2aa13 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1598,19 +1598,11 @@ print_search_path (struct r_search_path_elem **list, else _dl_debug_printf_c ("\t\t(%s)\n", what); } - -/* Open a file and verify it is an ELF file for this architecture. We - ignore only ELF files for other architectures. Non-ELF files and - ELF files with different header information cause fatal errors since - this could mean there is something wrong in the installation and the - user might want to know about this. - If FD is not -1, then the file is already open and FD refers to it. - In that case, FD is consumed for both successful and error returns. */ static int -open_verify (const char *name, int fd, - struct filebuf *fbp, struct link_map *loader, - int whatcode, int mode, bool *found_other_class, bool free_name) +do_open_verify (const char *name, int fd, + struct filebuf *fbp, struct link_map *loader, + bool *found_other_class, bool free_name) { /* This is the expected ELF header. */ #define ELF32_CLASS ELFCLASS32 @@ -1637,7 +1629,170 @@ open_verify (const char *name, int fd, /* Initialize it to make the compiler happy. */ const char *errstring = NULL; int errval = 0; + ElfW(Ehdr) *ehdr; + ElfW(Phdr) *phdr; + size_t maplength; + + /* We successfully opened the file. Now verify it is a file + we can use. */ + __set_errno (0); + fbp->len = 0; + assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); + /* Read in the header. */ + do + { + ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len, + sizeof (fbp->buf) - fbp->len); + if (retlen <= 0) + break; + fbp->len += retlen; + } + while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr)))); + + /* This is where the ELF header is loaded. */ + ehdr = (ElfW(Ehdr) *) fbp->buf; + + /* Now run the tests. */ + if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)))) + { + errval = errno; + errstring = (errval == 0 + ? N_("file too short") : N_("cannot read file data")); + lose: + if (free_name) + { + char *realname = (char *) name; + name = strdupa (realname); + free (realname); + } + _dl_signal_error (errval, name, NULL, errstring); + return -1; + } + + /* 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 + ) + 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; + __set_errno (ENOENT); + return -1; + } + 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"); + + goto lose; + } + + if (__glibc_unlikely (ehdr->e_version != EV_CURRENT)) + { + errstring = N_("ELF file version does not match current one"); + goto lose; + } + if (! __glibc_likely (elf_machine_matches_host (ehdr))) + { + __set_errno (ENOENT); + return -1; + } + 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) + phdr = (void *) (fbp->buf + ehdr->e_phoff); + else + { + phdr = alloca (maplength); + if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength, + ehdr->e_phoff) != maplength) + { + errval = errno; + errstring = N_("cannot read file data"); + goto lose; + } + } + + if (__glibc_unlikely (elf_machine_reject_phdr_p + (phdr, ehdr->e_phnum, fbp->buf, fbp->len, + loader, -1))) + { + __set_errno (ENOENT); + return -1; + } + + return 0; +} + + +/* Open a file and verify it is an ELF file for this architecture. We + ignore only ELF files for other architectures. Non-ELF files and + ELF files with different header information cause fatal errors since + this could mean there is something wrong in the installation and the + user might want to know about this. + If FD is not -1, then the file is already open and FD refers to it. + In that case, FD is consumed for both successful and error returns. */ +static int +open_verify (const char *name, int fd, + struct filebuf *fbp, struct link_map *loader, + int whatcode, int mode, bool *found_other_class, bool free_name) +{ #ifdef SHARED /* Give the auditing libraries a chance. */ if (__glibc_unlikely (GLRO(dl_naudit) > 0)) @@ -1663,161 +1818,19 @@ open_verify (const char *name, int fd, if (fd != -1) { - ElfW(Ehdr) *ehdr; - ElfW(Phdr) *phdr; - size_t maplength; - - /* We successfully opened the file. Now verify it is a file - we can use. */ - __set_errno (0); - fbp->len = 0; - assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); - /* Read in the header. */ - do - { - ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len, - sizeof (fbp->buf) - fbp->len); - if (retlen <= 0) - break; - fbp->len += retlen; - } - while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr)))); - - /* This is where the ELF header is loaded. */ - ehdr = (ElfW(Ehdr) *) fbp->buf; - - /* Now run the tests. */ - if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)))) - { - errval = errno; - errstring = (errval == 0 - ? N_("file too short") : N_("cannot read file data")); - lose: - if (free_name) - { - char *realname = (char *) name; - name = strdupa (realname); - free (realname); - } - __close_nocancel (fd); - _dl_signal_error (errval, name, NULL, errstring); - } - - /* 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 - ) - 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; - __close_nocancel (fd); - __set_errno (ENOENT); - return -1; - } - 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"); - - goto lose; - } - - if (__glibc_unlikely (ehdr->e_version != EV_CURRENT)) - { - errstring = N_("ELF file version does not match current one"); - goto lose; - } - if (! __glibc_likely (elf_machine_matches_host (ehdr))) - { - __close_nocancel (fd); - __set_errno (ENOENT); - return -1; - } - 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) - phdr = (void *) (fbp->buf + ehdr->e_phoff); - else - { - phdr = alloca (maplength); - if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength, - ehdr->e_phoff) != maplength) - { - errval = errno; - errstring = N_("cannot read file data"); - goto lose; - } - } - - if (__glibc_unlikely (elf_machine_reject_phdr_p - (phdr, ehdr->e_phnum, fbp->buf, fbp->len, - loader, fd))) - { - __close_nocancel (fd); - __set_errno (ENOENT); - return -1; - } - + int err = do_open_verify (name, fd, fbp, loader, + found_other_class, + free_name); + if (err) + { + __close_nocancel (fd); + return -1; + } } return fd; } - + /* Try to open NAME in one of the directories in *DIRSP. Return the fd, or -1. If successful, fill in *REALNAME with the malloc'd full directory name. If it turns out