public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] libdwfl: add dwfl_report_offline_memory
@ 2022-09-20 13:36 vvvvvv
  2022-10-16 15:19 ` Mark Wielaard
  0 siblings, 1 reply; 2+ messages in thread
From: vvvvvv @ 2022-09-20 13:36 UTC (permalink / raw)
  To: elfutils-devel; +Cc: kernel-team, maennich, vvvvvv

From: Aleksei Vetrov <vvvvvv@google.com>

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>
---
 ChangeLog                               |  4 +
 libdw/libdw.map                         |  1 +
 libdwfl/libdwfl.h                       |  4 +
 libdwfl/libdwflP.h                      |  6 ++
 libdwfl/offline.c                       | 26 +++++++
 libdwfl/open.c                          | 30 +++++++-
 tests/Makefile.am                       |  4 +
 tests/dwfl-report-offline-memory.c      | 97 +++++++++++++++++++++++++
 tests/run-dwfl-report-offline-memory.sh | 26 +++++++
 9 files changed, 194 insertions(+), 4 deletions(-)
 create mode 100644 tests/dwfl-report-offline-memory.c
 create mode 100755 tests/run-dwfl-report-offline-memory.sh

diff --git a/ChangeLog b/ChangeLog
index 5421f5b8..c3a1948a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2022-09-13  Aleksei Vetrov  <vvvvvv@google.com>
+
+	* NEWS (libdwfl): Add dwfl_report_offline_memory.
+
 2022-04-28  Di Chen  <dichen@redhat.com>
 
 	* NEWS: Add readefl -D, --use-dynamic.
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 8f393438..ff3b7d20 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -137,6 +137,7 @@ ELFUTILS_0.122 {
     dwfl_report_end;
     dwfl_report_module;
     dwfl_report_offline;
+    dwfl_report_offline_memory;
     dwfl_standard_argp;
     dwfl_standard_find_debuginfo;
     dwfl_version;
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index edb537c2..9114f7f0 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -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
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index a2949e74..011b5de9 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -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)
diff --git a/libdwfl/offline.c b/libdwfl/offline.c
index 58ba4c36..499663e3 100644
--- a/libdwfl/offline.c
+++ b/libdwfl/offline.c
@@ -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)
diff --git a/libdwfl/open.c b/libdwfl/open.c
index 77bd2bd9..da8b59a3 100644
--- a/libdwfl/open.c
+++ b/libdwfl/open.c
@@ -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);
 }
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 87988fb9..0cd98add 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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 \
@@ -148,6 +149,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 \
@@ -411,6 +413,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 \
@@ -709,6 +712,7 @@ test_elf_cntl_gelf_getshdr_LDADD = $(libelf)
 dwflsyms_LDADD = $(libdw) $(libelf) $(argp_LDADD)
 dwfllines_LDADD = $(libdw) $(libelf) $(argp_LDADD)
 dwfl_report_elf_align_LDADD = $(libdw)
+dwfl_report_offline_memory_LDADD = $(libdw)
 dwfl_report_segment_contiguous_LDADD = $(libdw) $(libebl) $(libelf)
 varlocs_LDADD = $(libdw) $(libelf) $(argp_LDADD)
 backtrace_LDADD = $(libdw) $(libelf) $(argp_LDADD)
diff --git a/tests/dwfl-report-offline-memory.c b/tests/dwfl-report-offline-memory.c
new file mode 100644
index 00000000..837aca5e
--- /dev/null
+++ b/tests/dwfl-report-offline-memory.c
@@ -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
index 00000000..644a45dc
--- /dev/null
+++ b/tests/run-dwfl-report-offline-memory.sh
@@ -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
-- 
2.37.3.968.ga6b4b080e4-goog


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-10-16 15:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-20 13:36 [PATCH] libdwfl: add dwfl_report_offline_memory vvvvvv
2022-10-16 15:19 ` Mark Wielaard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).