From: Martin KOCH <Martin.KOCH@bachmann.info>
To: <gdb-patches@sourceware.org>
Cc: <simark@simark.ca>, Martin KOCH <Martin.KOCH@bachmann.info>
Subject: [PATCH v2] gdb/remote: fix assertions when attaching in non-stop mode
Date: Fri, 19 Jun 2026 11:25:35 +0200 [thread overview]
Message-ID: <20260619092535.2476689-1-Martin.KOCH@bachmann.info> (raw)
In-Reply-To: <15e03eb6-40b4-4f69-a96b-57e3650e021f@simark.ca>
When connecting to a remote target in non-stop mode against a
multi-threaded inferior stopped at raise(SIGSTOP), three internal-error
assertions can fire in sequence:
remote.c:546: mark_async_event_handler: Assertion 'this->is_async_p ()' failed.
thread.c:429: set_pending_waitstatus: Assertion 'this->internal_state () ==
THREAD_INT_STOPPED || ...' failed.
thread.c:426: set_pending_waitstatus: Assertion '!this->has_pending_waitstatus ()' failed.
The first one is what PR 30630 reports and is fixed with the PR proposed
by Mikhail Terekhov, but with the fix the two other assertions surface:
* thread.c:429: addressed by reordering set_internal_state / set_state
to run before set_pending_waitstatus.
* thread.c:426: addressed by clearing any existing pending wait
status before installing the new one, when gdbserver delivers
multiple events for the same thread.
These assertions are reached through
remote.c:process_initial_stop_replies, which only runs on the initial
connection to a remote target, not via GDB's own "attach" command. The
new test gdb.threads/attach-non-stop-stopped.exp reproduces the issue:
it starts gdbserver attached to a multi-threaded inferior that is
already stopped via a pending SIGSTOP, then connects GDB to it in
non-stop mode. Without this fix the connection trips the assertions
above; with it, the connection succeeds.
[1] https://sourceware.org/pipermail/gdb-patches/2023-October/202937.html
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30630
Signed-off-by: Martin KOCH <Martin.KOCH@bachmann.info>
---
gdb/remote.c | 14 ++-
.../gdb.threads/attach-non-stop-stopped.c | 78 +++++++++++++
.../gdb.threads/attach-non-stop-stopped.exp | 104 ++++++++++++++++++
3 files changed, 191 insertions(+), 5 deletions(-)
create mode 100644 gdb/testsuite/gdb.threads/attach-non-stop-stopped.c
create mode 100644 gdb/testsuite/gdb.threads/attach-non-stop-stopped.exp
diff --git a/gdb/remote.c b/gdb/remote.c
index 2961664cf33..4148301bc6f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5237,13 +5237,17 @@ remote_target::process_initial_stop_replies (int from_tty)
ws.set_stopped (sig);
}
- if (ws.kind () != TARGET_WAITKIND_STOPPED
- || ws.sig () != GDB_SIGNAL_0)
- evthread->set_pending_waitstatus (ws);
-
set_internal_state (this, event_ptid, THREAD_INT_STOPPED);
set_state (this, event_ptid, THREAD_STOPPED);
get_remote_thread_info (evthread)->set_not_resumed ();
+
+ if (ws.kind () != TARGET_WAITKIND_STOPPED
+ || ws.sig () != GDB_SIGNAL_0)
+ {
+ if (evthread->has_pending_waitstatus ())
+ evthread->clear_pending_waitstatus ();
+ evthread->set_pending_waitstatus (ws);
+ }
}
/* "Notice" the new inferiors before anything related to
@@ -8384,7 +8388,7 @@ remote_target::queued_stop_reply (ptid_t ptid)
remote_state *rs = get_remote_state ();
stop_reply_up r = remote_notif_remove_queued_reply (ptid);
- if (!rs->stop_reply_queue.empty () && target_can_async_p ())
+ if (!rs->stop_reply_queue.empty () && target_is_async_p ())
{
/* There's still at least an event left. */
rs->mark_async_event_handler ();
diff --git a/gdb/testsuite/gdb.threads/attach-non-stop-stopped.c b/gdb/testsuite/gdb.threads/attach-non-stop-stopped.c
new file mode 100644
index 00000000000..c58562892e9
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/attach-non-stop-stopped.c
@@ -0,0 +1,78 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2026 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 program creates a few threads and then stops the whole process
+ with a pending SIGSTOP, leaving it group-stopped. It is intended to
+ be started outside of GDB and then attached to. See
+ attach-non-stop-stopped.exp. */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+#include <assert.h>
+
+/* Number of threads we'll create, in addition to the main thread. */
+#define N_THREADS 4
+
+/* Used to make sure all threads are running before we stop the
+ process. */
+static pthread_barrier_t barrier;
+
+/* Entry point for threads. Loops forever. */
+
+static void *
+thread_func (void *arg)
+{
+ pthread_barrier_wait (&barrier);
+
+ while (1)
+ sleep (1);
+
+ return NULL;
+}
+
+int
+main (void)
+{
+ pthread_t threads[N_THREADS];
+ int i;
+
+ /* Safety net, in case the process is left running. */
+ alarm (300);
+
+ pthread_barrier_init (&barrier, NULL, N_THREADS + 1);
+
+ for (i = 0; i < N_THREADS; ++i)
+ {
+ int rc = pthread_create (&threads[i], NULL, thread_func, NULL);
+ assert (rc == 0);
+ }
+
+ /* Wait until all threads are up and running. */
+ pthread_barrier_wait (&barrier);
+
+ /* Stop the whole process (all threads) with a real, pending SIGSTOP.
+ When GDB later attaches in non-stop mode against a remote target,
+ each thread is reported with this pending signal. That is what
+ used to trigger the assertions described in PR gdb/30630. */
+ raise (SIGSTOP);
+
+ while (1)
+ sleep (1);
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/attach-non-stop-stopped.exp b/gdb/testsuite/gdb.threads/attach-non-stop-stopped.exp
new file mode 100644
index 00000000000..af68f8f781a
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/attach-non-stop-stopped.exp
@@ -0,0 +1,104 @@
+# Copyright 2026 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 PR gdb/30630.
+#
+# Start gdbserver attached to a multi-threaded inferior that is already
+# stopped (here, via a pending SIGSTOP), then connect GDB to it in
+# non-stop mode.
+
+load_lib gdbserver-support.exp
+
+require allow_gdbserver_tests
+require can_spawn_for_attach
+
+standard_testfile
+
+if {[build_executable "failed to prepare" $testfile $srcfile \
+ {debug pthreads}] == -1} {
+ return
+}
+
+# Number of threads the test program creates (see N_THREADS in .c file)
+set n_threads 4
+set total [expr {$n_threads + 1}]
+
+# Spawn the program. It creates its threads and then stops the whole
+# process with a pending SIGSTOP.
+set test_spawn_id [spawn_wait_for_attach $binfile]
+set testpid [spawn_id_get_pid $test_spawn_id]
+
+save_vars { GDBFLAGS } {
+ append GDBFLAGS " -ex \"set non-stop on\""
+
+ if { ![is_remote host] && ![is_remote target] } {
+ append GDBFLAGS " -ex \"set sysroot\""
+ }
+
+ clean_restart $testfile
+}
+
+gdb_test "disconnect" ".*"
+
+# Start gdbserver already attached to the stopped process.
+set res [gdbserver_start "--attach" $testpid]
+if { [llength $res] == 0 } {
+ kill_wait_spawned_process $test_spawn_id
+ unsupported "gdbserver --attach failed"
+ return
+}
+set gdbserver_gdbport [lindex $res 1]
+
+# Connect to gdbserver in non-stop mode. Without the fix for PR 30630,
+# GDB hits an internal error here.
+set connected 0
+gdb_test_multiple "target remote $gdbserver_gdbport" \
+ "connect to gdbserver in non-stop mode" {
+ -re "Non-stop mode requested, but remote does not support non-stop.*$gdb_prompt $" {
+ unsupported $gdb_test_name
+ }
+ -re "Remote debugging using .*$gdb_prompt $" {
+ pass $gdb_test_name
+ set connected 1
+ }
+}
+
+if { !$connected } {
+ kill_wait_spawned_process $test_spawn_id
+ return
+}
+
+# GDB survived the connection. As a sanity check, confirm it is
+# responsive and that all threads are present.
+for {set attempt 0} {$attempt < 10} {incr attempt} {
+ set thread_count 0
+ gdb_test_multiple "info threads" "" {
+ -re "\r\n\[ *\]+$decimal\[ \t\]+(Thread|LWP|process)\[^\r\n\]*" {
+ incr thread_count
+ exp_continue
+ }
+ -re "$gdb_prompt " {
+ }
+ }
+ if {$thread_count >= $total} {
+ break
+ }
+ sleep 1
+}
+gdb_assert {$thread_count == $total} "all threads present"
+
+gdb_test "detach" "Detaching from.*"
+
+kill_wait_spawned_process $test_spawn_id
base-commit: ddc575edf104eb477018a32b31074beffb179034
--
2.43.0
next prev parent reply other threads:[~2026-06-19 9:26 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-11 5:36 [PATCH] " Martin KOCH
2026-06-17 3:44 ` Simon Marchi
2026-06-19 9:25 ` Martin KOCH [this message]
2026-07-08 18:57 ` [PATCH v2] " Simon Marchi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260619092535.2476689-1-Martin.KOCH@bachmann.info \
--to=martin.koch@bachmann.info \
--cc=gdb-patches@sourceware.org \
--cc=simark@simark.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox