From patchwork Thu Feb 1 14:38:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Wielaard X-Patchwork-Id: 85136 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 85A9B38582AC for ; Thu, 1 Feb 2024 14:39:20 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from gnu.wildebeest.org (gnu.wildebeest.org [45.83.234.184]) by sourceware.org (Postfix) with ESMTPS id 5D2363858C62 for ; Thu, 1 Feb 2024 14:39:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5D2363858C62 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=klomp.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=klomp.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5D2363858C62 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=45.83.234.184 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706798349; cv=none; b=vdJzfJ7SqA7EUYnFGf7pGOJBNWNwuoYSSrjpcwtwPHMVHQWKYoBk61mciOV50L09HhtxUiRYkCmPnvJrqae5qcpq+Pz3Uz+DJc5GSPLqPs9yXmHsPsO8K00jqPBKVLqsVLkvWJ3RPzJuefnxvnLlJTZXr5bjZpLINWWQNHHWDb0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706798349; c=relaxed/simple; bh=hJl9F+VZJ+fuFHPqoXjpq0AkMxpXeFOJOytnnUEXMl4=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=YPCFFbaTkRn8IDbbm+Q8pONR6plZT+P2KoGqq+0YblkDiuvZK9R5I0UGr2IqmxH0baxtvaTB3PUupjQZF0bYzjzOmxDMLEeG95ja/PxwItAAA1tZrW1zOkVgTLIW4EMQj5drziJBfomht3PdIOKgBCRhhOJpMFhNYsC5N2HwQf4= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from r6.localdomain (82-217-174-174.cable.dynamic.v4.ziggo.nl [82.217.174.174]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by gnu.wildebeest.org (Postfix) with ESMTPSA id 7EC61302FDEA; Thu, 1 Feb 2024 15:39:04 +0100 (CET) Received: by r6.localdomain (Postfix, from userid 1000) id 070313404F1; Thu, 1 Feb 2024 15:39:01 +0100 (CET) From: Mark Wielaard To: elfutils-devel@sourceware.org Cc: Mark Wielaard , Derek Bruening Subject: [PATCH] libelf: Treat elf_memory as if using ELF_C_READ_MMAP Date: Thu, 1 Feb 2024 15:38:58 +0100 Message-ID: <20240201143858.930159-1-mark@klomp.org> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, RCVD_IN_BARRACUDACENTRAL, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE 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: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces+patchwork=sourceware.org@sourceware.org An Elf handle created through elf_memory was treated as if opened with ELF_C_READ. Which means libelf believed it had read the memory itself and could simply write to it if it wanted (because it wasn't mmaped directly on top of a file). This causes issues when that memory was actually read-only. Work around this by pretending the memory was actually read with ELF_C_READ_MMAP (so directly readable, but not writable). Add extra tests to elfgetzdata to check using elf_memory with read-only memory works as expected. * libelf/elf_memory.c (elf_memory): Call __libelf_read_mmaped_file with ELF_C_READ_MMAP. * tests/elfgetzdata.c (main): Add new "mem" option. * tests/run-elfgetzdata.sh: Also run all tests with new "mem" option. https://sourceware.org/bugzilla/show_bug.cgi?id=31225 Reported-by: Derek Bruening Signed-off-by: Mark Wielaard --- libelf/elf_memory.c | 2 +- tests/elfgetzdata.c | 70 +++++++++++++++++++++++++++--- tests/run-elfgetzdata.sh | 92 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 6 deletions(-) diff --git a/libelf/elf_memory.c b/libelf/elf_memory.c index a47f1d24..13d77cb7 100644 --- a/libelf/elf_memory.c +++ b/libelf/elf_memory.c @@ -46,5 +46,5 @@ elf_memory (char *image, size_t size) return NULL; } - return __libelf_read_mmaped_file (-1, image, 0, size, ELF_C_READ, NULL); + return __libelf_read_mmaped_file (-1, image, 0, size, ELF_C_READ_MMAP, NULL); } diff --git a/tests/elfgetzdata.c b/tests/elfgetzdata.c index 82afbe52..0af6c223 100644 --- a/tests/elfgetzdata.c +++ b/tests/elfgetzdata.c @@ -1,4 +1,5 @@ /* Copyright (C) 2015 Red Hat, Inc. + Copyright (C) 2024 Mark J. Wielaard This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -18,16 +19,19 @@ # include #endif +#include #include #include #include #include #include +#include #include #include #include #include #include +#include int @@ -38,21 +42,62 @@ main (int argc, char *argv[]) if (argc < 3 || (strcmp (argv[1], "read") != 0 - && strcmp (argv[1], "mmap") != 0)) + && strcmp (argv[1], "mmap") != 0 + && strcmp (argv[1], "mem") != 0)) { - printf ("Usage: (read|mmap) files...\n"); + printf ("Usage: (read|mmap|mem) files...\n"); return -1; } - bool mmap = strcmp (argv[1], "mmap") == 0; + bool do_read = strcmp (argv[1], "read") == 0; + bool do_mmap = strcmp (argv[1], "mmap") == 0; + bool do_mem = strcmp (argv[1], "mem") == 0; elf_version (EV_CURRENT); for (cnt = 2; cnt < argc; ++cnt) { int fd = open (argv[cnt], O_RDONLY); - - Elf *elf = elf_begin (fd, mmap ? ELF_C_READ_MMAP : ELF_C_READ, NULL); + void *map_address = NULL; + size_t map_size = 0; + + Elf *elf; + if (do_read) + elf = elf_begin (fd, ELF_C_READ, NULL); + else if (do_mmap) + elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + else + { + assert (do_mem); + // We mmap the memory ourselves, explicitly PROT_READ only + struct stat st; + if (fstat (fd, &st) != 0) + { + printf ("%s cannot stat %s\n", argv[cnt], strerror (errno)); + result = 1; + close (fd); + continue; + } + map_size = st.st_size; + map_address = mmap (NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map_address == MAP_FAILED) + { + printf ("%s cannot mmap %s\n", argv[cnt], strerror (errno)); + result = 1; + close (fd); + continue; + } + if (map_size < EI_NIDENT + || memcmp (map_address, ELFMAG, SELFMAG) != 0) + { + printf ("%s isn't an ELF file\n", argv[cnt]); + result = 1; + munmap (map_address, map_size); + close (fd); + continue; + } + elf = elf_memory (map_address, map_size); + } if (elf == NULL) { printf ("%s not usable %s\n", argv[cnt], elf_errmsg (-1)); @@ -107,6 +152,21 @@ main (int argc, char *argv[]) elf_end (elf); close (fd); + + if (do_mem) + { + /* Make sure we can still get at the memory. */ + if (memcmp (map_address, ELFMAG, SELFMAG) != 0) + { + printf ("%s isn't an ELF file anymore???\n", argv[cnt]); + result = 1; + } + if (munmap (map_address, map_size) != 0) + { + printf ("%s cannot munmap %s\n", argv[cnt], strerror (errno)); + result = 1; + } + } } return result; diff --git a/tests/run-elfgetzdata.sh b/tests/run-elfgetzdata.sh index e2df3081..7d3d1a5e 100755 --- a/tests/run-elfgetzdata.sh +++ b/tests/run-elfgetzdata.sh @@ -42,6 +42,17 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgnu64 <<\EO 8: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgnu64 <<\EOF +1: .text, NOT compressed +2: .zdebug_aranges, GNU compressed, size: 60 +3: .zdebug_info, GNU compressed, size: aa +4: .debug_abbrev, NOT compressed +5: .zdebug_line, GNU compressed, size: 8d +6: .shstrtab, NOT compressed +7: .symtab, NOT compressed +8: .strtab, NOT compressed +EOF + testfiles testfile-zgnu64be testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgnu64be <<\EOF 1: .text, NOT compressed @@ -67,6 +78,18 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgnu64be <<\ 9: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgnu64be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .zdebug_aranges, GNU compressed, size: 60 +4: .zdebug_info, GNU compressed, size: 7e +5: .debug_abbrev, NOT compressed +6: .zdebug_line, GNU compressed, size: 8d +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + testfiles testfile-zgabi64 testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgabi64 <<\EOF 1: .text, NOT compressed @@ -90,6 +113,17 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgabi64 <<\E 8: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgabi64 <<\EOF +1: .text, NOT compressed +2: .debug_aranges, ELF compressed, size: 60 +3: .debug_info, ELF compressed, size: aa +4: .debug_abbrev, NOT compressed +5: .debug_line, ELF compressed, size: 8d +6: .shstrtab, NOT compressed +7: .symtab, NOT compressed +8: .strtab, NOT compressed +EOF + testfiles testfile-zgabi64be testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgabi64be <<\EOF 1: .text, NOT compressed @@ -115,6 +149,18 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgabi64be << 9: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgabi64be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .debug_aranges, ELF compressed, size: 60 +4: .debug_info, ELF compressed, size: 7e +5: .debug_abbrev, NOT compressed +6: .debug_line, ELF compressed, size: 8d +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + testfiles testfile-zgnu32 testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgnu32 <<\EOF 1: .text, NOT compressed @@ -138,6 +184,17 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgnu32 <<\EO 8: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgnu32 <<\EOF +1: .text, NOT compressed +2: .zdebug_aranges, GNU compressed, size: 40 +3: .zdebug_info, GNU compressed, size: 9a +4: .debug_abbrev, NOT compressed +5: .zdebug_line, GNU compressed, size: 85 +6: .shstrtab, NOT compressed +7: .symtab, NOT compressed +8: .strtab, NOT compressed +EOF + testfiles testfile-zgnu32be testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgnu32be <<\EOF 1: .text, NOT compressed @@ -163,6 +220,18 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgnu32be <<\ 9: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgnu32be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .zdebug_aranges, GNU compressed, size: 40 +4: .zdebug_info, GNU compressed, size: 6e +5: .debug_abbrev, NOT compressed +6: .zdebug_line, GNU compressed, size: 85 +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + testfiles testfile-zgabi32 testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgabi32 <<\EOF 1: .text, NOT compressed @@ -186,6 +255,17 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgabi32 <<\E 8: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgabi32 <<\EOF +1: .text, NOT compressed +2: .debug_aranges, ELF compressed, size: 40 +3: .debug_info, ELF compressed, size: 9a +4: .debug_abbrev, NOT compressed +5: .debug_line, ELF compressed, size: 85 +6: .shstrtab, NOT compressed +7: .symtab, NOT compressed +8: .strtab, NOT compressed +EOF + testfiles testfile-zgabi32be testrun_compare ${abs_top_builddir}/tests/elfgetzdata read testfile-zgabi32be <<\EOF 1: .text, NOT compressed @@ -211,4 +291,16 @@ testrun_compare ${abs_top_builddir}/tests/elfgetzdata mmap testfile-zgabi32be << 9: .strtab, NOT compressed EOF +testrun_compare ${abs_top_builddir}/tests/elfgetzdata mem testfile-zgabi32be <<\EOF +1: .text, NOT compressed +2: .eh_frame, NOT compressed +3: .debug_aranges, ELF compressed, size: 40 +4: .debug_info, ELF compressed, size: 6e +5: .debug_abbrev, NOT compressed +6: .debug_line, ELF compressed, size: 85 +7: .shstrtab, NOT compressed +8: .symtab, NOT compressed +9: .strtab, NOT compressed +EOF + exit 0