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 <bruen...@google.com> Signed-off-by: Mark Wielaard <m...@klomp.org> --- 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 <m...@klomp.org> This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -18,16 +19,19 @@ # include <config.h> #endif +#include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <inttypes.h> #include <libelf.h> +#include <errno.h> #include <gelf.h> #include <stdbool.h> #include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/mman.h> 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 -- 2.43.0