(root)/
glibc-2.38/
hurd/
hurdexec.c
       1  /* Copyright (C) 1991-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <unistd.h>
      20  #include <fcntl.h>
      21  #include <limits.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <hurd.h>
      25  #include <hurd/fd.h>
      26  #include <hurd/signal.h>
      27  #include <hurd/id.h>
      28  #include <assert.h>
      29  #include <argz.h>
      30  
      31  /* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
      32     If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
      33     ARGV and ENVP are terminated by NULL pointers.
      34     Deprecated: use _hurd_exec_paths instead.  */
      35  error_t
      36  _hurd_exec (task_t task, file_t file,
      37  	    char *const argv[], char *const envp[])
      38  {
      39    return _hurd_exec_paths (task, file, NULL, NULL, argv, envp);
      40  }
      41  
      42  link_warning (_hurd_exec,
      43  	      "_hurd_exec is deprecated, use _hurd_exec_paths instead");
      44  
      45  /* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
      46     If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
      47     ARGV and ENVP are terminated by NULL pointers.  PATH is the relative path to
      48     FILE and ABSPATH is the absolute path to FILE. Passing NULL, though possible,
      49     should be avoided, since then the exec server may not know the path to
      50     FILE if FILE is a script, and will then pass /dev/fd/N to the
      51     interpreter.  */
      52  error_t
      53  _hurd_exec_paths (task_t task, file_t file,
      54  		   const char *path, const char *abspath,
      55  		   char *const argv[], char *const envp[])
      56  {
      57    error_t err;
      58    char *args, *env;
      59    size_t argslen, envlen;
      60    int ints[INIT_INT_MAX];
      61    mach_port_t ports[_hurd_nports];
      62    struct hurd_userlink ulink_ports[_hurd_nports];
      63    inline void free_port (unsigned int i)
      64      {
      65        _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
      66      }
      67    file_t *dtable;
      68    unsigned int dtablesize, i, j;
      69    struct hurd_port **dtable_cells;
      70    struct hurd_userlink *ulink_dtable;
      71    struct hurd_sigstate *ss;
      72    mach_port_t *please_dealloc, *pdp;
      73    int reauth = 0;
      74    mach_port_t *portnames = NULL;
      75    mach_msg_type_number_t nportnames = 0;
      76    mach_port_type_t *porttypes = NULL;
      77    mach_msg_type_number_t nporttypes = 0;
      78  
      79    /* XXX needs to be hurdmalloc XXX */
      80    if (argv == NULL)
      81      args = NULL, argslen = 0;
      82    else if (err = __argz_create (argv, &args, &argslen))
      83      return err;
      84    if (envp == NULL)
      85      env = NULL, envlen = 0;
      86    else if (err = __argz_create (envp, &env, &envlen))
      87      goto outargs;
      88  
      89    /* Load up the ports to give to the new program.  */
      90    for (i = 0; i < _hurd_nports; ++i)
      91      if (i == INIT_PORT_PROC && task != __mach_task_self ())
      92        {
      93  	/* This is another task, so we need to ask the proc server
      94  	   for the right proc server port for it.  */
      95  	if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i])))
      96  	  {
      97  	    while (--i > 0)
      98  	      free_port (i);
      99  	    goto outenv;
     100  	  }
     101        }
     102      else
     103        ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]);
     104  
     105  
     106    /* Load up the ints to give the new program.  */
     107    for (i = 0; i < INIT_INT_MAX; ++i)
     108      switch (i)
     109        {
     110        case INIT_UMASK:
     111  	ints[i] = _hurd_umask;
     112  	break;
     113  
     114        case INIT_SIGMASK:
     115        case INIT_SIGIGN:
     116        case INIT_SIGPENDING:
     117  	/* We will set these all below.  */
     118  	break;
     119  
     120        case INIT_TRACEMASK:
     121  	ints[i] = _hurdsig_traced;
     122  	break;
     123  
     124        default:
     125  	ints[i] = 0;
     126        }
     127  
     128    ss = _hurd_self_sigstate ();
     129  
     130  retry:
     131    assert (! __spin_lock_locked (&ss->critical_section_lock));
     132    __spin_lock (&ss->critical_section_lock);
     133  
     134    _hurd_sigstate_lock (ss);
     135    struct sigaction *actions = _hurd_sigstate_actions (ss);
     136    ints[INIT_SIGMASK] = ss->blocked;
     137    ints[INIT_SIGPENDING] = _hurd_sigstate_pending (ss);
     138    ints[INIT_SIGIGN] = 0;
     139    for (i = 1; i < NSIG; ++i)
     140      if (actions[i].sa_handler == SIG_IGN)
     141        ints[INIT_SIGIGN] |= __sigmask (i);
     142  
     143    /* We hold the sigstate lock until the exec has failed so that no signal
     144       can arrive between when we pack the blocked and ignored signals, and
     145       when the exec actually happens.  A signal handler could change what
     146       signals are blocked and ignored.  Either the change will be reflected
     147       in the exec, or the signal will never be delivered.  Setting the
     148       critical section flag avoids anything we call trying to acquire the
     149       sigstate lock.  */
     150  
     151    _hurd_sigstate_unlock (ss);
     152  
     153    /* Pack up the descriptor table to give the new program.  */
     154    __mutex_lock (&_hurd_dtable_lock);
     155  
     156    dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize;
     157  
     158    if (task == __mach_task_self ())
     159      /* Request the exec server to deallocate some ports from us if the exec
     160         succeeds.  The init ports and descriptor ports will arrive in the
     161         new program's exec_startup message.  If we failed to deallocate
     162         them, the new program would have duplicate user references for them.
     163         But we cannot deallocate them ourselves, because we must still have
     164         them after a failed exec call.  */
     165      please_dealloc = __alloca ((_hurd_nports + 3 + (3 * dtablesize))
     166  				* sizeof (mach_port_t));
     167    else
     168      please_dealloc = NULL;
     169    pdp = please_dealloc;
     170  
     171    if (_hurd_dtable != NULL)
     172      {
     173        dtable = __alloca (dtablesize * sizeof (dtable[0]));
     174        ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
     175        dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
     176        for (i = 0; i < dtablesize; ++i)
     177  	{
     178  	  struct hurd_fd *const d = _hurd_dtable[i];
     179  	  if (d == NULL)
     180  	    {
     181  	      dtable[i] = MACH_PORT_NULL;
     182  	      continue;
     183  	    }
     184  	  __spin_lock (&d->port.lock);
     185  	  if (d->flags & FD_CLOEXEC)
     186  	    {
     187  	      /* This descriptor is marked to be closed on exec.
     188  		 So don't pass it to the new program.  */
     189  	      dtable[i] = MACH_PORT_NULL;
     190  	      if (pdp && d->port.port != MACH_PORT_NULL)
     191  		{
     192  		  /* We still need to deallocate the ports.  */
     193  		  *pdp++ = d->port.port;
     194  		  if (d->ctty.port != MACH_PORT_NULL)
     195  		    *pdp++ = d->ctty.port;
     196  		}
     197  	      __spin_unlock (&d->port.lock);
     198  	    }
     199  	  else
     200  	    {
     201  	      if (pdp && d->ctty.port != MACH_PORT_NULL)
     202  		/* All the elements of DTABLE are added to PLEASE_DEALLOC
     203  		   below, so we needn't add the port itself.
     204  		   But we must deallocate the ctty port as well as
     205  		   the normal port that got installed in DTABLE[I].  */
     206  		*pdp++ = d->ctty.port;
     207  	      dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]);
     208  	      dtable_cells[i] = &d->port;
     209  	    }
     210  	}
     211      }
     212    else
     213      {
     214        dtable = _hurd_init_dtable;
     215        ulink_dtable = NULL;
     216        dtable_cells = NULL;
     217      }
     218  
     219    /* Prune trailing null ports from the descriptor table.  */
     220    while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL)
     221      --dtablesize;
     222  
     223    /* See if we need to diddle the auth port of the new program.
     224       The purpose of this is to get the effect setting the saved-set UID and
     225       GID to the respective effective IDs after the exec, as POSIX.1 requires.
     226       Note that we don't reauthenticate with the proc server; that would be a
     227       no-op since it only keeps track of the effective UIDs, and if it did
     228       keep track of the available IDs we would have the problem that we'd be
     229       changing the IDs before the exec and have to change them back after a
     230       failure.  Arguably we could skip all the reauthentications because the
     231       available IDs have no bearing on any filesystem.  But the conservative
     232       approach is to reauthenticate all the io ports so that no state anywhere
     233       reflects that our whole ID set differs from what we've set it to.  */
     234    __mutex_lock (&_hurd_id.lock);
     235    err = _hurd_check_ids ();
     236  
     237    /* Avoid leaking the rid_auth port reference to the new program */
     238    if (_hurd_id.rid_auth != MACH_PORT_NULL)
     239      {
     240        __mach_port_deallocate (__mach_task_self (), _hurd_id.rid_auth);
     241        _hurd_id.rid_auth = MACH_PORT_NULL;
     242      }
     243  
     244    if (err == 0 && ((_hurd_id.aux.nuids >= 2 && _hurd_id.gen.nuids >= 1
     245  		    && _hurd_id.aux.uids[1] != _hurd_id.gen.uids[0])
     246  		   || (_hurd_id.aux.ngids >= 2 && _hurd_id.gen.ngids >= 1
     247  		       && _hurd_id.aux.gids[1] != _hurd_id.gen.gids[0])))
     248      {
     249        /* We have euid != svuid or egid != svgid.  POSIX.1 says that exec
     250  	 sets svuid = euid and svgid = egid.  So we must get a new auth
     251  	 port and reauthenticate everything with it.  We'll pass the new
     252  	 ports in file_exec_paths instead of our own ports.  */
     253  
     254        auth_t newauth;
     255  
     256        _hurd_id.aux.uids[1] = _hurd_id.gen.uids[0];
     257        _hurd_id.aux.gids[1] = _hurd_id.gen.gids[0];
     258        _hurd_id.valid = 0;
     259  
     260        err = __auth_makeauth (ports[INIT_PORT_AUTH],
     261  			     NULL, MACH_MSG_TYPE_COPY_SEND, 0,
     262  			     _hurd_id.gen.uids, _hurd_id.gen.nuids,
     263  			     _hurd_id.aux.uids, _hurd_id.aux.nuids,
     264  			     _hurd_id.gen.gids, _hurd_id.gen.ngids,
     265  			     _hurd_id.aux.gids, _hurd_id.aux.ngids,
     266  			     &newauth);
     267        if (err == 0)
     268  	{
     269  	  /* Now we have to reauthenticate the ports with this new ID.
     270  	   */
     271  
     272  	  inline error_t reauth_io (io_t port, io_t *newport)
     273  	    {
     274  	      mach_port_t ref = __mach_reply_port ();
     275  	      *newport = MACH_PORT_NULL;
     276  	      error_t err = __io_reauthenticate (port,
     277  						 ref, MACH_MSG_TYPE_MAKE_SEND);
     278  	      if (!err)
     279  		err = __auth_user_authenticate (newauth,
     280  						ref, MACH_MSG_TYPE_MAKE_SEND,
     281  						newport);
     282  	      __mach_port_destroy (__mach_task_self (), ref);
     283  	      return err;
     284  	    }
     285  	  inline void reauth_port (unsigned int idx)
     286  	    {
     287  	      io_t newport;
     288  	      err = reauth_io (ports[idx], &newport) ?: err;
     289  	      if (pdp)
     290  		*pdp++ = ports[idx]; /* XXX presumed still in _hurd_ports */
     291  	      free_port (idx);
     292  	      ports[idx] = newport;
     293  	    }
     294  
     295  	  if (pdp)
     296  	    *pdp++ = ports[INIT_PORT_AUTH];
     297  	  free_port (INIT_PORT_AUTH);
     298  	  ports[INIT_PORT_AUTH] = newauth;
     299  
     300  	  reauth_port (INIT_PORT_CRDIR);
     301  	  reauth_port (INIT_PORT_CWDIR);
     302  
     303  	  if (!err)
     304  	    {
     305  	      /* Now we'll reauthenticate each file descriptor.  */
     306  	      if (ulink_dtable == NULL)
     307  		{
     308  		  assert (dtable == _hurd_init_dtable);
     309  		  dtable = __alloca (dtablesize * sizeof (dtable[0]));
     310  		  for (i = 0; i < dtablesize; ++i)
     311  		    if (_hurd_init_dtable[i] != MACH_PORT_NULL)
     312  		      {
     313  			if (pdp)
     314  			  *pdp++ = _hurd_init_dtable[i];
     315  			err = reauth_io (_hurd_init_dtable[i], &dtable[i]);
     316  			if (err)
     317  			  {
     318  			    while (++i < dtablesize)
     319  			      dtable[i] = MACH_PORT_NULL;
     320  			    break;
     321  			  }
     322  		      }
     323  		    else
     324  		      dtable[i] = MACH_PORT_NULL;
     325  		}
     326  	      else
     327  		{
     328  		  if (pdp)
     329  		    {
     330  		      /* Ask to deallocate all the old fd ports,
     331  			 since we will have new ones in DTABLE.  */
     332  		      memcpy (pdp, dtable, dtablesize * sizeof pdp[0]);
     333  		      pdp += dtablesize;
     334  		    }
     335  		  for (i = 0; i < dtablesize; ++i)
     336  		    if (dtable[i] != MACH_PORT_NULL)
     337  		      {
     338  			io_t newport;
     339  			err = reauth_io (dtable[i], &newport);
     340  			_hurd_port_free (dtable_cells[i], &ulink_dtable[i],
     341  					 dtable[i]);
     342  			dtable[i] = newport;
     343  			if (err)
     344  			  {
     345  			    while (++i < dtablesize)
     346  			      _hurd_port_free (dtable_cells[i],
     347  					       &ulink_dtable[i], dtable[i]);
     348  			    break;
     349  			  }
     350  		      }
     351  		  ulink_dtable = NULL;
     352  		  dtable_cells = NULL;
     353  		}
     354  	    }
     355  	}
     356  
     357        reauth = 1;
     358      }
     359    __mutex_unlock (&_hurd_id.lock);
     360  
     361    /* The information is all set up now.  Try to exec the file.  */
     362    if (!err)
     363      {
     364        int flags;
     365  
     366        if (pdp)
     367  	{
     368  	  /* Get all ports that we may not know about and we should thus destroy.  */
     369  	  /* XXX need to disable other threads to be safe.  */
     370  	  if (err = __mach_port_names (__mach_task_self (),
     371  				     &portnames, &nportnames,
     372  				     &porttypes, &nporttypes))
     373  	    return err;
     374  	  if (nportnames != nporttypes)
     375  	    return EGRATUITOUS;
     376  
     377  	  /* Request the exec server to deallocate some ports from us if
     378  	     the exec succeeds.  The init ports and descriptor ports will
     379  	     arrive in the new program's exec_startup message.  If we
     380  	     failed to deallocate them, the new program would have
     381  	     duplicate user references for them.  But we cannot deallocate
     382  	     them ourselves, because we must still have them after a failed
     383  	     exec call.  */
     384  
     385  	  for (i = 0; i < _hurd_nports; ++i)
     386  	    if (ports[i] != MACH_PORT_NULL)
     387  	      {
     388  		*pdp++ = ports[i];
     389  		for (j = 0; j < nportnames; j++)
     390  		  if (portnames[j] == ports[i])
     391  		    portnames[j] = MACH_PORT_NULL;
     392  	      }
     393  	  for (i = 0; i < dtablesize; ++i)
     394  	    if (dtable[i] != MACH_PORT_NULL)
     395  	      {
     396  		*pdp++ = dtable[i];
     397  		for (j = 0; j < nportnames; j++)
     398  		  if (portnames[j] == dtable[i])
     399  		    portnames[j] = MACH_PORT_NULL;
     400  	      }
     401  
     402  	  /* Pack ports to be destroyed together.  */
     403  	  for (i = 0, j = 0; i < nportnames; i++)
     404  	    {
     405  	      if (portnames[i] == MACH_PORT_NULL)
     406  		continue;
     407  	      if (j != i)
     408  		portnames[j] = portnames[i];
     409  	      j++;
     410  	    }
     411  	  nportnames = j;
     412  	}
     413  
     414        flags = 0;
     415  #ifdef EXEC_SIGTRAP
     416        /* PTRACE_TRACEME sets all bits in _hurdsig_traced, which is
     417  	 propagated through exec by INIT_TRACEMASK, so this checks if
     418  	 PTRACE_TRACEME has been called in this process in any of its
     419  	 current or prior lives.  */
     420        if (__sigismember (&_hurdsig_traced, SIGKILL))
     421  	flags |= EXEC_SIGTRAP;
     422  #endif
     423        err = __file_exec_paths (file, task, flags,
     424  			       path ? path : "",
     425  			       abspath ? abspath : "",
     426  			       args, argslen, env, envlen,
     427  			       dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
     428  			       ports, MACH_MSG_TYPE_COPY_SEND,
     429  			       _hurd_nports,
     430  			       ints, INIT_INT_MAX,
     431  			       please_dealloc, pdp - please_dealloc,
     432  			       portnames, nportnames);
     433        /* Fall back for backwards compatibility.  This can just be removed
     434           when __file_exec goes away.  */
     435        if (err == MIG_BAD_ID)
     436  	err = __file_exec (file, task, flags,
     437  			   args, argslen, env, envlen,
     438  			   dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
     439  			   ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
     440  			   ints, INIT_INT_MAX,
     441  			   please_dealloc, pdp - please_dealloc,
     442  			   portnames, nportnames);
     443      }
     444  
     445    /* Release references to the standard ports.  */
     446    for (i = 0; i < _hurd_nports; ++i)
     447      if ((i == INIT_PORT_PROC && task != __mach_task_self ())
     448  	|| (reauth && (i == INIT_PORT_AUTH
     449  		       || i == INIT_PORT_CRDIR || i == INIT_PORT_CWDIR)))
     450        __mach_port_deallocate (__mach_task_self (), ports[i]);
     451      else
     452        free_port (i);
     453  
     454    /* Release references to the file descriptor ports.  */
     455    if (ulink_dtable != NULL)
     456      {
     457        for (i = 0; i < dtablesize; ++i)
     458  	if (dtable[i] != MACH_PORT_NULL)
     459  	  _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
     460      }
     461    else if (dtable && dtable != _hurd_init_dtable)
     462      for (i = 0; i < dtablesize; ++i)
     463        __mach_port_deallocate (__mach_task_self (), dtable[i]);
     464  
     465    /* Release lock on the file descriptor table. */
     466    __mutex_unlock (&_hurd_dtable_lock);
     467  
     468    /* Safe to let signals happen now.  */
     469    _hurd_critical_section_unlock (ss);
     470    if (err == EINTR)
     471      /* Got a signal while inside an RPC of the critical section, retry again */
     472      goto retry;
     473  
     474   outenv:
     475    free (env);
     476   outargs:
     477    free (args);
     478    return err;
     479  }
     480  libc_hidden_def (_hurd_exec_paths)