/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
/* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
/* Copyright (C) 2014 OSCAR lab, Stony Brook University
This file is part of Graphene Library OS.
Graphene Library OS is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Graphene Library OS 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/*
* shim_epoll.c
*
* Implementation of system call "execve".
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int close_cloexec_handle (struct shim_handle_map * map)
{
auto int close_on_exec (struct shim_fd_handle * fd_hdl,
struct shim_handle_map * map, void * arg)
{
if (fd_hdl->flags & FD_CLOEXEC) {
struct shim_handle * hdl = __detach_fd_handle(fd_hdl, NULL, map);
close_handle(hdl);
}
return 0;
}
return walk_handle_map(&close_on_exec, map, NULL);
}
DEFINE_PROFILE_CATAGORY(exec_rtld, exec);
DEFINE_PROFILE_INTERVAL(alloc_new_stack_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(arrange_arguments_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(unmap_executable_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(unmap_loaded_binaries_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(unmap_all_vmas_for_exec, exec_rtld);
DEFINE_PROFILE_INTERVAL(load_new_executable_for_exec, exec_rtld);
static void * old_stack_top, * old_stack, * old_stack_red;
static const char ** new_argp;
static int new_argc;
static elf_auxv_t * new_auxp;
#define REQUIRED_ELF_AUXV 6
int shim_do_execve_rtld (struct shim_handle * hdl, const char ** argv,
const char ** envp)
{
BEGIN_PROFILE_INTERVAL();
struct shim_thread * cur_thread = get_cur_thread();
int ret;
if ((ret = close_cloexec_handle(cur_thread->handle_map)) < 0)
return ret;
SAVE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec);
void * tcb = malloc(sizeof(__libc_tcb_t));
if (!tcb)
return -ENOMEM;
populate_tls(tcb, false);
debug("set tcb to %p\n", tcb);
put_handle(cur_thread->exec);
get_handle(hdl);
cur_thread->exec = hdl;
old_stack_top = cur_thread->stack_top;
old_stack = cur_thread->stack;
old_stack_red = cur_thread->stack_red;
cur_thread->stack_top = NULL;
cur_thread->stack = NULL;
cur_thread->stack_red = NULL;
initial_envp = NULL;
new_argc = 0;
for (const char ** a = argv ; *a ; a++, new_argc++);
if ((ret = init_stack(argv, envp, &new_argp,
REQUIRED_ELF_AUXV, &new_auxp)) < 0)
return ret;
SAVE_PROFILE_INTERVAL(alloc_new_stack_for_exec);
switch_stack(new_argp);
cur_thread = get_cur_thread();
UPDATE_PROFILE_INTERVAL();
DkVirtualMemoryFree(old_stack, old_stack_top - old_stack);
DkVirtualMemoryFree(old_stack_red, old_stack - old_stack_red);
int flags = 0;
bkeep_munmap(old_stack, old_stack_top - old_stack, &flags);
bkeep_munmap(old_stack_red, old_stack - old_stack_red, &flags);
remove_loaded_libraries();
clean_link_map_list();
SAVE_PROFILE_INTERVAL(unmap_loaded_binaries_for_exec);
init_brk();
unmap_all_vmas();
SAVE_PROFILE_INTERVAL(unmap_all_vmas_for_exec);
if ((ret = load_elf_object(cur_thread->exec, NULL, 0)) < 0)
shim_terminate();
load_elf_interp(cur_thread->exec);
SAVE_PROFILE_INTERVAL(load_new_executable_for_exec);
cur_thread->robust_list = NULL;
#ifdef PROFILE
if (ENTER_TIME)
SAVE_PROFILE_INTERVAL_SINCE(syscall_execve, ENTER_TIME);
#endif
debug("execve: start execution\n");
execute_elf_object(cur_thread->exec, new_argc, new_argp,
REQUIRED_ELF_AUXV, new_auxp);
return 0;
}
#include
DEFINE_PROFILE_CATAGORY(exec, );
DEFINE_PROFILE_INTERVAL(search_and_check_file_for_exec, exec);
DEFINE_PROFILE_INTERVAL(open_file_for_exec, exec);
DEFINE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec, exec);
static int migrate_execve (struct shim_cp_store * cpstore,
struct shim_thread * thread,
struct shim_process * process, va_list ap)
{
struct shim_handle_map * handle_map;
const char ** envp = va_arg(ap, const char **);
int ret;
BEGIN_PROFILE_INTERVAL();
if ((ret = dup_handle_map(&handle_map, thread->handle_map)) < 0)
return ret;
set_handle_map(thread, handle_map);
if ((ret = close_cloexec_handle(handle_map)) < 0)
return ret;
SAVE_PROFILE_INTERVAL(close_CLOEXEC_files_for_exec);
/* Now we start to migrate bookkeeping for exec.
The data we need to migrate are:
1. cur_threadrent thread
2. cur_threadrent filesystem
3. handle mapping
4. each handle */
BEGIN_MIGRATION_DEF(execve,
struct shim_thread * thread,
struct shim_process * proc,
const char ** envp)
{
DEFINE_MIGRATE(process, proc, sizeof(struct shim_process));
DEFINE_MIGRATE(all_mounts, NULL, 0);
DEFINE_MIGRATE(running_thread, thread, sizeof(struct shim_thread));
DEFINE_MIGRATE(handle_map, thread->handle_map,
sizeof (struct shim_handle_map));
DEFINE_MIGRATE(migratable, NULL, 0);
DEFINE_MIGRATE(environ, envp, 0);
}
END_MIGRATION_DEF(execve)
return START_MIGRATE(cpstore, execve, thread, process, envp);
}
int shim_do_execve (const char * file, const char ** argv,
const char ** envp)
{
struct shim_thread * cur_thread = get_cur_thread();
struct shim_dentry * dent = NULL;
int ret = 0, argc = 0;
for (const char ** a = argv ; *a ; a++, argc++);
if (!envp)
envp = initial_envp;
BEGIN_PROFILE_INTERVAL();
LIST_HEAD(shargs);
struct sharg {
struct list_head list;
int len;
char arg[0];
};
reopen:
if ((ret = path_lookupat(NULL, file, LOOKUP_OPEN, &dent)) < 0)
return ret;
struct shim_mount * fs = dent->fs;
get_dentry(dent);
if (!fs->d_ops->open) {
ret = -EACCES;
err:
put_dentry(dent);
return ret;
}
if (fs->d_ops->mode) {
__kernel_mode_t mode;
if ((ret = fs->d_ops->mode(dent, &mode, 1)) < 0)
goto err;
}
SAVE_PROFILE_INTERVAL(search_and_check_file_for_exec);
struct shim_handle * exec = NULL;
if (!(exec = get_new_handle())) {
ret = -ENOMEM;
goto err;
}
set_handle_fs(exec, fs);
exec->flags = O_RDONLY;
exec->acc_mode = MAY_READ;
ret = fs->d_ops->open(exec, dent, O_RDONLY);
if (qstrempty(&exec->uri)) {
put_handle(exec);
return -EACCES;
}
int pathlen;
char *path = dentry_get_path(dent, true, &pathlen);
qstrsetstr(&exec->path, path, pathlen);
if ((ret = check_elf_object(exec)) < 0 && ret != -EINVAL) {
put_handle(exec);
return ret;
}
if (ret == -EINVAL) { /* it's a shebang */
LIST_HEAD(new_shargs);
struct sharg * next = NULL;
bool ended = false, started = false;
char buf[80];
do {
ret = do_handle_read(exec, buf, 80);
if (ret <= 0)
break;
char * s = buf, * c = buf, * e = buf + ret;
if (!started) {
if (ret < 2 || buf[0] != '#' || buf[1] != '!')
break;
s += 2;
c += 2;
started = true;
}
for (; c < e ; c++) {
if (*c == ' ' || *c == '\n' || c == e - 1) {
int l = (*c == ' ' || * c == '\n') ? c - s : e - s;
if (next) {
struct sharg * sh =
__alloca(sizeof(struct sharg) + next->len + l + 1);
sh->len = next->len + l;
memcpy(sh->arg, next->arg, next->len);
memcpy(sh->arg + next->len, s, l);
sh->arg[next->len + l] = 0;
next = sh;
} else {
next = __alloca(sizeof(struct sharg) + l + 1);
next->len = l;
memcpy(next->arg, s, l);
next->arg[l] = 0;
}
if (*c == ' ' || *c == '\n') {
INIT_LIST_HEAD(&next->list);
list_add_tail(&next->list, &new_shargs);
next = NULL;
s = c + 1;
if (*c == '\n') {
ended = true;
break;
}
}
}
}
} while (!ended);
if (started) {
if (next) {
INIT_LIST_HEAD(&next->list);
list_add_tail(&next->list, &new_shargs);
}
struct sharg * first =
list_first_entry(&new_shargs, struct sharg, list);
assert(first);
debug("detected as script: run by %s\n", first->arg);
file = first->arg;
list_splice(&new_shargs, &shargs);
put_handle(exec);
goto reopen;
}
}
SAVE_PROFILE_INTERVAL(open_file_for_exec);
int is_last = check_last_thread(cur_thread) == 0;
if (is_last)
return shim_do_execve_rtld(exec, argv, envp);
INC_PROFILE_OCCURENCE(syscall_use_ipc);
if (!list_empty(&shargs)) {
struct sharg * sh;
int shargc = 0, cnt = 0;
list_for_each_entry(sh, &shargs, list)
shargc++;
const char ** new_argv =
__alloca(sizeof(const char *) * (argc + shargc + 1));
list_for_each_entry(sh, &shargs, list)
new_argv[cnt++] = sh->arg;
for (cnt = 0 ; cnt < argc ; cnt++)
new_argv[shargc + cnt] = argv[cnt];
new_argv[shargc + argc] = NULL;
argv = new_argv;
}
lock(cur_thread->lock);
put_handle(cur_thread->exec);
cur_thread->exec = exec;
void * stack = cur_thread->stack;
void * stack_top = cur_thread->stack_top;
void * tcb = cur_thread->tcb;
bool user_tcb = cur_thread->user_tcb;
void * frameptr = cur_thread->frameptr;
cur_thread->stack = NULL;
cur_thread->stack_top = NULL;
cur_thread->frameptr = NULL;
cur_thread->tcb = NULL;
cur_thread->user_tcb = false;
cur_thread->in_vm = false;
unlock(cur_thread->lock);
ret = do_migrate_process(&migrate_execve, exec, argv, cur_thread, envp);
lock(cur_thread->lock);
cur_thread->stack = stack;
cur_thread->stack_top = stack_top;
cur_thread->frameptr = frameptr;
cur_thread->tcb = tcb;
cur_thread->user_tcb = user_tcb;
if (ret < 0) {
cur_thread->in_vm = true;
unlock(cur_thread->lock);
return ret;
}
struct shim_handle_map * handle_map = cur_thread->handle_map;
cur_thread->handle_map = NULL;
unlock(cur_thread->lock);
if (handle_map)
put_handle_map(handle_map);
if (cur_thread->dummy)
switch_dummy_thread(cur_thread);
try_process_exit(0);
return 0;
}