glibc/hurd/hurdmsg.c
Sergey Bugaev 89f1e04174 hurd: Respect existing FD_CLOEXEC in S_msg_set_fd
If the process has set the close-on-exec flag for the file descriptor,
it expects the file descriptor to get closed on exec, even if we replace
what the file descriptor refers to.

Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
Message-Id: <20230429131354.2507443-6-bugaevc@gmail.com>
2023-04-29 16:57:04 +02:00

502 lines
12 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (C) 1992-2023 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <hurd.h>
#include <hurd/msg_server.h>
#include <hurd/fd.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <argz.h>
#define AUTHCHECK \
if (auth != mach_task_self () && ! __USEPORT (AUTH, port == auth)) \
return EPERM
/* Snarfing and frobbing the init ports. */
kern_return_t
_S_msg_get_init_port (mach_port_t msgport, mach_port_t auth, int which,
mach_port_t *result, mach_msg_type_name_t *result_type)
{
error_t err;
AUTHCHECK;
*result_type = MACH_MSG_TYPE_MOVE_SEND;
/* This function adds a new user reference for the *RESULT it gives back.
Our reply message uses a move-send right that consumes this reference. */
err = _hurd_ports_get (which, result);
if (!err && MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return err;
}
kern_return_t
_S_msg_set_init_port (mach_port_t msgport, mach_port_t auth,
int which, mach_port_t port)
{
error_t err;
AUTHCHECK;
err = _hurd_ports_set (which, port);
if (!err && MACH_PORT_VALID (port))
__mach_port_deallocate (__mach_task_self (), port);
if (!err && MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return err;
}
kern_return_t
_S_msg_get_init_ports (mach_port_t msgport, mach_port_t auth,
mach_port_t **ports,
mach_msg_type_name_t *ports_type,
mach_msg_type_number_t *nports)
{
mach_msg_type_number_t i;
error_t err;
AUTHCHECK;
if (err = __vm_allocate (__mach_task_self (), (vm_address_t *) ports,
_hurd_nports * sizeof (mach_port_t), 1))
return err;
*nports = _hurd_nports;
for (i = 0; i < _hurd_nports; ++i)
/* This function adds a new user ref for the *RESULT it gives back.
Our reply message uses move-send rights that consumes this ref. */
if (err = _hurd_ports_get (i, &(*ports)[i]))
{
/* Died part way through. Deallocate the ports already fetched. */
while (i-- > 0)
__mach_port_deallocate (__mach_task_self (), (*ports)[i]);
__vm_deallocate (__mach_task_self (),
(vm_address_t) *ports,
*nports * sizeof (mach_port_t));
return err;
}
*ports_type = MACH_MSG_TYPE_MOVE_SEND;
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_set_init_ports (mach_port_t msgport, mach_port_t auth,
const mach_port_t *ports, mach_msg_type_number_t nports)
{
mach_msg_type_number_t i;
error_t err;
AUTHCHECK;
for (i = 0; i < _hurd_nports; ++i)
{
if (err = _hurd_ports_set (i, ports[i]))
return err;
else
__mach_port_deallocate (__mach_task_self (), ports[i]);
}
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
/* Snarfing and frobbing the init ints. */
static kern_return_t
get_int (int which, int *value)
{
switch (which)
{
case INIT_UMASK:
*value = _hurd_umask;
return 0;
case INIT_SIGPENDING:
{
struct hurd_sigstate *ss = _hurd_global_sigstate;
__spin_lock (&ss->lock);
*value = ss->pending;
__spin_unlock (&ss->lock);
return 0;
}
case INIT_SIGIGN:
{
struct hurd_sigstate *ss = _hurd_global_sigstate;
sigset_t ign;
int sig;
__spin_lock (&ss->lock);
__sigemptyset (&ign);
for (sig = 1; sig < NSIG; ++sig)
if (ss->actions[sig].sa_handler == SIG_IGN)
__sigaddset (&ign, sig);
__spin_unlock (&ss->lock);
*value = ign;
return 0;
}
default:
return EINVAL;
}
}
kern_return_t
_S_msg_get_init_int (mach_port_t msgport, mach_port_t auth,
int which, int *value)
{
error_t err;
AUTHCHECK;
err = get_int (which, value);
if (err)
return err;
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_get_init_ints (mach_port_t msgport, mach_port_t auth,
int **values, mach_msg_type_number_t *nvalues)
{
error_t err;
mach_msg_type_number_t i;
AUTHCHECK;
if (err = __vm_allocate (__mach_task_self (), (vm_address_t *) values,
INIT_INT_MAX * sizeof (int), 1))
return err;
*nvalues = INIT_INT_MAX;
for (i = 0; i < INIT_INT_MAX; ++i)
switch (err = get_int (i, &(*values)[i]))
{
case 0: /* Success. */
break;
case EINVAL: /* Unknown index. */
(*values)[i] = 0;
break;
default: /* Lossage. */
__vm_deallocate (__mach_task_self (),
(vm_address_t) *values, INIT_INT_MAX * sizeof (int));
return err;
}
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
static kern_return_t
set_int (int which, int value)
{
switch (which)
{
case INIT_UMASK:
_hurd_umask = value;
return 0;
/* These are pretty odd things to do. But you asked for it. */
case INIT_SIGPENDING:
{
struct hurd_sigstate *ss = _hurd_global_sigstate;
__spin_lock (&ss->lock);
ss->pending = value;
__spin_unlock (&ss->lock);
return 0;
}
case INIT_SIGIGN:
{
struct hurd_sigstate *ss = _hurd_global_sigstate;
int sig;
const sigset_t ign = value;
__spin_lock (&ss->lock);
for (sig = 1; sig < NSIG; ++sig)
{
if (__sigismember (&ign, sig))
ss->actions[sig].sa_handler = SIG_IGN;
else if (ss->actions[sig].sa_handler == SIG_IGN)
ss->actions[sig].sa_handler = SIG_DFL;
}
__spin_unlock (&ss->lock);
return 0;
case INIT_TRACEMASK:
_hurdsig_traced = value;
return 0;
}
default:
return EINVAL;
}
}
kern_return_t
_S_msg_set_init_int (mach_port_t msgport, mach_port_t auth,
int which, int value)
{
error_t err;
AUTHCHECK;
err = set_int (which, value);
if (err)
return err;
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_set_init_ints (mach_port_t msgport, mach_port_t auth,
const int *values, mach_msg_type_number_t nvalues)
{
error_t err;
mach_msg_type_number_t i;
AUTHCHECK;
for (i = 0; i < INIT_INT_MAX; ++i)
switch (err = set_int (i, values[i]))
{
case 0: /* Success. */
break;
case EINVAL: /* Unknown index. */
break;
default: /* Lossage. */
return err;
}
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_get_fd (mach_port_t msgport, mach_port_t auth, int which,
mach_port_t *result, mach_msg_type_name_t *result_type)
{
AUTHCHECK;
/* This creates a new user reference for the send right.
Our reply message will move that reference to the caller. */
*result = __getdport (which);
if (*result == MACH_PORT_NULL)
return errno;
*result_type = MACH_MSG_TYPE_MOVE_SEND;
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_set_fd (mach_port_t msgport, mach_port_t auth,
int which, mach_port_t port)
{
error_t err;
AUTHCHECK;
/* We consume the reference if successful. */
err = HURD_FD_USE (which,
({
int flags = (descriptor->flags & FD_CLOEXEC)
? O_CLOEXEC : 0;
_hurd_port2fd (descriptor, port, flags);
0;
}));
if (err)
return err;
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
/* Snarfing and frobbing environment variables. */
kern_return_t
_S_msg_get_env_variable (mach_port_t msgport,
const_string_t variable,
char **data, mach_msg_type_number_t *datalen)
{
error_t err;
mach_msg_type_number_t valuelen;
const char *value = getenv (variable);
if (value == NULL)
return ENOENT;
valuelen = strlen (value);
if (valuelen > *datalen)
{
if (err = __vm_allocate (__mach_task_self (),
(vm_address_t *) data, valuelen, 1))
return err;
}
memcpy (*data, value, valuelen);
*datalen = valuelen;
return 0;
}
kern_return_t
_S_msg_set_env_variable (mach_port_t msgport, mach_port_t auth,
const_string_t variable,
const_string_t value,
int replace)
{
AUTHCHECK;
if (__setenv (variable, value, replace)) /* XXX name space */
return errno;
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_get_environment (mach_port_t msgport,
char **data, mach_msg_type_number_t *datalen)
{
/* Pack the environment into an array with nulls separating elements. */
if (__environ != NULL)
{
char *ap, **p;
size_t envlen = 0;
for (p = __environ; *p != NULL; ++p)
envlen += strlen (*p) + 1;
if (envlen > *datalen)
{
if (__vm_allocate (__mach_task_self (),
(vm_address_t *) data, envlen, 1))
return ENOMEM;
}
ap = *data;
for (p = __environ; *p != NULL; ++p)
ap = __memccpy (ap, *p, '\0', ULONG_MAX);
*datalen = envlen;
}
else
*datalen = 0;
return 0;
}
kern_return_t
_S_msg_set_environment (mach_port_t msgport, mach_port_t auth,
const char *data, mach_msg_type_number_t datalen)
{
int _hurd_split_args (char *, mach_msg_type_number_t, char **);
int envc;
char **envp;
AUTHCHECK;
envc = __argz_count (data, datalen);
envp = malloc ((envc + 1) * sizeof (char *));
if (envp == NULL)
return errno;
__argz_extract (data, datalen, envp);
__environ = envp; /* XXX cooperate with loadenv et al */
if (MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return 0;
}
kern_return_t
_S_msg_get_dtable (mach_port_t process,
mach_port_t auth,
portarray_t *dtable,
mach_msg_type_name_t *dtablePoly,
mach_msg_type_number_t *dtableCnt)
{
mach_port_t *ports;
mach_msg_type_number_t i;
error_t err;
AUTHCHECK;
HURD_CRITICAL_BEGIN;
__mutex_lock (&_hurd_dtable_lock);
if (err = __vm_allocate (__mach_task_self (), (vm_address_t *) &ports,
_hurd_dtablesize * sizeof(mach_port_t), 1))
goto out;
for (i = 0; i < _hurd_dtablesize; i++)
{
struct hurd_fd *cell = _hurd_dtable[i];
if (cell == NULL)
ports[i] = MACH_PORT_NULL;
else
{
__spin_lock (&cell->port.lock);
if (cell->port.port == MACH_PORT_NULL)
ports[i] = MACH_PORT_NULL;
else
{
ports[i] = cell->port.port;
/* We will move this send right. */
__mach_port_mod_refs (__mach_task_self (), ports[i],
MACH_PORT_RIGHT_SEND, +1);
}
__spin_unlock (&cell->port.lock);
}
}
*dtable = ports;
*dtablePoly = MACH_MSG_TYPE_MOVE_SEND;
*dtableCnt = _hurd_dtablesize;
out:
__mutex_unlock (&_hurd_dtable_lock);
HURD_CRITICAL_END;
if (!err && MACH_PORT_VALID (auth))
__mach_port_deallocate (__mach_task_self (), auth);
return err;
}
/* XXX */
kern_return_t
_S_msg_set_dtable (mach_port_t process,
mach_port_t refport,
const_portarray_t dtable,
mach_msg_type_number_t dtableCnt)
{ return EOPNOTSUPP; }