Hi Mark,

jankratochvil/deleted

with the new /proc/PID/map_files/ feature it is possible to fully access even
deleted executable + shared_libraries.  Current Linux kernels (3.11.10) permit
on root user as the tracer but (1) hopefully it gets fixed in the future and
(2) at least for the use in ABRT it is OK as backtracer is run from core
dumping hook.

Linux kernels have always supported also /proc/PID/exe which works for deleted
main executable.  It even works for non-root users.  But it works only for the
executable, not for shared libraries.  I find more useful to push for Linux
kernel /proc/PID/map_files/ permissions fix than to implement /proc/PID/exe.

If the running binary has been upgraded while it is still running its separate
debug info files in /usr/lib/debug/ will no longer match.  But still
.eh_frame-based unwind is possible using /proc/PID/map_files/ that time.

Testcase is skipped when /proc/PID/map_files/ gives permission error.

OK for check-in?

I am not completely sure setting just mod->main.name is right but it looks so.


Thanks,
Jan
--- Begin Message ---
libdwfl/
2013-12-18  Jan Kratochvil  <[email protected]>

        Support live PIDs with deleted files.
        * linux-proc-maps.c (PROCMAPFILESFMT): New #defined.
        (proc_maps_report): New comment for low and high.  New variable
        high_start, set it everywhere.
        (proc_maps_report) (map_files_name): New inlined function.
        (proc_maps_report) (report): Call it and set mod->main.NAME.

tests/
2013-12-18  Jan Kratochvil  <[email protected]>

        Support live PIDs with deleted files.
        * Makefile.am (check_PROGRAMS): Add deleted and deleted-lib.so.
        (TESTS, EXTRA_DIST): Add run-deleted.sh.
        (deleted_LDADD, deleted_lib_so_LDFLAGS, deleted_lib_so_CFLAGS): New.
        * deleted-lib.c: New file.
        * deleted.c: New file.
        * run-deleted.sh: New file.

Signed-off-by: Jan Kratochvil <[email protected]>
---
 libdwfl/linux-proc-maps.c | 30 +++++++++++++++++++++++++---
 tests/Makefile.am         | 10 +++++++---
 tests/deleted-lib.c       | 27 +++++++++++++++++++++++++
 tests/deleted.c           | 50 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/run-deleted.sh      | 34 ++++++++++++++++++++++++++++++++
 5 files changed, 145 insertions(+), 6 deletions(-)
 create mode 100644 tests/deleted-lib.c
 create mode 100644 tests/deleted.c
 create mode 100755 tests/run-deleted.sh

diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index 8863cc8..b9604d0 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -43,6 +43,7 @@
 
 
 #define PROCMAPSFMT    "/proc/%d/maps"
+#define PROCMAPFILESFMT        "/proc/%d/map_files/%" PRIx64 "-%" PRIx64
 #define PROCMEMFMT     "/proc/%d/mem"
 #define PROCAUXVFMT    "/proc/%d/auxv"
 #define PROCEXEFMT     "/proc/%d/exe"
@@ -176,7 +177,26 @@ proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr 
sysinfo_ehdr, pid_t pid)
   unsigned int last_dmajor = -1, last_dminor = -1;
   uint64_t last_ino = -1;
   char *last_file = NULL;
-  Dwarf_Addr low = 0, high = 0;
+  // LOW-HIGH is cumulative range for all lines of the current filename.
+  // HIGH_START-HIGH is range of only the very last PROCMAPSFMT line.
+  Dwarf_Addr low = 0, high_start = 0, high = 0;
+
+  inline char *map_files_name (void)
+    {
+      static int map_files_failed = -1;
+      if (map_files_failed > 0)
+       return NULL;
+      char *map_fname;
+      if (asprintf (&map_fname, PROCMAPFILESFMT, pid, high_start, high) < 0)
+       return NULL;
+      if (map_files_failed == 0)
+       return map_fname;
+      map_files_failed = access (map_fname, R_OK) != 0;
+      if (! map_files_failed)
+       return map_fname;
+      free (map_fname);
+      return NULL;
+    }
 
   inline bool report (void)
     {
@@ -188,6 +208,9 @@ proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr 
sysinfo_ehdr, pid_t pid)
          last_file = NULL;
          if (unlikely (mod == NULL))
            return true;
+         char *map_fname = map_files_name ();
+         if (map_fname != NULL)
+           mod->main.name = map_fname;
        }
       return false;
     }
@@ -224,7 +247,7 @@ proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr 
sysinfo_ehdr, pid_t pid)
              return -1;
            }
 
-         low = start;
+         low = high_start = start;
          high = end;
          if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
              || report ())
@@ -241,6 +264,7 @@ proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr 
sysinfo_ehdr, pid_t pid)
        {
          /* This is another portion of the same file's mapping.  */
          assert (!strcmp (last_file, file));
+         high_start = start;
          high = end;
        }
       else
@@ -248,7 +272,7 @@ proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr 
sysinfo_ehdr, pid_t pid)
          /* This is a different file mapping.  Report the last one.  */
          if (report ())
            goto bad_report;
-         low = start;
+         low = high_start = start;
          high = end;
          last_file = strdup (file);
          last_ino = ino;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b58e0f5..cb2f861 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -53,7 +53,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames 
sectiondump \
                  alldts md5-sha1-test typeiter typeiter2 low_high_pc \
                  test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
                  dwfl-report-elf-align varlocs backtrace backtrace-child \
-                 backtrace-data backtrace-dwarf
+                 backtrace-data backtrace-dwarf deleted deleted-lib.so
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
            asm-tst6 asm-tst7 asm-tst8 asm-tst9
 
@@ -107,7 +107,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile 
test-nlist \
        run-backtrace-native-biarch.sh run-backtrace-native-core.sh \
        run-backtrace-native-core-biarch.sh run-backtrace-core-x86_64.sh \
        run-backtrace-core-i386.sh run-backtrace-core-ppc.sh \
-       run-backtrace-core-s390x.sh run-backtrace-core-s390.sh
+       run-backtrace-core-s390x.sh run-backtrace-core-s390.sh run-deleted.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -251,7 +251,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             run-backtrace-core-ppc.sh \
             backtrace.s390x.core.bz2 backtrace.s390x.exec.bz2 \
             backtrace.s390.core.bz2 backtrace.s390.exec.bz2 \
-            run-backtrace-core-s390x.sh run-backtrace-core-s390.sh
+            run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
+            run-deleted.sh
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
@@ -388,6 +389,9 @@ backtrace_child_biarch_SOURCES = backtrace-child.c
 backtrace_data_LDADD = $(libdw) $(libelf) $(libmudflap)
 backtrace_dwarf_CFLAGS = -Wno-unused-parameter
 backtrace_dwarf_LDADD = $(libdw) $(libelf) $(libmudflap)
+deleted_LDADD = ./deleted-lib.so
+deleted_lib_so_LDFLAGS = -shared
+deleted_lib_so_CFLAGS = -fPIC
 
 if GCOV
 check: check-am coverage
diff --git a/tests/deleted-lib.c b/tests/deleted-lib.c
new file mode 100644
index 0000000..b4a12bf
--- /dev/null
+++ b/tests/deleted-lib.c
@@ -0,0 +1,27 @@
+/* Test program for opening already deleted running binaries.
+   Copyright (C) 2013 Red Hat, Inc.
+   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 <config.h>
+#include <unistd.h>
+
+void
+libfunc (void)
+{
+  sleep (60);
+  /* Avoid tail call optimization for the sleep call.  */
+  asm volatile ("");
+}
diff --git a/tests/deleted.c b/tests/deleted.c
new file mode 100644
index 0000000..feb6ac5
--- /dev/null
+++ b/tests/deleted.c
@@ -0,0 +1,50 @@
+/* Test program for opening already deleted running binaries.
+   Copyright (C) 2013 Red Hat, Inc.
+   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 <config.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <error.h>
+#include <errno.h>
+
+extern void libfunc (void);
+
+int
+main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
+{
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  pid_t pid = fork ();
+  assert (pid != -1);
+  if (pid == 0)
+    {
+      int err = close (0);
+      assert (!err);
+      err = close (1);
+      assert (!err);
+      err = close (2);
+      assert (!err);
+      libfunc ();
+      abort ();
+    }
+  printf ("%d\n", pid);
+  return EXIT_SUCCESS;
+}
diff --git a/tests/run-deleted.sh b/tests/run-deleted.sh
new file mode 100755
index 0000000..4f20404
--- /dev/null
+++ b/tests/run-deleted.sh
@@ -0,0 +1,34 @@
+#! /bin/bash
+# Copyright (C) 2013 Red Hat, Inc.
+# 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
+
+# Linux kernel 3.11.10 permits map_files/ access only for root.
+if head -qc0 /proc/self/map_files/* 2>&1 | grep -w 'Operation not permitted'; 
then
+  exit 77
+fi
+
+tempfiles deleted deleted-lib.so
+cp -p ${abs_builddir}/deleted ${abs_builddir}/deleted-lib.so .
+pid=$(testrun ${abs_builddir}/deleted)
+sleep 1
+tempfiles bt
+testrun ${abs_top_builddir}/src/stack -p $pid >bt
+kill -9 $pid
+wait
+grep -w libfunc bt
+grep -w main bt
-- 
1.8.3.1

--- End Message ---

Reply via email to