]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdwfl: add dwfl_report_offline_memory
authorAleksei Vetrov <vvvvvv@google.com>
Tue, 20 Sep 2022 13:36:37 +0000 (13:36 +0000)
committerMark Wielaard <mark@klomp.org>
Sun, 16 Oct 2022 15:09:42 +0000 (17:09 +0200)
This method allows to read and report ELF from memory instead of opening
a file. That way arbitrary memory can be worked with, e.g. when coming
from a stream without the need to persist.

Another useful application is for fuzzing, because fuzzers might be able
to track accesses to the memory and change the fuzzer input to cover
more edge cases through more targeted input. Hence, add a new function
along with a test case.

Signed-off-by: Aleksei Vetrov <vvvvvv@google.com>
13 files changed:
ChangeLog
NEWS
libdw/ChangeLog
libdw/libdw.map
libdwfl/ChangeLog
libdwfl/libdwfl.h
libdwfl/libdwflP.h
libdwfl/offline.c
libdwfl/open.c
tests/ChangeLog
tests/Makefile.am
tests/dwfl-report-offline-memory.c [new file with mode: 0644]
tests/run-dwfl-report-offline-memory.sh [new file with mode: 0755]

index 1f449d60a3a45b7f4893d2720a006b4c1b2d07ac..6062418340ffb201757fc71cd33295c53e4a3a0e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2022-09-13  Aleksei Vetrov  <vvvvvv@google.com>
+
+       * NEWS (libdwfl): Add dwfl_report_offline_memory.
+
 2022-09-27  Taketo Kabe  <kabe@sra-tohoku.co.jp>
 
        * debuginfod/debuginfod-client.c: Correctly get timestamp when
diff --git a/NEWS b/NEWS
index 156f78df9099e87b47254c3ec417e237d5c48227..6ebd172c5caec6592c04d0bc5d73b250b9e2c56d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,7 @@ debuginfod: Add --disable-source-scan option.
 
 libdwfl: Add new function dwfl_get_debuginfod_client.
          Add new function dwfl_frame_reg.
+         Add new function dwfl_report_offline_memory.
 
 Version 0.187
 
index dd54afc2b4cd2268dbc4cbe8adba9c768834ee46..9a798ff986b9b3832e98ab7a7bd7538ce189ef25 100644 (file)
@@ -1,3 +1,7 @@
+2022-09-13  Aleksei Vetrov  <vvvvvv@google.com>
+
+       * libdw.map (ELFUTILS_0.188): Add dwfl_report_offline_memory.
+
 2022-08-09  Ulrich Drepper  <drepper@redhat.com>
 
        * dwarf_next_cfi.c (dwarf_next_cfi): Don't skip processing the
index 8f39343885483b344a6b038481d0970d207071fd..5331ad45149813a4278d6ee089df81660c31f3fd 100644 (file)
@@ -371,4 +371,5 @@ ELFUTILS_0.188 {
   global:
     dwfl_get_debuginfod_client;
     dwfl_frame_reg;
+    dwfl_report_offline_memory;
 } ELFUTILS_0.186;
index 30f30b14bfbf01b49e99aa2eda4501e5faa41d7d..942f05d53d7f4eb13ad59be16852df3143e259d1 100644 (file)
@@ -1,3 +1,16 @@
+2022-09-13  Aleksei Vetrov  <vvvvvv@google.com>
+
+       * libdwfl.h (dwfl_report_offline_memory): New function.
+       * libdwflP.h (__libdw_open_elf_memory): New internal function.
+       (dwfl_report_offline_memory): INTDECL.
+       * offline.c (dwfl_report_offline_memory): New function.
+       * open.c (decompress): Return DWFL_E_BADELF when fd is -1.
+       (libdw_open_elf): New argument use_elfp. Adding *elfp to elf if
+       true.
+       (__libdw_open_file): Pass false to libdw_open_elf.
+       (__libdw_open_elf_memory): New function.
+       (__libdw_open_elf): Pass false for libdw_open_elf.
+
 2022-07-28  Di Chen  <dichen@redhat.com>
 
        * libdwfl.h (dwfl_frame_reg): New function.
index edb537c21e6cb70637fd53c27a56c484982801b2..9114f7f05d81ad7b3d8288c00d296ca0b620f6e9 100644 (file)
@@ -159,6 +159,10 @@ extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name,
 extern Dwfl_Module *dwfl_report_offline (Dwfl *dwfl, const char *name,
                                         const char *file_name, int fd);
 
+/* Similar, but report ELF from memory region.  */
+extern Dwfl_Module *dwfl_report_offline_memory (Dwfl *dwfl, const char *name,
+                                               const char *file_name,
+                                               char *data, size_t size);
 
 /* Finish reporting the current set of modules to the library.
    If REMOVED is not null, it's called for each module that
index a2949e74c871bb3b6007a2365b51510d04ca6c0b..011b5de9bdb93db600e20fd6e1847e268c719c9e 100644 (file)
@@ -631,6 +631,11 @@ extern Dwfl_Error __libdw_open_file (int *fdp, Elf **elfp,
                                     bool close_on_fail, bool archive_ok)
   internal_function;
 
+/* Same as __libdw_open_file, but opens Elf handle from memory region.  */
+extern Dwfl_Error __libdw_open_elf_memory (char *data, size_t size, Elf **elfp,
+                                          bool archive_ok)
+  internal_function;
+
 /* Same as __libdw_open_file, but never closes the given file
    descriptor and ELF_K_AR is always an acceptable type.  */
 extern Dwfl_Error __libdw_open_elf (int fd, Elf **elfp) internal_function;
@@ -760,6 +765,7 @@ INTDECL (dwfl_report_begin_add)
 INTDECL (dwfl_report_module)
 INTDECL (dwfl_report_segment)
 INTDECL (dwfl_report_offline)
+INTDECL (dwfl_report_offline_memory)
 INTDECL (dwfl_report_end)
 INTDECL (dwfl_build_id_find_elf)
 INTDECL (dwfl_build_id_find_debuginfo)
index 58ba4c3699b93c2faccf670fe4cfb82222eabe53..499663e39e3f037394a8d69be069bab2f3ae9776 100644 (file)
@@ -1,6 +1,7 @@
 /* Recover relocatibility for addresses computed from debug information.
    Copyright (C) 2005-2009, 2012 Red Hat, Inc.
    Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org>
+   Copyright (C) 2022 Google LLC
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -252,6 +253,7 @@ process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
 
 {
   Dwfl_Module *mod = NULL;
+  /* elf_begin supports opening archives even with fd == -1 passed.  */
   Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
   if (unlikely (member == NULL)) /* Empty archive.  */
     {
@@ -320,3 +322,27 @@ dwfl_report_offline (Dwfl *dwfl, const char *name,
   return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL);
 }
 INTDEF (dwfl_report_offline)
+
+Dwfl_Module *
+dwfl_report_offline_memory (Dwfl *dwfl, const char *name,
+                           const char *file_name, char *data, size_t size)
+{
+  if (dwfl == NULL)
+    return NULL;
+
+  Elf *elf;
+  Dwfl_Error error = __libdw_open_elf_memory (data, size, &elf, true);
+  if (error != DWFL_E_NOERROR)
+    {
+      __libdwfl_seterrno (error);
+      return NULL;
+    }
+  /* It is ok to pass fd == -1 here, because libelf uses it as a value for
+     "no file opened" and supports working with files without fd, thanks to
+     the existence of the elf_memory function.  */
+  Dwfl_Module *mod = process_file (dwfl, name, file_name, -1, elf, NULL);
+  if (mod == NULL)
+    elf_end (elf);
+  return mod;
+}
+INTDEF (dwfl_report_offline_memory)
index 77bd2bd9f473d988b316fc0e0af69af9578746bd..da8b59a32a84307a1c32012903c31b2e5074b321 100644 (file)
@@ -1,5 +1,6 @@
 /* Decompression support for libdwfl: zlib (gzip), bzlib (bzip2) or lzma (xz).
    Copyright (C) 2009, 2016 Red Hat, Inc.
+   Copyright (C) 2022 Google LLC
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -53,6 +54,9 @@ static Dwfl_Error
 decompress (int fd __attribute__ ((unused)), Elf **elf)
 {
   Dwfl_Error error = DWFL_E_BADELF;
+  /* ELF cannot be decompressed, if there is no file descriptor.  */
+  if (fd == -1)
+    return error;
   void *buffer = NULL;
   size_t size = 0;
 
@@ -124,11 +128,12 @@ what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *may_close_fd)
 
 static Dwfl_Error
 libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok,
-               bool never_close_fd, bool bad_elf_ok)
+               bool never_close_fd, bool bad_elf_ok, bool use_elfp)
 {
   bool may_close_fd = false;
 
-  Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
+  Elf *elf =
+      use_elfp ? *elfp : elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
 
   Elf_Kind kind;
   Dwfl_Error error = what_kind (*fdp, &elf, &kind, &may_close_fd);
@@ -194,11 +199,28 @@ libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok,
 Dwfl_Error internal_function
 __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
 {
-  return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false);
+  return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false,
+                        false);
+}
+
+Dwfl_Error internal_function
+__libdw_open_elf_memory (char *data, size_t size, Elf **elfp, bool archive_ok)
+{
+  /* It is ok to use `fd == -1` here, because libelf uses it as a value for
+     "no file opened" and code supports working with this value, and also
+     `never_close_fd == false` is passed to prevent closing non-existant file.
+     The only caveat is in `decompress` method, which doesn't support
+     decompressing from memory, so reading compressed zImage using this method
+     won't work.  */
+  int fd = -1;
+  *elfp = elf_memory (data, size);
+  /* Allow using this ELF as reference for subsequent elf_begin calls.  */
+  (*elfp)->cmd = ELF_C_READ_MMAP_PRIVATE;
+  return libdw_open_elf (&fd, elfp, false, archive_ok, true, false, true);
 }
 
 Dwfl_Error internal_function
 __libdw_open_elf (int fd, Elf **elfp)
 {
-  return libdw_open_elf (&fd, elfp, false, true, true, true);
+  return libdw_open_elf (&fd, elfp, false, true, true, true, false);
 }
index d10acfa0a7c661587ebd11c40d3dfb252953ad82..6ac2c1e8a27bb0e8f0901c89b3dfc36b8389b2b4 100644 (file)
@@ -1,3 +1,12 @@
+2022-09-13  Aleksei Vetrov  <vvvvvv@google.com>
+
+       * Makefile.am (check_PROGRAMS): Add dwfl-report-offline-memory.
+       (TESTS): Add run-dwfl-report-offline-memory.sh.
+       (EXTRA_DIST): Likewise.
+       (dwfl_report_offline_memory_LDADD): New variable.
+       * dwfl-report-offline-memory.c: New file.
+       * run-dwfl-report-offline-memory.sh: Likewise.
+
 2022-09-13  Khem Raj <raj.khem@gmail.com>
 
        * Makefile.am (*_LDADD): Add libeu if needed for error.
index 142b9c30404b2eba5ad5d222439939a466bccce3..f680d3e14a750279e852375dcdfb78d4e52430cc 100644 (file)
@@ -47,6 +47,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
                  alldts typeiter typeiter2 low_high_pc \
                  test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
                  dwfl-report-elf-align dwfl-report-segment-contiguous \
+                 dwfl-report-offline-memory \
                  varlocs backtrace backtrace-child \
                  backtrace-data backtrace-dwarf debuglink debugaltlink \
                  buildid deleted deleted-lib.so aggregate_size peel_type \
@@ -149,6 +150,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
        run-readelf-mixed-corenote.sh run-dwfllines.sh \
        run-readelf-variant.sh run-readelf-fat-lto.sh \
        run-dwfl-report-elf-align.sh run-addr2line-test.sh \
+       run-dwfl-report-offline-memory.sh \
        run-addr2line-i-test.sh run-addr2line-i-lex-test.sh \
        run-addr2line-i-demangle-test.sh run-addr2line-alt-debugpath.sh \
        run-varlocs.sh run-exprlocs.sh run-varlocs-vars.sh run-funcretval.sh \
@@ -413,6 +415,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             testfile69.core.bz2 testfile69.so.bz2 \
             testfile70.core.bz2 testfile70.exec.bz2 testfile71.bz2 \
             run-dwfllines.sh run-dwfl-report-elf-align.sh \
+            run-dwfl-report-offline-memory.sh \
             testfile-dwfl-report-elf-align-shlib.so.bz2 \
             testfilenolines.bz2 test-core-lib.so.bz2 test-core.core.bz2 \
             test-core.exec.bz2 run-addr2line-test.sh \
@@ -711,6 +714,7 @@ test_elf_cntl_gelf_getshdr_LDADD = $(libelf)
 dwflsyms_LDADD = $(libdw) $(libelf) $(argp_LDADD)
 dwfllines_LDADD = $(libeu) $(libdw) $(libelf) $(argp_LDADD)
 dwfl_report_elf_align_LDADD = $(libeu) $(libdw)
+dwfl_report_offline_memory_LDADD = $(libeu) $(libdw)
 dwfl_report_segment_contiguous_LDADD = $(libdw) $(libebl) $(libelf)
 varlocs_LDADD = $(libeu) $(libdw) $(libelf) $(argp_LDADD)
 backtrace_LDADD = $(libeu) $(libdw) $(libelf) $(argp_LDADD)
diff --git a/tests/dwfl-report-offline-memory.c b/tests/dwfl-report-offline-memory.c
new file mode 100644 (file)
index 0000000..837aca5
--- /dev/null
@@ -0,0 +1,97 @@
+/* Test program for dwfl_report_offline_memory.
+   Copyright (C) 2022 Google LLC
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   elfutils 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <config.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include ELFUTILS_HEADER(dwfl)
+#include "system.h"
+
+
+static const Dwfl_Callbacks offline_callbacks =
+  {
+    .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
+    .section_address = INTUSE(dwfl_offline_section_address),
+  };
+
+static int
+count_modules (Dwfl_Module *mod __attribute__ ((unused)),
+              void **userdata __attribute__ ((unused)),
+              const char *name __attribute__ ((unused)),
+              Dwarf_Addr base __attribute__ ((unused)), void *arg)
+{
+  unsigned long long *counter = arg;
+  ++(*counter);
+  return DWARF_CB_OK;
+}
+
+int
+main (int argc, char **argv)
+{
+  /* We use no threads here which can interfere with handling a stream.  */
+  (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  if (argc != 3)
+    error (-1, 0,
+          "usage: dwfl_report_offline_memory [filename] "
+          "[expected number of modules]");
+
+  const char *fname = argv[1];
+  int fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    error (-1, 0, "can't open file %s: %s", fname, strerror (errno));
+  size_t size = lseek (fd, 0, SEEK_END);
+  lseek (fd, 0, SEEK_SET);
+  char *data = malloc (size);
+  size_t bytes_read = read (fd, data, size);
+  assert (bytes_read == size);
+  close (fd);
+
+  Dwfl *dwfl = dwfl_begin (&offline_callbacks);
+  assert (dwfl != NULL);
+
+  Dwfl_Module *mod =
+      dwfl_report_offline_memory (dwfl, argv[1], argv[1], data, size);
+  assert (mod != NULL);
+  dwfl_report_end (dwfl, NULL, NULL);
+
+  unsigned long long number_of_modules = 0;
+  ptrdiff_t offset =
+      dwfl_getmodules (dwfl, &count_modules, &number_of_modules, 0);
+  if (offset < 0)
+    error (1, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1));
+  assert (offset == 0);
+
+  char *endptr;
+  unsigned long long expected_number_of_modules =
+      strtoull (argv[2], &endptr, 0);
+  assert (endptr && !*endptr);
+  assert (number_of_modules == expected_number_of_modules);
+
+  dwfl_end (dwfl);
+  free (data);
+
+  return 0;
+}
diff --git a/tests/run-dwfl-report-offline-memory.sh b/tests/run-dwfl-report-offline-memory.sh
new file mode 100755 (executable)
index 0000000..644a45d
--- /dev/null
@@ -0,0 +1,26 @@
+#! /bin/sh
+# Copyright (C) 2022 Google LLC
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile-dwfl-report-elf-align-shlib.so
+testfiles testarchive64.a
+
+testrun ${abs_builddir}/dwfl-report-offline-memory ./testfile-dwfl-report-elf-align-shlib.so 1
+testrun ${abs_builddir}/dwfl-report-offline-memory ./testarchive64.a 3
+
+exit 0