1 /* Process tracing interface `ptrace' for GNU Hurd.
2    Copyright (C) 1991-2021 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 #include <errno.h>
20 #include <sys/ptrace.h>
21 #include <sys/types.h>
22 #include <stdarg.h>
23 #include <hurd.h>
24 #include <hurd/signal.h>
25 #include <hurd/msg.h>
26 #include <thread_state.h>
27 
28 /* Perform process tracing functions.  REQUEST is one of the values
29    in <sys/ptrace.h>, and determines the action to be taken.
30    For all requests except PTRACE_TRACEME, PID specifies the process to be
31    traced.
32 
33    PID and the other arguments described above for the various requests should
34    appear (those that are used for the particular request) as:
35      pid_t PID, void *ADDR, int DATA, void *ADDR2
36    after PID.  */
37 int
ptrace(enum __ptrace_request request,...)38 ptrace (enum __ptrace_request request, ... )
39 {
40   pid_t pid;
41   void *addr, *addr2;
42   natural_t data;
43   va_list ap;
44 
45   /* Read data from PID's address space, from ADDR for DATA bytes.  */
46   error_t read_data (task_t task, vm_address_t *ourpage, vm_size_t *size)
47     {
48       /* Read the pages containing the addressed range.  */
49       error_t err;
50       *size = round_page (addr + data) - trunc_page (addr);
51       err = __vm_read (task, trunc_page (addr), *size, ourpage, size);
52       return err;
53     }
54 
55   /* Fetch the thread port for PID's user thread.  */
56   error_t fetch_user_thread (task_t task, thread_t *thread)
57     {
58       thread_t threadbuf[3], *threads = threadbuf;
59       mach_msg_type_number_t nthreads = 3, i;
60       error_t err = __task_threads (task, &threads, &nthreads);
61       if (err)
62 	return err;
63       if (nthreads == 0)
64 	return EINVAL;
65       *thread = threads[0];	/* Assume user thread is first.  */
66       for (i = 1; i < nthreads; ++i)
67 	__mach_port_deallocate (__mach_task_self (), threads[i]);
68       if (threads != threadbuf)
69 	__vm_deallocate (__mach_task_self (),
70 			 (vm_address_t) threads, nthreads * sizeof threads[0]);
71       return 0;
72     }
73 
74   /* Fetch a thread state structure from PID and store it at ADDR.  */
75   int get_regs (int flavor, mach_msg_type_number_t count)
76     {
77       error_t err;
78       task_t task = __pid2task (pid);
79       thread_t thread;
80       if (task == MACH_PORT_NULL)
81 	return -1;
82       err = fetch_user_thread (task, &thread);
83       __mach_port_deallocate (__mach_task_self (), task);
84       if (!err)
85 	err = __thread_get_state (thread, flavor, addr, &count);
86       __mach_port_deallocate (__mach_task_self (), thread);
87       return err ? __hurd_fail (err) : 0;
88     }
89 
90 
91   switch (request)
92     {
93     case PTRACE_TRACEME:
94       /* Make this process be traced.  */
95       __sigfillset (&_hurdsig_traced);
96       __USEPORT (PROC, __proc_mark_traced (port));
97       break;
98 
99     case PTRACE_CONT:
100       va_start (ap, request);
101       pid = va_arg (ap, pid_t);
102       addr = va_arg (ap, void *);
103       data = va_arg (ap, int);
104       va_end (ap);
105       {
106 	/* Send a DATA signal to PID, telling it to take the signal
107 	   normally even if it's traced.  */
108 	error_t err;
109 	task_t task = __pid2task (pid);
110 	if (task == MACH_PORT_NULL)
111 	  return -1;
112 	if (data == SIGKILL)
113 	  err = __task_terminate (task);
114 	else
115 	  {
116 	    if (addr != (void *) 1)
117 	      {
118 		/* Move the user thread's PC to ADDR.  */
119 		thread_t thread;
120 		err = fetch_user_thread (task, &thread);
121 		if (!err)
122 		  {
123 		    struct machine_thread_state state;
124 		    mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
125 		    err = __thread_get_state (thread,
126 					      MACHINE_THREAD_STATE_FLAVOR,
127 					      (natural_t *) &state, &count);
128 		    if (!err)
129 		      {
130 			MACHINE_THREAD_STATE_SET_PC (&state, addr);
131 			err = __thread_set_state (thread,
132 						  MACHINE_THREAD_STATE_FLAVOR,
133 						  (natural_t *) &state, count);
134 		      }
135 
136 		  }
137 		__mach_port_deallocate (__mach_task_self (), thread);
138 	      }
139 	    else
140 	      err = 0;
141 
142 	    if (! err)
143 	      /* Tell the process to take the signal (or just resume if 0).  */
144 	      err = HURD_MSGPORT_RPC
145 		(__USEPORT (PROC, __proc_getmsgport (port, pid, &msgport)),
146 		 0, 0, __msg_sig_post_untraced (msgport, data, 0, task));
147 	  }
148 	__mach_port_deallocate (__mach_task_self (), task);
149 	return err ? __hurd_fail (err) : 0;
150       }
151 
152     case PTRACE_KILL:
153       va_start (ap, request);
154       pid = va_arg (ap, pid_t);
155       va_end (ap);
156       /* SIGKILL always just terminates the task,
157 	 so normal kill is just the same when traced.  */
158       return __kill (pid, SIGKILL);
159 
160     case PTRACE_SINGLESTEP:
161       /* This is a machine-dependent kernel RPC on
162 	 machines that support it.  Punt.  */
163       return __hurd_fail (EOPNOTSUPP);
164 
165     case PTRACE_ATTACH:
166     case PTRACE_DETACH:
167       va_start (ap, request);
168       pid = va_arg (ap, pid_t);
169       va_end (ap);
170       {
171 	/* Tell PID to set or clear its trace bit.  */
172 	error_t err;
173 	mach_port_t msgport;
174 	task_t task = __pid2task (pid);
175 	if (task == MACH_PORT_NULL)
176 	  return -1;
177 	err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
178 	if (! err)
179 	  {
180 	    err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
181 				      request == PTRACE_DETACH ? 0
182 				      : ~(sigset_t) 0);
183 	    if (! err)
184 	      {
185 		if (request == PTRACE_ATTACH)
186 		  /* Now stop the process.  */
187 		  err = __msg_sig_post (msgport, SIGSTOP, 0, task);
188 		else
189 		  /* Resume the process from tracing stop.  */
190 		  err = __msg_sig_post_untraced (msgport, 0, 0, task);
191 	      }
192 	    __mach_port_deallocate (__mach_task_self (), msgport);
193 	  }
194 	__mach_port_deallocate (__mach_task_self (), task);
195 	return err ? __hurd_fail (err) : 0;
196       }
197 
198     case PTRACE_PEEKTEXT:
199     case PTRACE_PEEKDATA:
200       va_start (ap, request);
201       pid = va_arg (ap, pid_t);
202       addr = va_arg (ap, void *);
203       va_end (ap);
204       {
205 	/* Read the page (or two pages, if the word lies on a boundary)
206 	   containing the addressed word.  */
207 	error_t err;
208 	vm_address_t ourpage;
209 	vm_size_t size;
210 	natural_t word;
211 	task_t task = __pid2task (pid);
212 	if (task == MACH_PORT_NULL)
213 	  return -1;
214 	data = sizeof word;
215 	ourpage = 0;
216 	size = 0;
217 	err = read_data (task, &ourpage, &size);
218 	__mach_port_deallocate (__mach_task_self (), task);
219 	if (err)
220 	  return __hurd_fail (err);
221 	word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
222 			       + ourpage);
223 	__vm_deallocate (__mach_task_self (), ourpage, size);
224 	return word;
225       }
226 
227     case PTRACE_PEEKUSER:
228     case PTRACE_POKEUSER:
229       /* U area, what's that?  */
230       return __hurd_fail (EOPNOTSUPP);
231 
232     case PTRACE_GETREGS:
233     case PTRACE_SETREGS:
234       va_start (ap, request);
235       pid = va_arg (ap, pid_t);
236       addr = va_arg (ap, void *);
237       va_end (ap);
238       return get_regs (MACHINE_THREAD_STATE_FLAVOR,
239 		       MACHINE_THREAD_STATE_COUNT);
240 
241     case PTRACE_GETFPREGS:
242     case PTRACE_SETFPREGS:
243       va_start (ap, request);
244       pid = va_arg (ap, pid_t);
245       addr = va_arg (ap, void *);
246       va_end (ap);
247 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
248       return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
249 		       MACHINE_THREAD_FLOAT_STATE_COUNT);
250 #else
251       return __hurd_fail (EOPNOTSUPP);
252 #endif
253 
254     case PTRACE_GETFPAREGS:
255     case PTRACE_SETFPAREGS:
256       va_start (ap, request);
257       pid = va_arg (ap, pid_t);
258       addr = va_arg (ap, void *);
259       va_end (ap);
260 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
261       return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
262 		       MACHINE_THREAD_FPA_STATE_COUNT);
263 #else
264       return __hurd_fail (EOPNOTSUPP);
265 #endif
266 
267     case PTRACE_POKETEXT:
268     case PTRACE_POKEDATA:
269       va_start (ap, request);
270       pid = va_arg (ap, pid_t);
271       addr = va_arg (ap, void *);
272       data = va_arg (ap, int);
273       va_end (ap);
274       {
275 	/* Read the page (or two pages, if the word lies on a boundary)
276 	   containing the addressed word.  */
277 	error_t err;
278 	vm_address_t ourpage;
279 	vm_size_t size;
280 	task_t task = __pid2task (pid);
281 	if (task == MACH_PORT_NULL)
282 	  return -1;
283 	data = sizeof (natural_t);
284 	ourpage = 0;
285 	size = 0;
286 	err = read_data (task, &ourpage, &size);
287 
288 	if (!err)
289 	  {
290 	    /* Now modify the specified word and write the page back.  */
291 	    *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
292 			    + ourpage) = data;
293 	    err = __vm_write (task, trunc_page (addr), ourpage, size);
294 	    __vm_deallocate (__mach_task_self (), ourpage, size);
295 	  }
296 
297 	__mach_port_deallocate (__mach_task_self (), task);
298 	return err ? __hurd_fail (err) : 0;
299       }
300 
301     case PTRACE_READDATA:
302     case PTRACE_READTEXT:
303       va_start (ap, request);
304       pid = va_arg (ap, pid_t);
305       addr = va_arg (ap, void *);
306       data = va_arg (ap, int);
307       addr2 = va_arg (ap, void *);
308       va_end (ap);
309       {
310 	error_t err;
311 	vm_address_t ourpage;
312 	vm_size_t size;
313 	task_t task = __pid2task (pid);
314 	if (task == MACH_PORT_NULL)
315 	  return -1;
316 	if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
317 	  {
318 	    /* Perhaps we can write directly to the user's buffer.  */
319 	    ourpage = (vm_address_t) addr2;
320 	    size = data;
321 	  }
322 	else
323 	  {
324 	    ourpage = 0;
325 	    size = 0;
326 	  }
327 	err = read_data (task, &ourpage, &size);
328 	__mach_port_deallocate (__mach_task_self (), task);
329 	if (!err && ourpage != (vm_address_t) addr2)
330 	  {
331 	    memcpy (addr2, (void *) ourpage, data);
332 	    __vm_deallocate (__mach_task_self (), ourpage, size);
333 	  }
334 	return err ? __hurd_fail (err) : 0;
335       }
336 
337     case PTRACE_WRITEDATA:
338     case PTRACE_WRITETEXT:
339       va_start (ap, request);
340       pid = va_arg (ap, pid_t);
341       addr = va_arg (ap, void *);
342       data = va_arg (ap, int);
343       addr2 = va_arg (ap, void *);
344       va_end (ap);
345       {
346 	error_t err;
347 	vm_address_t ourpage;
348 	vm_size_t size;
349 	task_t task = __pid2task (pid);
350 	if (task == MACH_PORT_NULL)
351 	  return -1;
352 	if ((vm_address_t) addr % __vm_page_size == 0
353 	    && (vm_address_t) data % __vm_page_size == 0)
354 	  {
355 	    /* Writing whole pages; can go directly from the user's buffer.  */
356 	    ourpage = (vm_address_t) addr2;
357 	    size = data;
358 	    err = 0;
359 	  }
360 	else
361 	  {
362 	    /* Read the task's pages and modify our own copy.  */
363 	    ourpage = 0;
364 	    size = 0;
365 	    err = read_data (task, &ourpage, &size);
366 	    if (!err)
367 	      memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
368 				+ ourpage),
369 		      addr2,
370 		      data);
371 	  }
372 	if (!err)
373 	  /* Write back the modified pages.  */
374 	  err = __vm_write (task, trunc_page (addr), ourpage, size);
375 	__mach_port_deallocate (__mach_task_self (), task);
376 	return err ? __hurd_fail (err) : 0;
377       }
378 
379     default:
380       errno = EINVAL;
381       return -1;
382     }
383 
384   return 0;
385 }
386