Blame SOURCES/gdb-rhbz1210889-thread-call-clone.patch

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