From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17110 invoked by alias); 14 Jun 2006 10:55:25 -0000 Received: (qmail 17088 invoked by uid 22791); 14 Jun 2006 10:55:23 -0000 X-Spam-Check-By: sourceware.org Received: from ip-85-160-19-24.eurotel.cz (HELO host0.dyn.jankratochvil.net) (85.160.19.24) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 14 Jun 2006 10:55:18 +0000 Received: from host0.dyn.jankratochvil.net (localhost [127.0.0.1]) by host0.dyn.jankratochvil.net (8.13.6/8.13.4) with ESMTP id k5EAtB0j020116 for ; Wed, 14 Jun 2006 12:55:11 +0200 Received: (from lace@localhost) by host0.dyn.jankratochvil.net (8.13.6/8.13.6/Submit) id k5EAtABa020115 for gdb-patches@sourceware.org; Wed, 14 Jun 2006 12:55:10 +0200 Date: Wed, 14 Jun 2006 10:55:00 -0000 From: Jan Kratochvil To: gdb-patches@sourceware.org Subject: RFC: Fix crash on i386 (%gs-)threaded programs using execve(2) Message-ID: <20060614105510.GA12067@host0.dyn.jankratochvil.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="C7zPtVaVf+AK4Oqc" Content-Disposition: inline User-Agent: Mutt/1.4.2.1i X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2006-06/txt/msg00216.txt.bz2 --C7zPtVaVf+AK4Oqc Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-length: 1984 Hi all, right now gdb(1) crashes on execve(2) executed by any -lpthread i386 process on recent %gs based TLS glibc. https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=182116 (new test case "gdb.threads/thread-lost") Currently: # gcc -o ./testsuite/gdb.threads/thread-lost ./testsuite/gdb.threads/thread-lost.c -Wall -lpthread -ggdb3 # ./gdb -nx ./testsuite/gdb.threads/thread-lost GNU gdb 6.5.50.20060614-cvs ... (gdb) run Starting program: /home/lace/redhat/src/gdb/testsuite/gdb.threads/thread-lost [Thread debugging using libthread_db enabled] [New Thread -1208801600 (LWP 17677)] Cannot find user-level thread for LWP 17677: generic error (gdb) bt Cannot fetch general-purpose registers for thread -1208801600: generic error With the patch: (gdb) run Starting program: /home/lace/redhat/src/gdb/testsuite/gdb.threads/thread-lost [Thread debugging using libthread_db enabled] [New Thread -1208301888 (LWP 18818)] warning: Original threaded process got lost, dropping threads EXECUTED Program exited normally. It is only a heuristic as execve(2) destroys %gs and gdb(1) fails to find the no longer existing threads through TLS. Patch will warn and turn off the gdb(1) threads support if it finds out %gs==0. On i386 with %gs based TLS NPTLS gdb calls glibc td_ta_map_lwp2thr() which calls ta_howto_reg_thread_area() (case ta_howto_reg_thread_area). After execve(2) it retrieves %gs as 0 and fails to ps_get_thread_area() as its idx must be 6 (glibc TLS descriptor) - value of the first/glibc Linux kernel GDT_ENTRY_TLS_MIN. It is now workarounded as to drop threading support if %gs==0. Do you have an idea for a cleaner solution than this hack of waiting till the threads are no longer accessible? Not aware of some indication which kernel syscall will replace the whole process memory space and the process registers. It also fixes 11 gdb testsuite failures but these were not analysed from case to case regarding possible false positives. Regards, Jan Kratochvil --C7zPtVaVf+AK4Oqc Content-Type: text/plain; charset=us-ascii Content-Description: Patch for i386 GDB as of CVS 2006-06-14 Content-Disposition: attachment; filename="gdb-cvs20060614-thread_lost.patch" Content-length: 7318 Index: i386-linux-nat.c =================================================================== RCS file: /cvs/src/src/gdb/i386-linux-nat.c,v retrieving revision 1.69 diff -u -p -r1.69 i386-linux-nat.c --- i386-linux-nat.c 24 Mar 2006 23:08:16 -0000 1.69 +++ i386-linux-nat.c 14 Jun 2006 10:52:11 -0000 @@ -721,7 +721,19 @@ ps_get_thread_area (const struct ps_proc if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, (void *) idx, (unsigned long) &desc) < 0) - return PS_ERR; + { + /* i386: execve(2) will clear the registers including %gs (idx) */ + if (!idx) + { + extern int thread_db_disconnect (void); + if (thread_db_disconnect ()) + warning (_("Original threaded process got lost, dropping threads")); + /* Caught by thread_from_lwp() like as TD_THR_ZOMBIE. */ + *(int *)base = 0; + return PS_BADADDR; + } + return PS_ERR; + } *(int *)base = desc[1]; return PS_OK; Index: linux-thread-db.c =================================================================== RCS file: /cvs/src/src/gdb/linux-thread-db.c,v retrieving revision 1.16 diff -u -p -r1.16 linux-thread-db.c --- linux-thread-db.c 5 May 2006 22:42:43 -0000 1.16 +++ linux-thread-db.c 14 Jun 2006 10:52:13 -0000 @@ -367,7 +367,12 @@ thread_from_lwp (ptid_t ptid) gdb_assert (is_lwp (ptid)); + th.th_unique = (psaddr_t) -1; err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th); + /* Catch the result from ps_get_thread_area(): + * Original threaded process got lost, dropping threads */ + if (err != TD_OK && th.th_unique == (psaddr_t) 0) + return pid_to_ptid (-1); if (err != TD_OK) error (_("Cannot find user-level thread for LWP %ld: %s"), GET_LWP (ptid), thread_db_err_str (err)); @@ -1098,22 +1103,36 @@ thread_db_post_startup_inferior (ptid_t } } -static void -thread_db_mourn_inferior (void) +int +thread_db_disconnect (void) { + if (!using_thread_db) + return 0; + /* Forget about the child's process ID. We shouldn't need it anymore. */ proc_handle.pid = 0; + /* Detach thread_db target ops. */ + unpush_target (&thread_db_ops); + using_thread_db = 0; + + return 1; +} + +static void +thread_db_mourn_inferior (void) +{ + int disconnected; + target_beneath->to_mourn_inferior (); /* Delete the old thread event breakpoints. Do this after mourning the inferior, so that we don't try to uninsert them. */ remove_thread_event_breakpoints (); - /* Detach thread_db target ops. */ - unpush_target (&thread_db_ops); - using_thread_db = 0; + disconnected = thread_db_disconnect (); + gdb_assert (disconnected != 0); } static int Index: testsuite/gdb.threads/thread-lost.c =================================================================== RCS file: testsuite/gdb.threads/thread-lost.c diff -N testsuite/gdb.threads/thread-lost.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.threads/thread-lost.c 14 Jun 2006 10:52:18 -0000 @@ -0,0 +1,36 @@ +/* A minimal test case linked with pthreads library. + + Copyright 2006 + Free Software Foundation, Inc. + + This file is part of GDB. + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include + +int main() +{ + pthread_t thr; + char *cmd_argv[] = { "/bin/echo", "EXECUTED", NULL }; + char *cmd_env[] = { NULL }; + + /* Just ensure we are linked with -lpthread. */ + thr = pthread_self (); + + return execve (cmd_argv[0], cmd_argv, cmd_env); +} Index: testsuite/gdb.threads/thread-lost.exp =================================================================== RCS file: testsuite/gdb.threads/thread-lost.exp diff -N testsuite/gdb.threads/thread-lost.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.threads/thread-lost.exp 14 Jun 2006 10:52:18 -0000 @@ -0,0 +1,88 @@ +# Copyright (C) 2003 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@prep.ai.mit.edu + +# This file was written by Jan Kratochvil . +# +# It tests regression of Red Hat Bug 78228, reported again as Bug 182116. +# On i386 with %gs based TLS NPTLS gdb calls glibc td_ta_map_lwp2thr() which +# calls ta_howto_reg_thread_area() (case ta_howto_reg_thread_area). +# After execve(2) it retrieves %gs as 0 and fails to ps_get_thread_area() +# as its idx must be 6 (glibc TLS descriptor) - value of the first/glibc Linux +# kernel GDT_ENTRY_TLS_MIN. +# It is now workarounded as to drop threading support if %gs==0. + +if $tracelevel then { + strace $tracelevel +} + +set testfile "thread-lost" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "incdir=${objdir}"]] != "" } { + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +gdb_run_cmd +gdb_expect { + -re "Original threaded process got lost, dropping threads" { + pass "run program to execve(2)" + } + -re "Cannot find user-level thread for LWP" { + fail "GDB did not cope with multithreaded execve(2)" + } + timeout { + fail "run program to execve(2) (timeout)" + } +} + +# Only if we would break the execution on: +# Original threaded process got lost, dropping threads +###send_gdb "continue\n"; +send_gdb ""; +gdb_expect { + -re "EXECUTED" { + pass "spawned child got executed" + } + -re "Cannot find user-level thread for LWP" { + fail "GDB did not cope with multithreaded execve(2)" + } + timeout { + fail "spawned child failed to be executed (timeout)" + } +} + +send_gdb "quit\n" +gdb_expect { + eof { + pass "GDB exits after spawned child finished" + } + -re "The program is running. Exit anyway\\? \\(y or n\\) $" { + send_gdb "y\n" + fail "GDB got stuck after spawned child" + } + timeout { + fail "GDB fails to exit after spawned child" + } +} --C7zPtVaVf+AK4Oqc Content-Type: text/plain; charset=us-ascii Content-Description: Minimized bug reproducibility test case Content-Disposition: attachment; filename="thread-lost.c" Content-length: 1142 /* A minimal test case linked with pthreads library. Copyright 2006 Free Software Foundation, Inc. This file is part of GDB. 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include int main() { pthread_t thr; char *cmd_argv[] = { "/bin/echo", "EXECUTED", NULL }; char *cmd_env[] = { NULL }; /* Just ensure we are linked with -lpthread. */ thr = pthread_self (); return execve (cmd_argv[0], cmd_argv, cmd_env); } --C7zPtVaVf+AK4Oqc--