freebsd-skq/sys/compat/cloudabi/cloudabi_proc.c
Ed Schouten 457f7e23b1 Implement CloudABI's exec() call.
Summary:
In a runtime that is purely based on capability-based security, there is
a strong emphasis on how programs start their execution. We need to make
sure that we execute an new program with an exact set of file
descriptors, ensuring that credentials are not leaked into the process
accidentally.

Providing the right file descriptors is just half the problem. There
also needs to be a framework in place that gives meaning to these file
descriptors. How does a CloudABI mail server know which of the file
descriptors corresponds to the socket that receives incoming emails?
Furthermore, how will this mail server acquire its configuration
parameters, as it cannot open a configuration file from a global path on
disk?

CloudABI solves this problem by replacing traditional string command
line arguments by tree-like data structure consisting of scalars,
sequences and mappings (similar to YAML/JSON). In this structure, file
descriptors are treated as a first-class citizen. When calling exec(),
file descriptors are passed on to the new executable if and only if they
are referenced from this tree structure. See the cloudabi-run(1) man
page for more details and examples (sysutils/cloudabi-utils).

Fortunately, the kernel does not need to care about this tree structure
at all. The C library is responsible for serializing and deserializing,
but also for extracting the list of referenced file descriptors. The
system call only receives a copy of the serialized data and a layout of
what the new file descriptor table should look like:

    int proc_exec(int execfd, const void *data, size_t datalen, const int *fds,
              size_t fdslen);

This change introduces a set of fd*_remapped() functions:

- fdcopy_remapped() pulls a copy of a file descriptor table, remapping
  all of the file descriptors according to the provided mapping table.
- fdinstall_remapped() replaces the file descriptor table of the process
  by the copy created by fdcopy_remapped().
- fdescfree_remapped() frees the table in case we aborted before
  fdinstall_remapped().

We then add a function exec_copyin_data_fds() that builds on top these
functions. It copies in the data and constructs a new remapped file
descriptor. This is used by cloudabi_sys_proc_exec().

Test Plan:
cloudabi-run(1) is capable of spawning processes successfully, providing
it data and file descriptors. procstat -f seems to confirm all is good.
Regular FreeBSD processes also work properly.

Reviewers: kib, mjg

Reviewed By: mjg

Subscribers: imp

Differential Revision: https://reviews.freebsd.org/D3079
2015-07-16 07:05:42 +00:00

124 lines
3.5 KiB
C

/*-
* Copyright (c) 2015 Nuxi, https://nuxi.nl/
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/imgact.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/syscallsubr.h>
#include <compat/cloudabi/cloudabi_proto.h>
int
cloudabi_sys_proc_exec(struct thread *td,
struct cloudabi_sys_proc_exec_args *uap)
{
struct image_args args;
int error;
error = exec_copyin_data_fds(td, &args, uap->data, uap->datalen,
uap->fds, uap->fdslen);
if (error == 0) {
args.fd = uap->fd;
error = kern_execve(td, &args, NULL);
}
return (error);
}
int
cloudabi_sys_proc_exit(struct thread *td,
struct cloudabi_sys_proc_exit_args *uap)
{
exit1(td, W_EXITCODE(uap->rval, 0));
/* NOTREACHED */
}
int
cloudabi_sys_proc_fork(struct thread *td,
struct cloudabi_sys_proc_fork_args *uap)
{
/* Not implemented. */
return (ENOSYS);
}
int
cloudabi_sys_proc_raise(struct thread *td,
struct cloudabi_sys_proc_raise_args *uap)
{
static const int signals[] = {
[CLOUDABI_SIGABRT] = SIGABRT,
[CLOUDABI_SIGALRM] = SIGALRM,
[CLOUDABI_SIGBUS] = SIGBUS,
[CLOUDABI_SIGCHLD] = SIGCHLD,
[CLOUDABI_SIGCONT] = SIGCONT,
[CLOUDABI_SIGFPE] = SIGFPE,
[CLOUDABI_SIGHUP] = SIGHUP,
[CLOUDABI_SIGILL] = SIGILL,
[CLOUDABI_SIGINT] = SIGINT,
[CLOUDABI_SIGKILL] = SIGKILL,
[CLOUDABI_SIGPIPE] = SIGPIPE,
[CLOUDABI_SIGQUIT] = SIGQUIT,
[CLOUDABI_SIGSEGV] = SIGSEGV,
[CLOUDABI_SIGSTOP] = SIGSTOP,
[CLOUDABI_SIGSYS] = SIGSYS,
[CLOUDABI_SIGTERM] = SIGTERM,
[CLOUDABI_SIGTRAP] = SIGTRAP,
[CLOUDABI_SIGTSTP] = SIGTSTP,
[CLOUDABI_SIGTTIN] = SIGTTIN,
[CLOUDABI_SIGTTOU] = SIGTTOU,
[CLOUDABI_SIGURG] = SIGURG,
[CLOUDABI_SIGUSR1] = SIGUSR1,
[CLOUDABI_SIGUSR2] = SIGUSR2,
[CLOUDABI_SIGVTALRM] = SIGVTALRM,
[CLOUDABI_SIGXCPU] = SIGXCPU,
[CLOUDABI_SIGXFSZ] = SIGXFSZ,
};
ksiginfo_t ksi;
struct proc *p;
if (uap->sig >= nitems(signals) || signals[uap->sig] == 0) {
/* Invalid signal, or the null signal. */
return (uap->sig == 0 ? 0 : EINVAL);
}
p = td->td_proc;
ksiginfo_init(&ksi);
ksi.ksi_signo = signals[uap->sig];
ksi.ksi_code = SI_USER;
ksi.ksi_pid = p->p_pid;
ksi.ksi_uid = td->td_ucred->cr_ruid;
PROC_LOCK(p);
pksignal(p, ksi.ksi_signo, &ksi);
PROC_UNLOCK(p);
return (0);
}