1 /* Smoke testing GDB process attach with thread-local variable access.
2    Copyright (C) 2021-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* This test runs GDB against a forked copy of itself, to check
20    whether libthread_db can be loaded, and that access to thread-local
21    variables works.  */
22 
23 #include <elf.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <support/capture_subprocess.h>
31 #include <support/check.h>
32 #include <support/xptrace.h>
33 #include <support/subprocess.h>
34 #include <support/support.h>
35 #include <support/temp_file.h>
36 #include <support/test-driver.h>
37 #include <support/xstdio.h>
38 #include <support/xthread.h>
39 #include <support/xunistd.h>
40 #include <unistd.h>
41 
42 /* Starts out as zero, changed to 1 or 2 by the debugger, depending on
43    the thread.  */
44 __thread volatile int altered_by_debugger;
45 
46 /* Common prefix between 32-bit and 64-bit ELF.  */
47 struct elf_prefix
48 {
49   unsigned char e_ident[EI_NIDENT];
50   uint16_t e_type;
51   uint16_t e_machine;
52   uint32_t e_version;
53 };
54 _Static_assert (sizeof (struct elf_prefix) == EI_NIDENT + 8,
55                 "padding in struct elf_prefix");
56 
57 /* Reads the ELF header from PATH.  Returns true if the header can be
58    read, false if the file is too short.  */
59 static bool
read_elf_header(const char * path,struct elf_prefix * elf)60 read_elf_header (const char *path, struct elf_prefix *elf)
61 {
62   int fd = xopen (path, O_RDONLY, 0);
63   bool result = read (fd, elf, sizeof (*elf)) == sizeof (*elf);
64   xclose (fd);
65   return result;
66 }
67 
68 /* Searches for "gdb" alongside the path variable.  See execvpe.  */
69 static char *
find_gdb(void)70 find_gdb (void)
71 {
72   const char *path = getenv ("PATH");
73   if (path == NULL)
74     return NULL;
75   while (true)
76     {
77       const char *colon = strchrnul (path, ':');
78       char *candidate = xasprintf ("%.*s/gdb", (int) (colon - path), path);
79       if (access (candidate, X_OK) == 0)
80         return candidate;
81       free (candidate);
82       if (*colon == '\0')
83         break;
84       path = colon + 1;
85     }
86   return NULL;
87 }
88 
89 /* Writes the GDB script to run the test to PATH.  */
90 static void
write_gdbscript(const char * path,int tested_pid)91 write_gdbscript (const char *path, int tested_pid)
92 {
93   FILE *fp = xfopen (path, "w");
94   fprintf (fp,
95            "set trace-commands on\n"
96            "set debug libthread-db 1\n"
97 #if DO_ADD_SYMBOL_FILE
98            /* Do not do this unconditionally to work around a GDB
99               assertion failure: ../../gdb/symtab.c:6404:
100               internal-error: CORE_ADDR get_msymbol_address(objfile*,
101               const minimal_symbol*): Assertion `(objf->flags &
102               OBJF_MAINLINE) == 0' failed.  */
103            "add-symbol-file %1$s/nptl/tst-pthread-gdb-attach\n"
104 #endif
105            "set auto-load safe-path %1$s/nptl_db\n"
106            "set libthread-db-search-path %1$s/nptl_db\n"
107            "attach %2$d\n",
108            support_objdir_root, tested_pid);
109   fputs ("break debugger_inspection_point\n"
110          "continue\n"
111          "thread 1\n"
112          "print altered_by_debugger\n"
113          "print altered_by_debugger = 1\n"
114          "thread 2\n"
115          "print altered_by_debugger\n"
116          "print altered_by_debugger = 2\n"
117          "continue\n",
118          fp);
119   xfclose (fp);
120 }
121 
122 /* The test sets a breakpoint on this function and alters the
123    altered_by_debugger thread-local variable.  */
124 void __attribute__ ((weak))
debugger_inspection_point(void)125 debugger_inspection_point (void)
126 {
127 }
128 
129 /* Thread function for the test thread in the subprocess.  */
130 static void *
subprocess_thread(void * closure)131 subprocess_thread (void *closure)
132 {
133   /* Wait until altered_by_debugger changes the value away from 0.  */
134   while (altered_by_debugger == 0)
135     {
136       usleep (100 * 1000);
137       debugger_inspection_point ();
138     }
139 
140   TEST_COMPARE (altered_by_debugger, 2);
141   return NULL;
142 }
143 
144 /* This function implements the subprocess under test.  It creates a
145    second thread, waiting for its value to change to 2, and checks
146    that the main thread also changed its value to 1.  */
147 static void
in_subprocess(void * arg)148 in_subprocess (void *arg)
149 {
150   pthread_t thr = xpthread_create (NULL, subprocess_thread, NULL);
151   TEST_VERIFY (xpthread_join (thr) == NULL);
152   TEST_COMPARE (altered_by_debugger, 1);
153   _exit (0);
154 }
155 
156 static void
gdb_process(const char * gdb_path,const char * gdbscript,pid_t * tested_pid)157 gdb_process (const char *gdb_path, const char *gdbscript, pid_t *tested_pid)
158 {
159   /* Create a copy of current test to check with gdb.  As the
160      target_process is a child of this gdb_process, gdb is also able
161      to attach to target_process if YAMA is configured to 1 =
162      "restricted ptrace".  */
163   struct support_subprocess target = support_subprocess (in_subprocess, NULL);
164 
165   write_gdbscript (gdbscript, target.pid);
166   *tested_pid = target.pid;
167 
168   xdup2 (STDOUT_FILENO, STDERR_FILENO);
169   execl (gdb_path, "gdb", "-nx", "-batch", "-x", gdbscript, NULL);
170   if (errno == ENOENT)
171     _exit (EXIT_UNSUPPORTED);
172   else
173     _exit (1);
174 }
175 
176 static int
do_test(void)177 do_test (void)
178 {
179   char *gdb_path = find_gdb ();
180   if (gdb_path == NULL)
181     FAIL_UNSUPPORTED ("gdb command not found in PATH: %s", getenv ("PATH"));
182 
183   /* Check that libthread_db is compatible with the gdb architecture
184      because gdb loads it via dlopen.  */
185   {
186     char *threaddb_path = xasprintf ("%s/nptl_db/libthread_db.so",
187                                      support_objdir_root);
188     struct elf_prefix elf_threaddb;
189     TEST_VERIFY_EXIT (read_elf_header (threaddb_path, &elf_threaddb));
190     struct elf_prefix elf_gdb;
191     /* If the ELF header cannot be read or "gdb" is not an ELF file,
192        assume this is a wrapper script that can run.  */
193     if (read_elf_header (gdb_path, &elf_gdb)
194         && memcmp (&elf_gdb, ELFMAG, SELFMAG) == 0)
195       {
196         if (elf_gdb.e_ident[EI_CLASS] != elf_threaddb.e_ident[EI_CLASS])
197           FAIL_UNSUPPORTED ("GDB at %s has wrong class", gdb_path);
198         if (elf_gdb.e_ident[EI_DATA] != elf_threaddb.e_ident[EI_DATA])
199           FAIL_UNSUPPORTED ("GDB at %s has wrong data", gdb_path);
200         if (elf_gdb.e_machine != elf_threaddb.e_machine)
201           FAIL_UNSUPPORTED ("GDB at %s has wrong machine", gdb_path);
202       }
203     free (threaddb_path);
204   }
205 
206   /* Check if our subprocess can be debugged with ptrace.  */
207   {
208     int ptrace_scope = support_ptrace_scope ();
209     if (ptrace_scope >= 2)
210       FAIL_UNSUPPORTED ("/proc/sys/kernel/yama/ptrace_scope >= 2");
211   }
212 
213   char *gdbscript;
214   xclose (create_temp_file ("tst-pthread-gdb-attach-", &gdbscript));
215 
216   /* Run 'gdb' on test subprocess which will be created in gdb_process.
217      The pid of the subprocess will be written to 'tested_pid'.  */
218   pid_t *tested_pid = support_shared_allocate (sizeof (pid_t));
219 
220   pid_t gdb_pid = xfork ();
221   if (gdb_pid == 0)
222     gdb_process (gdb_path, gdbscript, tested_pid);
223 
224   int status;
225   TEST_COMPARE (xwaitpid (gdb_pid, &status, 0), gdb_pid);
226   if (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_UNSUPPORTED)
227     /* gdb is not installed.  */
228     return EXIT_UNSUPPORTED;
229   TEST_COMPARE (status, 0);
230 
231   kill (*tested_pid, SIGKILL);
232 
233   support_shared_free (tested_pid);
234   free (gdbscript);
235   free (gdb_path);
236   return 0;
237 }
238 
239 #include <support/test-driver.c>
240