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

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