diff options
author | Pedro Alves <palves@redhat.com> | 2019-04-22 14:20:59 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2019-04-22 14:20:59 +0100 |
commit | 7905fc359d6921c411999633e382330e6fd04fb6 (patch) | |
tree | 0f3a244806093ebf3563379e57fceaf444a10a15 | |
parent | 73f8a5908695e96d8ecd5e0fbe9f1ebb16179547 (diff) | |
download | gdb-7905fc359d6921c411999633e382330e6fd04fb6.zip gdb-7905fc359d6921c411999633e382330e6fd04fb6.tar.gz gdb-7905fc359d6921c411999633e382330e6fd04fb6.tar.bz2 |
Fix "nosharedlibrary + continue + shared lib event" crash
On systems that use the probes-based solib interface, 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:
2019-04-22 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:
2019-04-22 Pedro Alves <palves@redhat.com>
* gdb.base/solib-probes-nosharedlibrary.c,
gdb.base/solib-probes-nosharedlibrary.exp: New files.
-rw-r--r-- | gdb/ChangeLog | 12 | ||||
-rw-r--r-- | gdb/solib-svr4.c | 46 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.c | 22 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/solib-probes-nosharedlibrary.exp | 51 |
5 files changed, 133 insertions, 3 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 0bd363e..5bd5215 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2019-04-22 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. + 2019-04-19 Tom Tromey <tom@tromey.com> * common/queue.h: Remove. diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index cf83196..2c79dfe 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/ChangeLog b/gdb/testsuite/ChangeLog index aaa110c..20bcd25 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2019-04-22 Pedro Alves <palves@redhat.com> + + * gdb.base/solib-probes-nosharedlibrary.c, + gdb.base/solib-probes-nosharedlibrary.exp: New files. + 2019-04-19 Tom Tromey <tromey@adacore.com> * gdb.ada/ptype_union.c: New file. 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 0000000..d94b807 --- /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 0000000..0d5dc87 --- /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" |