From patchwork Mon Nov 9 18:40:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40973 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 CE2603896C10; Mon, 9 Nov 2020 18:40:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CE2603896C10 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947257; bh=kOIM/m6n9ypRw8kYlySjChIKINrkxddkMGM5GHqM0uQ=; h=To:Subject:In-Reply-To:References:Date:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=L01cRyQ09gRvlSP6f5sQDdrMoA+6DP24NlF/MjBk4gldNx0OTLbWU5ewrUX0cqqtQ Z0dVCvUKKQYFGiFKpN6DTWJbUXCWPf29AtuuwsXdPph28Nv+ncOwr1KEdgTpVotNUP tu6IhcLzktz9I3mgkSFaj1g1T1VfXntR0BQGFZpg= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 3B5FE388CC1C for ; Mon, 9 Nov 2020 18:40:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3B5FE388CC1C Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-360-cJ1DUL2pM-66zaEkWzWvmA-1; Mon, 09 Nov 2020 13:40:53 -0500 X-MC-Unique: cJ1DUL2pM-66zaEkWzWvmA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7E3131084D65 for ; Mon, 9 Nov 2020 18:40:52 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8EDB95C5B7 for ; Mon, 9 Nov 2020 18:40:51 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 05/11] elf: Add extension mechanism to ld.so.cache In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:40:49 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) 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: Florian Weimer via Libc-alpha From: Florian Weimer Reply-To: Florian Weimer Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" A previously unused new-format header field is used to record the address of an extension directory. This change adds a demo extension which records the version of ldconfig which builds a file. Reviewed-by: Adhemerval Zanella --- elf/cache.c | 89 +++++++++++++++++++++++++++ sysdeps/generic/dl-cache.h | 123 ++++++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 1 deletion(-) diff --git a/elf/cache.c b/elf/cache.c index ffecbe6d82..549e04ce21 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#include struct cache_entry { @@ -161,6 +163,21 @@ check_new_cache (struct cache_file_new *cache) error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n")); } +/* Print the extension information at the cache at start address + FILE_BASE, of length FILE_SIZE bytes. The new-format cache header + is at CACHE, and the file name for diagnostics is CACHE_NAME. */ +static void +print_extensions (struct cache_extension_all_loaded *ext) +{ + if (ext->sections[cache_extension_tag_generator].base != NULL) + { + fputs (_("Cache generated by: "), stdout); + fwrite (ext->sections[cache_extension_tag_generator].base, 1, + ext->sections[cache_extension_tag_generator].size, stdout); + putchar ('\n'); + } +} + /* Print the whole cache file, if a file contains the new cache format hidden in the old one, print the contents of the new format. */ void @@ -250,6 +267,11 @@ print_cache (const char *cache_name) } else if (format == 1) { + struct cache_extension_all_loaded ext; + if (!cache_extension_load (cache_new, cache, cache_size, &ext)) + error (EXIT_FAILURE, 0, + _("Malformed extension data in cache file %s\n"), cache_name); + printf (_("%d libs found in cache `%s'\n"), cache_new->nlibs, cache_name); @@ -260,6 +282,7 @@ print_cache (const char *cache_name) cache_new->libs[i].osversion, cache_new->libs[i].hwcap, cache_data + cache_new->libs[i].value); + print_extensions (&ext); } /* Cleanup. */ munmap (cache, cache_size); @@ -301,6 +324,45 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2) return res; } +/* Size of the cache extension directory. All tags are assumed to be + present. */ +enum + { + cache_extension_size = (offsetof (struct cache_extension, sections) + + (cache_extension_count + * sizeof (struct cache_extension_section))) + }; + +/* Write the cache extensions to FD. The extension directory is + assumed to be located at CACHE_EXTENSION_OFFSET. */ +static void +write_extensions (int fd, uint32_t cache_extension_offset) +{ + assert ((cache_extension_offset % 4) == 0); + + struct cache_extension *ext = xmalloc (cache_extension_size); + ext->magic = cache_extension_magic; + ext->count = cache_extension_count; + + for (int i = 0; i < cache_extension_count; ++i) + { + ext->sections[i].tag = i; + ext->sections[i].flags = 0; + } + + const char *generator + = "ldconfig " PKGVERSION RELEASE " release version " VERSION; + ext->sections[cache_extension_tag_generator].offset + = cache_extension_offset + cache_extension_size; + ext->sections[cache_extension_tag_generator].size = strlen (generator); + + if (write (fd, ext, cache_extension_size) != cache_extension_size + || write (fd, generator, strlen (generator)) != strlen (generator)) + error (EXIT_FAILURE, errno, _("Writing of cache extension data failed")); + + free (ext); +} + /* Save the contents of the cache. */ void save_cache (const char *cache_name) @@ -435,6 +497,25 @@ save_cache (const char *cache_name) && idx_old < cache_entry_old_count) file_entries->libs[idx_old] = file_entries->libs[idx_old - 1]; + /* Compute the location of the extension directory. This + implementation puts the directory after the string table. The + size computation matches the write calls below. The extension + directory does not exist with format 0, so the value does not + matter. */ + uint32_t extension_offset = 0; + if (opt_format != opt_format_new) + extension_offset += file_entries_size; + if (opt_format != opt_format_old) + { + if (opt_format != opt_format_new) + extension_offset += pad; + extension_offset += file_entries_new_size; + } + extension_offset += total_strlen; + extension_offset = roundup (extension_offset, 4); /* Provide alignment. */ + if (opt_format != opt_format_old) + file_entries_new->extension_offset = extension_offset; + /* Write out the cache. */ /* Write cache first to a temporary file and rename it later. */ @@ -473,6 +554,14 @@ save_cache (const char *cache_name) if (write (fd, strings, total_strlen) != (ssize_t) total_strlen) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); + if (opt_format != opt_format_old) + { + /* Align file position to 4. */ + off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET); + assert ((unsigned long long int) (extension_offset - old_offset) < 4); + write_extensions (fd, extension_offset); + } + /* Make sure user can always read cache file */ if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR)) error (EXIT_FAILURE, errno, diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h index 46026e0988..cdc24c8009 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -21,7 +21,9 @@ #include #include +#include #include +#include #ifndef _DL_CACHE_DEFAULT_ID # define _DL_CACHE_DEFAULT_ID 3 @@ -142,7 +144,11 @@ struct cache_file_new uint8_t padding_unsed[3]; /* Not used, for future extensions. */ - uint32_t unused[4]; /* Leave space for future extensions + /* File offset of the extension directory. See struct + cache_extension below. Must be a multiple of four. */ + uint32_t extension_offset; + + uint32_t unused[3]; /* Leave space for future extensions and align to 8 byte boundary. */ struct file_entry_new libs[0]; /* Entries describing libraries. */ /* After this the string table of size len_strings is found. */ @@ -162,6 +168,121 @@ cache_file_new_matches_endian (const struct cache_file_new *cache) } +/* Randomly chosen magic value, which allows for additional + consistency verification. */ +enum { cache_extension_magic = (uint32_t) -358342284 }; + +/* Tag values for different kinds of extension sections. Similar to + SHT_* constants. */ +enum cache_extension_tag + { + /* Array of bytes containing the glibc version that generated this + cache file. */ + cache_extension_tag_generator, + + /* Total number of known cache extension tags. */ + cache_extension_count + }; + +/* Element in the array following struct cache_extension. Similar to + an ELF section header. */ +struct cache_extension_section +{ + /* Type of the extension section. A enum cache_extension_tag value. */ + uint32_t tag; + + /* Extension-specific flags. Currently generated as zero. */ + uint32_t flags; + + /* Offset from the start of the file for the data in this extension + section. Specific extensions can have alignment constraints. */ + uint32_t offset; + + /* Length in bytes of the extension data. Specific extensions may + have size requirements. */ + uint32_t size; +}; + +/* The extension directory in the cache. An array of struct + cache_extension_section entries. */ +struct cache_extension +{ + uint32_t magic; /* Always cache_extension_magic. */ + uint32_t count; /* Number of following entries. */ + + /* count section descriptors of type struct cache_extension_section + follow. */ + struct cache_extension_section sections[]; +}; + +/* A relocated version of struct cache_extension_section. */ +struct cache_extension_loaded +{ + /* Address and size of this extension section. base is NULL if the + section is missing from the file. */ + const void *base; + size_t size; + + /* Flags from struct cache_extension_section. */ + uint32_t flags; +}; + +/* All supported extension sections, relocated. Filled in by + cache_extension_load below. */ +struct cache_extension_all_loaded +{ + struct cache_extension_loaded sections[cache_extension_count]; +}; + +static bool __attribute__ ((unused)) +cache_extension_load (const struct cache_file_new *cache, + const void *file_base, size_t file_size, + struct cache_extension_all_loaded *loaded) +{ + memset (loaded, 0, sizeof (*loaded)); + if (cache->extension_offset == 0) + /* No extensions present. This is not a format error. */ + return true; + if ((cache->extension_offset % 4) != 0) + /* Extension offset is misaligned. */ + return false; + size_t size_tmp; + if (__builtin_add_overflow (cache->extension_offset, + sizeof (struct cache_extension), &size_tmp) + || size_tmp > file_size) + /* Extension extends beyond the end of the file. */ + return false; + const struct cache_extension *ext = file_base + cache->extension_offset; + if (ext->magic != cache_extension_magic) + return false; + if (__builtin_mul_overflow (ext->count, + sizeof (struct cache_extension_section), + &size_tmp) + || __builtin_add_overflow (cache->extension_offset + + sizeof (struct cache_extension), size_tmp, + &size_tmp) + || size_tmp > file_size) + /* Extension array extends beyond the end of the file. */ + return false; + for (uint32_t i = 0; i < ext->count; ++i) + { + if (__builtin_add_overflow (ext->sections[i].offset, + ext->sections[i].size, &size_tmp) + || size_tmp > file_size) + /* Extension data extends beyond the end of the file. */ + return false; + + uint32_t tag = ext->sections[i].tag; + if (tag >= cache_extension_count) + /* Tag is out of range and unrecognized. */ + continue; + loaded->sections[tag].base = file_base + ext->sections[i].offset; + loaded->sections[tag].size = ext->sections[i].size; + loaded->sections[tag].flags = ext->sections[i].flags; + } + return true; +} + /* Used to align cache_file_new. */ #define ALIGN_CACHE(addr) \ (((addr) + __alignof__ (struct cache_file_new) -1) \