diff options
-rw-r--r-- | gdb/testsuite/gdb.multi/attach-while-running.c | 26 | ||||
-rw-r--r-- | gdb/testsuite/gdb.multi/attach-while-running.exp | 73 | ||||
-rw-r--r-- | gdbserver/thread-db.cc | 29 |
3 files changed, 116 insertions, 12 deletions
diff --git a/gdb/testsuite/gdb.multi/attach-while-running.c b/gdb/testsuite/gdb.multi/attach-while-running.c new file mode 100644 index 0000000..dd321df --- /dev/null +++ b/gdb/testsuite/gdb.multi/attach-while-running.c @@ -0,0 +1,26 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 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/>. */ + +#include <unistd.h> + +int global_var = 123; + +int +main (void) +{ + sleep (30); +} diff --git a/gdb/testsuite/gdb.multi/attach-while-running.exp b/gdb/testsuite/gdb.multi/attach-while-running.exp new file mode 100644 index 0000000..125273d --- /dev/null +++ b/gdb/testsuite/gdb.multi/attach-while-running.exp @@ -0,0 +1,73 @@ +# Copyright 2022 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/>. + +# This test was introduced to reproduce a specific bug in GDBserver, where +# attaching an inferior while another one was running would trigger a segfault +# in GDBserver. Reproducing the bug required specific circumstances: +# +# - The first process must be far enough to have loaded its libc or +# libpthread (whatever triggers the loading of libthread_db), such that +# its proc->priv->thread_db is not nullptr +# +# - However, its lwp must still be in the `!lwp->thread_known` state, +# meaning GDBserver hasn't asked libthread_db to compute the thread +# handle yet. That means, GDB must not have refreshed the thread list +# yet, since that would cause the thread handles to be computed. That +# means, no stopping on a breakpoint, since that causes a thread list +# update. That's why the first inferior needs to be started with "run +# &". +# +# - Attaching the second process would segfault GDBserver. +# +# All of this to say, if modifying this test, please keep in mind the original +# intent. + +standard_testfile + +if [use_gdb_stub] { + unsupported "test requires running" + return +} + +if { [build_executable "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +proc do_test {} { + save_vars { $::GDBFLAGS } { + append ::GDBFLAGS " -ex \"maint set target-non-stop on\"" + clean_restart $::binfile + } + + gdb_test_multiple "run &" "" { + -re ".*$::gdb_prompt " { + pass $gdb_test_name + } + } + gdb_test "add-inferior" "Added inferior 2 on connection 1 .*" + gdb_test "inferior 2" "Switching to inferior 2 .*" + + set spawn_id [spawn_wait_for_attach $::binfile] + set pid [spawn_id_get_pid $spawn_id] + + # This call would crash GDBserver. + gdb_attach $pid + + # Read a variable from the inferior, just to make sure the attach worked + # fine. + gdb_test "print global_var" " = 123" +} + +do_test diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc index 6e0e222..bf98ca9 100644 --- a/gdbserver/thread-db.cc +++ b/gdbserver/thread-db.cc @@ -155,30 +155,35 @@ thread_db_state_str (td_thr_state_e state) } #endif -/* Get thread info about PTID, accessing memory via the current - thread. */ +/* Get thread info about PTID. */ static int find_one_thread (ptid_t ptid) { - td_thrhandle_t th; - td_thrinfo_t ti; - td_err_e err; - struct lwp_info *lwp; - struct thread_db *thread_db = current_process ()->priv->thread_db; - int lwpid = ptid.lwp (); - thread_info *thread = find_thread_ptid (ptid); - lwp = get_thread_lwp (thread); + lwp_info *lwp = get_thread_lwp (thread); if (lwp->thread_known) return 1; - /* Get information about this thread. */ - err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th); + /* Get information about this thread. libthread_db will need to read some + memory, which will be done on the current process, so make PTID's process + the current one. */ + process_info *proc = find_process_pid (ptid.pid ()); + gdb_assert (proc != nullptr); + + scoped_restore_current_thread restore_thread; + switch_to_process (proc); + + thread_db *thread_db = proc->priv->thread_db; + td_thrhandle_t th; + int lwpid = ptid.lwp (); + td_err_e err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, + &th); if (err != TD_OK) error ("Cannot get thread handle for LWP %d: %s", lwpid, thread_db_err_str (err)); + td_thrinfo_t ti; err = thread_db->td_thr_get_info_p (&th, &ti); if (err != TD_OK) error ("Cannot get thread info for LWP %d: %s", |