|
|
7b26da |
NOTE: This patch has been forwardported from RHEL-6.7.
|
|
|
7b26da |
|
|
|
7b26da |
From: Pedro Alves <palves@redhat.com>
|
|
|
7b26da |
Date: Fri, 20 Feb 2015 19:10:08 +0000
|
|
|
7b26da |
Subject: [PATCH] PR18006: internal error if threaded program calls
|
|
|
7b26da |
clone(CLONE_VM)
|
|
|
7b26da |
|
|
|
7b26da |
On GNU/Linux, if a pthreaded program has a thread call clone(CLONE_VM)
|
|
|
7b26da |
directly, and then that clone LWP hits a debug event (breakpoint,
|
|
|
7b26da |
etc.) GDB internal errors. Threaded programs shouldn't really be
|
|
|
7b26da |
calling clone directly, but GDB shouldn't crash either.
|
|
|
7b26da |
|
|
|
7b26da |
The crash looks like this:
|
|
|
7b26da |
|
|
|
7b26da |
(gdb) break clone_fn
|
|
|
7b26da |
Breakpoint 1 at 0x4007d8: file clone-thread_db.c, line 35.
|
|
|
7b26da |
(gdb) r
|
|
|
7b26da |
...
|
|
|
7b26da |
[Thread debugging using libthread_db enabled]
|
|
|
7b26da |
...
|
|
|
7b26da |
[New Thread 0x7ffff7fc2700 (LWP 3886)]
|
|
|
7b26da |
../../gdb/linux-thread-db.c:437: internal-error: thread_get_info_callback: Assertion `inout->thread_info != NULL' failed.
|
|
|
7b26da |
A problem internal to GDB has been detected,
|
|
|
7b26da |
further debugging may prove unreliable.
|
|
|
7b26da |
|
|
|
7b26da |
The problem is that 'clone' ends up clearing the parent thread's tid
|
|
|
7b26da |
field in glibc's thread data structure. For x86_64, the glibc code in
|
|
|
7b26da |
question is here:
|
|
|
7b26da |
|
|
|
7b26da |
sysdeps/unix/sysv/linux/x86_64/clone.S:
|
|
|
7b26da |
|
|
|
7b26da |
...
|
|
|
7b26da |
testq $CLONE_THREAD, %rdi
|
|
|
7b26da |
jne 1f
|
|
|
7b26da |
testq $CLONE_VM, %rdi
|
|
|
7b26da |
movl $-1, %eax <----
|
|
|
7b26da |
jne 2f
|
|
|
7b26da |
movl $SYS_ify(getpid), %eax
|
|
|
7b26da |
syscall
|
|
|
7b26da |
2: movl %eax, %fs:PID
|
|
|
7b26da |
movl %eax, %fs:TID <----
|
|
|
7b26da |
1:
|
|
|
7b26da |
|
|
|
7b26da |
When GDB refreshes the thread list out of libthread_db, it finds a
|
|
|
7b26da |
thread with LWP with pid -1 (the clone's parent), which naturally
|
|
|
7b26da |
isn't yet on the thread list. GDB then tries to attach to that bogus
|
|
|
7b26da |
LWP id, that fails, and then GDB gets confused.
|
|
|
7b26da |
|
|
|
7b26da |
The fix is to detect the bad PID early.
|
|
|
7b26da |
|
|
|
7b26da |
Tested on x86-64 Fedora 20. GDBserver doesn't need any fix.
|
|
|
7b26da |
|
|
|
7b26da |
gdb/ChangeLog:
|
|
|
7b26da |
2015-02-20 Pedro Alves <palves@redhat.com>
|
|
|
7b26da |
|
|
|
7b26da |
PR threads/18006
|
|
|
7b26da |
* linux-thread-db.c (thread_get_info_callback): Return early if
|
|
|
7b26da |
the thread's lwp id is -1.
|
|
|
7b26da |
(check_event): On TD_DEATH, if the thread is not on the thread
|
|
|
7b26da |
list, warn instead of erroring out.
|
|
|
7b26da |
|
|
|
7b26da |
gdb/testsuite/ChangeLog:
|
|
|
7b26da |
2015-02-20 Pedro Alves <palves@redhat.com>
|
|
|
7b26da |
|
|
|
7b26da |
PR threads/18006
|
|
|
7b26da |
* gdb.threads/clone-thread_db.c: New file.
|
|
|
7b26da |
* gdb.threads/clone-thread_db.exp: New file.
|
|
|
7b26da |
---
|
|
|
7b26da |
gdb/linux-thread-db.c | 14 +++--
|
|
|
7b26da |
gdb/testsuite/gdb.threads/clone-thread_db.c | 73 +++++++++++++++++++++++++++
|
|
|
7b26da |
gdb/testsuite/gdb.threads/clone-thread_db.exp | 44 ++++++++++++++++
|
|
|
7b26da |
3 files changed, 128 insertions(+), 3 deletions(-)
|
|
|
7b26da |
create mode 100644 gdb/testsuite/gdb.threads/clone-thread_db.c
|
|
|
7b26da |
create mode 100644 gdb/testsuite/gdb.threads/clone-thread_db.exp
|
|
|
7b26da |
|
|
|
7b26da |
Index: gdb-7.6.1/gdb/linux-thread-db.c
|
|
|
7b26da |
===================================================================
|
|
|
7b26da |
--- gdb-7.6.1.orig/gdb/linux-thread-db.c
|
|
|
7b26da |
+++ gdb-7.6.1/gdb/linux-thread-db.c
|
|
|
7b26da |
@@ -422,6 +422,14 @@ thread_get_info_callback (const td_thrha
|
|
|
7b26da |
error (_("thread_get_info_callback: cannot get thread info: %s"),
|
|
|
7b26da |
thread_db_err_str (err));
|
|
|
7b26da |
|
|
|
7b26da |
+ if (ti.ti_lid == -1)
|
|
|
7b26da |
+ {
|
|
|
7b26da |
+ /* We'll get this if a threaded program has a thread call clone
|
|
|
7b26da |
+ with CLONE_VM. `clone' sets the pthread LID of the new LWP
|
|
|
7b26da |
+ to -1, which ends up clearing the parent thread's LID. */
|
|
|
7b26da |
+ return 0;
|
|
|
7b26da |
+ }
|
|
|
7b26da |
+
|
|
|
7b26da |
/* Fill the cache. */
|
|
|
7b26da |
thread_ptid = ptid_build (info->pid, ti.ti_lid, 0);
|
|
|
7b26da |
inout->thread_info = find_thread_ptid (thread_ptid);
|
|
|
7b26da |
@@ -1454,9 +1462,9 @@ check_event (ptid_t ptid)
|
|
|
7b26da |
case TD_DEATH:
|
|
|
7b26da |
|
|
|
7b26da |
if (!in_thread_list (ptid))
|
|
|
7b26da |
- error (_("Spurious thread death event."));
|
|
|
7b26da |
-
|
|
|
7b26da |
- detach_thread (ptid);
|
|
|
7b26da |
+ warning (_("Spurious thread death event."));
|
|
|
7b26da |
+ else
|
|
|
7b26da |
+ detach_thread (ptid);
|
|
|
7b26da |
|
|
|
7b26da |
break;
|
|
|
7b26da |
|
|
|
7b26da |
Index: gdb-7.6.1/gdb/testsuite/gdb.threads/clone-thread_db.c
|
|
|
7b26da |
===================================================================
|
|
|
7b26da |
--- /dev/null
|
|
|
7b26da |
+++ gdb-7.6.1/gdb/testsuite/gdb.threads/clone-thread_db.c
|
|
|
7b26da |
@@ -0,0 +1,75 @@
|
|
|
7b26da |
+/* This testcase is part of GDB, the GNU debugger.
|
|
|
7b26da |
+
|
|
|
7b26da |
+ Copyright 2015 Free Software Foundation, Inc.
|
|
|
7b26da |
+
|
|
|
7b26da |
+ This program is free software; you can redistribute it and/or modify
|
|
|
7b26da |
+ it under the terms of the GNU General Public License as published by
|
|
|
7b26da |
+ the Free Software Foundation; either version 3 of the License, or
|
|
|
7b26da |
+ (at your option) any later version.
|
|
|
7b26da |
+
|
|
|
7b26da |
+ This program is distributed in the hope that it will be useful,
|
|
|
7b26da |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
7b26da |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
7b26da |
+ GNU General Public License for more details.
|
|
|
7b26da |
+
|
|
|
7b26da |
+ You should have received a copy of the GNU General Public License
|
|
|
7b26da |
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
7b26da |
+
|
|
|
7b26da |
+ Test that GDB doesn't lose an event for a thread it didn't know
|
|
|
7b26da |
+ about, until an event is reported for it. */
|
|
|
7b26da |
+
|
|
|
7b26da |
+#define _GNU_SOURCE
|
|
|
7b26da |
+#include <sched.h>
|
|
|
7b26da |
+#include <assert.h>
|
|
|
7b26da |
+#include <stdlib.h>
|
|
|
7b26da |
+#include <sys/types.h>
|
|
|
7b26da |
+#include <sys/wait.h>
|
|
|
7b26da |
+#include <unistd.h>
|
|
|
7b26da |
+#include <pthread.h>
|
|
|
7b26da |
+
|
|
|
7b26da |
+#define STACK_SIZE 0x1000
|
|
|
7b26da |
+
|
|
|
7b26da |
+int clone_pid;
|
|
|
7b26da |
+
|
|
|
7b26da |
+static int
|
|
|
7b26da |
+clone_fn (void *unused)
|
|
|
7b26da |
+{
|
|
|
7b26da |
+ return 0;
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
+void *
|
|
|
7b26da |
+thread_fn (void *arg)
|
|
|
7b26da |
+{
|
|
|
7b26da |
+ unsigned char *stack;
|
|
|
7b26da |
+ int res;
|
|
|
7b26da |
+
|
|
|
7b26da |
+ stack = malloc (STACK_SIZE);
|
|
|
7b26da |
+ assert (stack != NULL);
|
|
|
7b26da |
+
|
|
|
7b26da |
+#ifdef __ia64__
|
|
|
7b26da |
+ clone_pid = __clone2 (clone_fn, stack, STACK_SIZE, CLONE_VM, NULL);
|
|
|
7b26da |
+#else
|
|
|
7b26da |
+ clone_pid = clone (clone_fn, stack + STACK_SIZE, CLONE_VM, NULL);
|
|
|
7b26da |
+#endif
|
|
|
7b26da |
+
|
|
|
7b26da |
+ assert (clone_pid > 0);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ /* Wait for child. */
|
|
|
7b26da |
+ res = waitpid (clone_pid, NULL, __WCLONE);
|
|
|
7b26da |
+ assert (res != -1);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ return NULL;
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
+int
|
|
|
7b26da |
+main (int argc, char **argv)
|
|
|
7b26da |
+{
|
|
|
7b26da |
+ pthread_t child;
|
|
|
7b26da |
+
|
|
|
7b26da |
+ alarm (300);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ pthread_create (&child, NULL, thread_fn, NULL);
|
|
|
7b26da |
+ pthread_join (child, NULL);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ return 0;
|
|
|
7b26da |
+}
|
|
|
7b26da |
Index: gdb-7.6.1/gdb/testsuite/gdb.threads/clone-thread_db.exp
|
|
|
7b26da |
===================================================================
|
|
|
7b26da |
--- /dev/null
|
|
|
7b26da |
+++ gdb-7.6.1/gdb/testsuite/gdb.threads/clone-thread_db.exp
|
|
|
7b26da |
@@ -0,0 +1,44 @@
|
|
|
7b26da |
+# This testcase is part of GDB, the GNU debugger.
|
|
|
7b26da |
+
|
|
|
7b26da |
+# Copyright 2015 Free Software Foundation, Inc.
|
|
|
7b26da |
+
|
|
|
7b26da |
+# This program is free software; you can redistribute it and/or modify
|
|
|
7b26da |
+# it under the terms of the GNU General Public License as published by
|
|
|
7b26da |
+# the Free Software Foundation; either version 3 of the License, or
|
|
|
7b26da |
+# (at your option) any later version.
|
|
|
7b26da |
+#
|
|
|
7b26da |
+# This program is distributed in the hope that it will be useful,
|
|
|
7b26da |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
7b26da |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
7b26da |
+# GNU General Public License for more details.
|
|
|
7b26da |
+#
|
|
|
7b26da |
+# You should have received a copy of the GNU General Public License
|
|
|
7b26da |
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
7b26da |
+
|
|
|
7b26da |
+# This only works on targets with the Linux kernel.
|
|
|
7b26da |
+if ![istarget *-*-linux*] {
|
|
|
7b26da |
+ return
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
+set testfile "clone-thread_db"
|
|
|
7b26da |
+set srcfile ${testfile}.c
|
|
|
7b26da |
+set binfile ${objdir}/${subdir}/${testfile}
|
|
|
7b26da |
+
|
|
|
7b26da |
+if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
|
|
|
7b26da |
+ untested $testfile.exp
|
|
|
7b26da |
+ return -1
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
+gdb_start
|
|
|
7b26da |
+gdb_reinitialize_dir $srcdir/$subdir
|
|
|
7b26da |
+gdb_load ${binfile}
|
|
|
7b26da |
+
|
|
|
7b26da |
+if ![runto_main] {
|
|
|
7b26da |
+ untested "could not run to main"
|
|
|
7b26da |
+ return -1
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
+gdb_test "break clone_fn" "Breakpoint.*at.*file.*$srcfile.*line.*"
|
|
|
7b26da |
+gdb_test "continue" "clone_fn .* at .*" "continue to clone_fn"
|
|
|
7b26da |
+
|
|
|
7b26da |
+gdb_test "continue" "exited normally.*" "continue to end"
|