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