This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix "nosharedlibrary + continue + shared lib event" crash


GDB misbehaves if you run the "nosharelibrary" command, continue
execution, and then the program hits the shared library event
breakpoint.  On my system it aborts like this:

 (gdb) nosharedlibrary
 (gdb) c
 Continuing.
 pure virtual method called
 terminate called without an active exception
 Aborted (core dumped)

Though it's really undefined behavior territory, caused by deferencing
a dangling solib event probe pointer.

I've observed this by running "nosharedlibrary" when stopped at the
entry point, but it should happen at any other point, if the program
does a dlopen/dlclose after.

The fix is to discard an objfile's probes from the svr4 probes table
when an objfile is about to be released.

New test included, works with both native and gdbserver testing.

Valgrind log:

 (gdb) starti
 (gdb) nosharedlibrary
 (gdb) c
 Continuing.
 ==24895== Invalid read of size 8
 ==24895==    at 0x89E5FB: solib_event_probe_action(probe_and_action*) (solib-svr4.c:1735)
 ==24895==    by 0x89E95A: svr4_handle_solib_event() (solib-svr4.c:1872)
 ==24895==    by 0x8A7198: handle_solib_event() (solib.c:1274)
 ==24895==    by 0x4E3407: bpstat_stop_status(address_space const*, unsigned long, thread_info*, target_waitstatus const*, bpstats*) (breakpoint.c:5407)
 ==24895==    by 0x721F41: handle_signal_stop(execution_control_state*) (infrun.c:5685)
 ==24895==    by 0x720B11: handle_inferior_event(execution_control_state*) (infrun.c:5129)
 ==24895==    by 0x71DD93: fetch_inferior_event(void*) (infrun.c:3748)
 ==24895==    by 0x7059C3: inferior_event_handler(inferior_event_type, void*) (inf-loop.c:43)
 ==24895==    by 0x874DF0: remote_async_serial_handler(serial*, void*) (remote.c:14039)
 ==24895==    by 0x894101: run_async_handler_and_reschedule(serial*) (ser-base.c:137)
 ==24895==    by 0x8941E6: fd_event(int, void*) (ser-base.c:188)
 ==24895==    by 0x67AFEF: handle_file_event(file_handler*, int) (event-loop.c:732)
 ==24895==  Address 0x18b63860 is 0 bytes inside a block of size 136 free'd
 ==24895==    at 0x4C2E616: operator delete(void*, unsigned long) (vg_replace_malloc.c:585)
 ==24895==    by 0x8C6A12: stap_probe::~stap_probe() (stap-probe.c:124)
 ==24895==    by 0x66F7DB: probe_key_free(bfd*, void*) (elfread.c:1382)
 ==24895==    by 0x69B705: bfdregistry_callback_adaptor(void (*)(registry_container*, void*), registry_container*, void*) (gdb_bfd.c:131)
 ==24895==    by 0x855A57: registry_clear_data(registry_data_registry*, void (*)(void (*)(registry_container*, void*), registry_container*, void*), registry_container*, registry_fields*) (registry.c:79)
 ==24895==    by 0x855B01: registry_container_free_data(registry_data_registry*, void (*)(void (*)(registry_container*, void*), registry_container*, void*), registry_container*, registry_fields*) (registry.c:92)
 ==24895==    by 0x69B783: bfd_free_data(bfd*) (gdb_bfd.c:131)
 ==24895==    by 0x69C4BA: gdb_bfd_unref(bfd*) (gdb_bfd.c:609)
 ==24895==    by 0x7CC33F: objfile::~objfile() (objfiles.c:651)
 ==24895==    by 0x7CD559: objfile_purge_solibs() (objfiles.c:1021)
 ==24895==    by 0x8A7132: no_shared_libraries(char const*, int) (solib.c:1252)
 ==24895==    by 0x548E3D: do_const_cfunc(cmd_list_element*, char const*, int) (cli-decode.c:106)
 ==24895==  Block was alloc'd at
 ==24895==    at 0x4C2D42A: operator new(unsigned long) (vg_replace_malloc.c:334)
 ==24895==    by 0x8C527C: handle_stap_probe(objfile*, sdt_note*, std::vector<probe*, std::allocator<probe*> >*, unsigned long) (stap-probe.c:1561)
 ==24895==    by 0x8C5535: stap_static_probe_ops::get_probes(std::vector<probe*, std::allocator<probe*> >*, objfile*) const (stap-probe.c:1656)
 ==24895==    by 0x66F71B: elf_get_probes(objfile*) (elfread.c:1365)
 ==24895==    by 0x7EDD85: find_probes_in_objfile(objfile*, char const*, char const*) (probe.c:227)
 ==24895==    by 0x4DF382: create_longjmp_master_breakpoint() (breakpoint.c:3275)
 ==24895==    by 0x4F6562: breakpoint_re_set() (breakpoint.c:13828)
 ==24895==    by 0x8A66AA: solib_add(char const*, int, int) (solib.c:1010)
 ==24895==    by 0x89F7C6: enable_break(svr4_info*, int) (solib-svr4.c:2360)
 ==24895==    by 0x8A104C: svr4_solib_create_inferior_hook(int) (solib-svr4.c:2992)
 ==24895==    by 0x8A70B9: solib_create_inferior_hook(int) (solib.c:1215)
 ==24895==    by 0x70C073: post_create_inferior(target_ops*, int) (infcmd.c:467)
 ==24895==
 pure virtual method called
 terminate called without an active exception
 ==24895==
 ==24895== Process terminating with default action of signal 6 (SIGABRT): dumping core
 ==24895==    at 0x7CF3750: raise (raise.c:51)
 ==24895==    by 0x7CF4D30: abort (abort.c:79)
 ==24895==    by 0xB008F4: __gnu_cxx::__verbose_terminate_handler() (in build/gdb/gdb)
 ==24895==    by 0xAFF845: __cxxabiv1::__terminate(void (*)()) (in build/gdb/gdb)
 ==24895==    by 0xAFF890: std::terminate() (in build/gdb/gdb)
 ==24895==    by 0xAFF95E: __cxa_pure_virtual (in build/gdb/gdb)
 ==24895==    by 0x89E610: solib_event_probe_action(probe_and_action*) (solib-svr4.c:1735)
 ==24895==    by 0x89E95A: svr4_handle_solib_event() (solib-svr4.c:1872)
 ==24895==    by 0x8A7198: handle_solib_event() (solib.c:1274)
 ==24895==    by 0x4E3407: bpstat_stop_status(address_space const*, unsigned long, thread_info*, target_waitstatus const*, bpstats*) (breakpoint.c:5407)
 ==24895==    by 0x721F41: handle_signal_stop(execution_control_state*) (infrun.c:5685)
 ==24895==    by 0x720B11: handle_inferior_event(execution_control_state*) (infrun.c:5129)
 ==24895==

Note, this little bit in the patch is just a cleanup that I noticed:

 -  lookup.prob = prob;
    lookup.address = address;

That line isn't necessary because hashing/comparison only looks at the
address.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* solib-svr4.c (svr4_free_objfile_observer): New.
	(probe_and_action::objfile): New field.
	(probes_table_htab_remove_objfile_probes)
	(probes_table_remove_objfile_probes): New functions.
	(register_solib_event_probe): Add 'objfile' parameter.  Store it
	in the new probe_and_action.  Don't store the probe in 'lookup'.
	(svr4_create_probe_breakpoints): Pass objfile to
	register_solib_event_probe.
	(_initialize_svr4_solib): Register a free_objfile observer.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/solib-probes-nosharedlibrary.c,
	gdb.base/solib-probes-nosharedlibrary.exp: New files.
---
 gdb/solib-svr4.c                                   | 46 +++++++++++++++++--
 .../gdb.base/solib-probes-nosharedlibrary.c        | 22 ++++++++++
 .../gdb.base/solib-probes-nosharedlibrary.exp      | 51 ++++++++++++++++++++++
 3 files changed, 116 insertions(+), 3 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.c
 create mode 100644 gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.exp

diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index cf83196721f..2c79dfec2bb 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -50,6 +50,7 @@ static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
 static int svr4_have_link_map_offsets (void);
 static void svr4_relocate_main_executable (void);
 static void svr4_free_library_list (void *p_list);
+static void probes_table_remove_objfile_probes (struct objfile *objfile);
 
 /* On SVR4 systems, a list of symbols in the dynamic linker where
    GDB can try to place a breakpoint to monitor shared library
@@ -1025,6 +1026,14 @@ struct svr4_library_list
   CORE_ADDR main_lm;
 };
 
+/* This module's 'free_objfile' observer.  */
+
+static void
+svr4_free_objfile_observer (struct objfile *objfile)
+{
+  probes_table_remove_objfile_probes (objfile);
+}
+
 /* Implementation for target_so_ops.free_so.  */
 
 static void
@@ -1636,6 +1645,9 @@ struct probe_and_action
 
   /* The action.  */
   enum probe_action action;
+
+  /* The objfile where this probe was found.  */
+  struct objfile *objfile;
 };
 
 /* Returns a hash code for the probe_and_action referenced by p.  */
@@ -1660,11 +1672,37 @@ equal_probe_and_action (const void *p1, const void *p2)
   return pa1->address == pa2->address;
 }
 
+/* Traversal function for probes_table_remove_objfile_probes.  */
+
+static int
+probes_table_htab_remove_objfile_probes (void **slot, void *info)
+{
+  probe_and_action *pa = (probe_and_action *) *slot;
+  struct objfile *objfile = (struct objfile *) info;
+
+  if (pa->objfile == objfile)
+    htab_clear_slot (get_svr4_info ()->probes_table, slot);
+
+  return 1;
+}
+
+/* Remove all probes that belong to OBJFILE from the probes table.  */
+
+static void
+probes_table_remove_objfile_probes (struct objfile *objfile)
+{
+  svr4_info *info = get_svr4_info ();
+  if (info->probes_table != nullptr)
+    htab_traverse_noresize (info->probes_table,
+			    probes_table_htab_remove_objfile_probes, objfile);
+}
+
 /* Register a solib event probe and its associated action in the
    probes table.  */
 
 static void
-register_solib_event_probe (probe *prob, CORE_ADDR address,
+register_solib_event_probe (struct objfile *objfile,
+			    probe *prob, CORE_ADDR address,
 			    enum probe_action action)
 {
   struct svr4_info *info = get_svr4_info ();
@@ -1677,7 +1715,6 @@ register_solib_event_probe (probe *prob, CORE_ADDR address,
 					    equal_probe_and_action,
 					    xfree, xcalloc, xfree);
 
-  lookup.prob = prob;
   lookup.address = address;
   slot = htab_find_slot (info->probes_table, &lookup, INSERT);
   gdb_assert (*slot == HTAB_EMPTY_ENTRY);
@@ -1686,6 +1723,7 @@ register_solib_event_probe (probe *prob, CORE_ADDR address,
   pa->prob = prob;
   pa->address = address;
   pa->action = action;
+  pa->objfile = objfile;
 
   *slot = pa;
 }
@@ -2030,7 +2068,7 @@ svr4_create_probe_breakpoints (struct gdbarch *gdbarch,
 	  CORE_ADDR address = p->get_relocated_address (objfile);
 
 	  create_solib_event_breakpoint (gdbarch, address);
-	  register_solib_event_probe (p, address, action);
+	  register_solib_event_probe (objfile, p, address, action);
 	}
     }
 
@@ -3224,4 +3262,6 @@ _initialize_svr4_solib (void)
   svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core;
   svr4_so_ops.update_breakpoints = svr4_update_solib_event_breakpoints;
   svr4_so_ops.handle_event = svr4_handle_solib_event;
+
+  gdb::observers::free_objfile.attach (svr4_free_objfile_observer);
 }
diff --git a/gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.c b/gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.c
new file mode 100644
index 00000000000..d94b8074ec0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.c
@@ -0,0 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.exp b/gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.exp
new file mode 100644
index 00000000000..0d5dc870ad5
--- /dev/null
+++ b/gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.exp
@@ -0,0 +1,51 @@
+# Copyright 2005-2019 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Regression test for a bug where GDB would misbehave (most likely
+# crash) if you ran the "nosharelibrary" command, continued execution,
+# and then the program hit the shared library event breakpoint.  GDB
+# would deference a dangling solib event probe pointer.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
+if {$use_gdb_stub && [target_info exists gdb,do_reload_on_run]} {
+    # This is the path taken by gdbserver "target remote" boards.
+    if { [gdb_reload] != 0 } {
+	untested "could not run to initial instruction"
+	return
+    }
+    pass "stopped at entry"
+} else {
+    if { [gdb_starti_cmd] < 0 } {
+	untested "could not run to initial instruction"
+	return
+    }
+    gdb_test "" "Program stopped.*" "stopped at entry"
+}
+
+# The program should stop at the first instruction, before the shared
+# library event breakpoint is first hit.  On systems where probes are
+# present in the dynamic linker, such as GNU/Linux, discarding all
+# shared libraries discards such probes too.  The probes-based
+# interface can no longer be used.
+gdb_test_no_output "nosharedlibrary"
+
+# Continue to main(), past the solib event.
+gdb_breakpoint main
+gdb_continue_to_breakpoint "main"
-- 
2.14.5


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]