Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Simon Marchi <simon.marchi@efficios.com>
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@efficios.com>
Subject: [PATCH 2/6] gdb/corefile: handle no signalled thread when writing NT_GDB_TDESC
Date: Fri, 16 Oct 2026 01:20:07 -0400	[thread overview]
Message-ID: <20261016052100.1386999-3-simon.marchi@efficios.com> (raw)
In-Reply-To: <20261016052100.1386999-1-simon.marchi@efficios.com>

Commit c14993e9dc5 ("gdb/corefile: write NT_GDB_TDESC based on
signalled thread") made linux_make_corefile_notes and
elf_none_make_corefile_notes use the signalled thread's architecture to
write the NT_GDB_TDESC note:

  gdbarch = target_thread_architecture (signalled_thr->ptid);

However, gcore_find_signalled_thread returns nullptr if the selected
thread has exited and no other thread was stopped by a signal.  The code
just above already checks for that, but this line does not, leading to
a null pointer dereference.

This can happen in non-stop mode, if the selected thread exits and the
other threads are then interrupted.  For instance, with an UBSan build:

  (gdb) thread
  [Current thread is 2 (LWP 301816) (exited)]
  (gdb) gcore
  .../gdb/linux-tdep.c:2586:40: runtime error: member access within null pointer of type 'struct thread_info'

(without UBSan it's just a segfault)

Fix this by falling back to the gdbarch passed to the function, which is
what was used before commit c14993e9dc5.

Add a test that reproduces the scenario above.

Change-Id: I058ec7a19dc7425e2728379317ba1dd879904eb9
---
 gdb/elf-none-tdep.c                           |  7 +-
 gdb/linux-tdep.c                              |  7 +-
 .../gcore-exited-selected-thread.c            | 51 ++++++++++++
 .../gcore-exited-selected-thread.exp          | 83 +++++++++++++++++++
 4 files changed, 144 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.threads/gcore-exited-selected-thread.c
 create mode 100644 gdb/testsuite/gdb.threads/gcore-exited-selected-thread.exp

diff --git a/gdb/elf-none-tdep.c b/gdb/elf-none-tdep.c
index 0d063038f5ec..e046d99bf51b 100644
--- a/gdb/elf-none-tdep.c
+++ b/gdb/elf-none-tdep.c
@@ -113,8 +113,11 @@ elf_none_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
   /* Include the target description when possible.  Some architectures
      allow for per-thread gdbarch so we should really be emitting a tdesc
      per-thread, however, we don't currently support reading in a
-     per-thread tdesc, so just emit the tdesc for the signalled thread.  */
-  gdbarch = target_thread_architecture (signalled_thr->ptid);
+     per-thread tdesc, so just emit the tdesc for the signalled thread.  If
+     there is no signalled thread, fall back to GDBARCH.  */
+  if (signalled_thr != nullptr)
+    gdbarch = target_thread_architecture (signalled_thr->ptid);
+
   gcore_elf_make_tdesc_note (gdbarch, obfd, &note_data, note_size);
 
   return note_data;
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 4f6910694d50..fbc96c0975a7 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2582,8 +2582,11 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
   /* Include the target description when possible.  Some architectures
      allow for per-thread gdbarch so we should really be emitting a tdesc
      per-thread, however, we don't currently support reading in a
-     per-thread tdesc, so just emit the tdesc for the signalled thread.  */
-  gdbarch = target_thread_architecture (signalled_thr->ptid);
+     per-thread tdesc, so just emit the tdesc for the signalled thread.  If
+     there is no signalled thread, fall back to GDBARCH.  */
+  if (signalled_thr != nullptr)
+    gdbarch = target_thread_architecture (signalled_thr->ptid);
+
   gcore_elf_make_tdesc_note (gdbarch, obfd, &note_data, note_size);
 
   return note_data;
diff --git a/gdb/testsuite/gdb.threads/gcore-exited-selected-thread.c b/gdb/testsuite/gdb.threads/gcore-exited-selected-thread.c
new file mode 100644
index 000000000000..5a2fbf710831
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/gcore-exited-selected-thread.c
@@ -0,0 +1,51 @@
+/* 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/>.  */
+
+#include <assert.h>
+#include <pthread.h>
+#include <unistd.h>
+
+static volatile int joined = 0;
+
+static void *
+thread_func (void *arg)
+{
+  return NULL; /* Break here.  */
+}
+
+int
+main (void)
+{
+  pthread_t thread;
+  int res;
+
+  alarm (30);
+
+  res = pthread_create (&thread, NULL, thread_func, NULL);
+  assert (res == 0);
+
+  res = pthread_join (thread, NULL);
+  assert (res == 0);
+
+  joined = 1; /* After join.  */
+
+  /* Keep running until GDB interrupts us.  */
+  while (1)
+    sleep (1);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/gcore-exited-selected-thread.exp b/gdb/testsuite/gdb.threads/gcore-exited-selected-thread.exp
new file mode 100644
index 000000000000..10a89ffa9060
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/gcore-exited-selected-thread.exp
@@ -0,0 +1,83 @@
+# 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/>.
+
+# Test generating a core file when the selected thread has exited and
+# no other thread was stopped by a signal.  In that case, there is no
+# "signalled thread" to dump first.
+
+standard_testfile
+
+if { [build_executable "failed to prepare" $testfile $srcfile \
+	  {debug pthreads}] == -1 } {
+    return
+}
+
+save_vars { ::GDBFLAGS } {
+    append ::GDBFLAGS " -ex \"set non-stop on\""
+    clean_restart $::testfile
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here."]
+gdb_run_cmd
+gdb_test "" "Thread 2 \[^\r\n\]* hit Breakpoint $decimal, thread_func .*" \
+    "run to breakpoint in thread"
+gdb_test "thread 2" "Switching to thread 2 .*" "select thread 2"
+
+# Print a message when the main thread has joined thread 2.  This tells
+# us that thread 2 has exited, without relying on GDB reporting the
+# thread exit right away, which it doesn't do with remote targets.  Use
+# a dprintf rather than a breakpoint, so that the main thread does not
+# stop with a SIGTRAP and become the signalled thread.
+gdb_test "dprintf [gdb_get_line_number "After join."],\"thread 2 joined\\n\"" \
+    "Dprintf $decimal at .*" "set dprintf after join"
+
+# Resume thread 2 and let it exit.  It stays the selected thread.
+gdb_test_multiple "continue &" "continue thread 2 until exit" {
+    -re "Continuing\\.\r\n$gdb_prompt " {
+	exp_continue
+    }
+    -re "thread 2 joined\r\n" {
+	pass $gdb_test_name
+    }
+}
+
+# Stop the main thread.  Being interrupted, it doesn't have a stop
+# signal.
+gdb_test_multiple "interrupt -a" "" {
+    -re "^interrupt -a\r\n$gdb_prompt " {
+	exp_continue
+    }
+    -re "Thread 1 \[^\r\n\]* stopped\\.\r\n" {
+	pass $gdb_test_name
+    }
+}
+
+gdb_test "thread" "Current thread is 2 \\(.*\\) \\(exited\\)\\\]" \
+    "selected thread has exited"
+
+set corefile [host_standard_output_file $testfile.core]
+if { [gdb_gcore_cmd $corefile "save a corefile"] != 1 } {
+    return
+}
+
+clean_restart $::testfile
+if { [gdb_core_cmd $corefile "load corefile"] != 1 } {
+    return
+}
+
+# Check that the core file contains only the main thread.
+gdb_test "info threads" \
+    "Id +Target Id +Frame *\r\n\\* 1 +Thread \[^\r\n\]*" \
+    "core file contains main thread"
-- 
2.56.0


  parent reply	other threads:[~2026-10-16  5:21 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-16  5:20 [PATCH 0/6] Fix generating core files when GPU waves are present Simon Marchi
2026-10-16  5:20 ` [PATCH 1/6] gdb/testsuite: make with_rocm_gpu_lock transparent to return Simon Marchi
2026-10-16  5:20 ` Simon Marchi [this message]
2026-10-16  5:20 ` [PATCH 3/6] gdb/amd-dbgapi: handle regno == -1 in fetch_registers and store_registers Simon Marchi
2026-10-16  5:20 ` [PATCH 4/6] gdb/linux-tdep: don't write core file notes for non-Linux threads Simon Marchi
2026-10-16  5:20 ` [PATCH 5/6] gdb/amd-dbgapi: defer TARGET_OBJECT_AUXV xfers to the beneath target Simon Marchi
2026-10-16  5:20 ` [PATCH 6/6] gdb/linux-tdep: ignore non-Linux threads in get_ptid_for_slash_proc 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=20261016052100.1386999-3-simon.marchi@efficios.com \
    --to=simon.marchi@efficios.com \
    --cc=gdb-patches@sourceware.org \
    /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