summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_exec.c
diff options
context:
space:
mode:
authored <ed@FreeBSD.org>2015-07-16 07:05:42 +0000
committered <ed@FreeBSD.org>2015-07-16 07:05:42 +0000
commita85d617da64df13a78f7bad95ba6c0a6be6da840 (patch)
tree37f928a112bc77ecd45bf699ac8abee94c4461c1 /sys/kern/kern_exec.c
parent03f3b4766f6801df0aeebea876454797668147ad (diff)
downloadFreeBSD-src-a85d617da64df13a78f7bad95ba6c0a6be6da840.zip
FreeBSD-src-a85d617da64df13a78f7bad95ba6c0a6be6da840.tar.gz
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
Diffstat (limited to 'sys/kern/kern_exec.c')
-rw-r--r--sys/kern/kern_exec.c88
1 files changed, 81 insertions, 7 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 7a5e913..4bb16e6 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -580,13 +580,20 @@ interpret:
else
suword(--stack_base, imgp->args->argc);
- /*
- * For security and other reasons, the file descriptor table cannot
- * be shared after an exec.
- */
- fdunshare(td);
- /* close files on exec */
- fdcloseexec(td);
+ if (args->fdp != NULL) {
+ /* Install a brand new file descriptor table. */
+ fdinstall_remapped(td, args->fdp);
+ args->fdp = NULL;
+ } else {
+ /*
+ * Keep on using the existing file descriptor table. For
+ * security and other reasons, the file descriptor table
+ * cannot be shared after an exec.
+ */
+ fdunshare(td);
+ /* close files on exec */
+ fdcloseexec(td);
+ }
/*
* Malloc things before we need locks.
@@ -1197,6 +1204,71 @@ err_exit:
return (error);
}
+int
+exec_copyin_data_fds(struct thread *td, struct image_args *args,
+ const void *data, size_t datalen, const int *fds, size_t fdslen)
+{
+ struct filedesc *ofdp;
+ const char *p;
+ int *kfds;
+ int error;
+
+ memset(args, '\0', sizeof(*args));
+ ofdp = td->td_proc->p_fd;
+ if (datalen >= ARG_MAX || fdslen > ofdp->fd_lastfile + 1)
+ return (E2BIG);
+ error = exec_alloc_args(args);
+ if (error != 0)
+ return (error);
+
+ args->begin_argv = args->buf;
+ args->stringspace = ARG_MAX;
+
+ if (datalen > 0) {
+ /*
+ * Argument buffer has been provided. Copy it into the
+ * kernel as a single string and add a terminating null
+ * byte.
+ */
+ error = copyin(data, args->begin_argv, datalen);
+ if (error != 0)
+ goto err_exit;
+ args->begin_argv[datalen] = '\0';
+ args->endp = args->begin_argv + datalen + 1;
+ args->stringspace -= datalen + 1;
+
+ /*
+ * Traditional argument counting. Count the number of
+ * null bytes.
+ */
+ for (p = args->begin_argv; p < args->endp; ++p)
+ if (*p == '\0')
+ ++args->argc;
+ } else {
+ /* No argument buffer provided. */
+ args->endp = args->begin_argv;
+ }
+ /* There are no environment variables. */
+ args->begin_envv = args->endp;
+
+ /* Create new file descriptor table. */
+ kfds = malloc(fdslen * sizeof(int), M_TEMP, M_WAITOK);
+ error = copyin(fds, kfds, fdslen * sizeof(int));
+ if (error != 0) {
+ free(kfds, M_TEMP);
+ goto err_exit;
+ }
+ error = fdcopy_remapped(ofdp, kfds, fdslen, &args->fdp);
+ free(kfds, M_TEMP);
+ if (error != 0)
+ goto err_exit;
+
+ return (0);
+err_exit:
+ exec_free_args(args);
+ return (error);
+}
+
/*
* Allocate temporary demand-paged, zero-filled memory for the file name,
* argument, and environment strings. Returns zero if the allocation succeeds
@@ -1223,6 +1295,8 @@ exec_free_args(struct image_args *args)
free(args->fname_buf, M_TEMP);
args->fname_buf = NULL;
}
+ if (args->fdp != NULL)
+ fdescfree_remapped(args->fdp);
}
/*
OpenPOWER on IntegriCloud