[LibOS] Remove ALIAS_VFORK_AS_FORK and always emulate vfork via fork

Graphene has been emulating vfork() via fork() for a year already,
and there were no real-world cases when this wasn't sufficient. It
is time to retire the old code, broken and commented out anyway.
This commit is contained in:
Dmitrii Kuvaiskii
2020-05-27 22:31:22 +00:00
parent e379f21419
commit be01f9e6c7
7 changed files with 10 additions and 201 deletions
-27
View File
@@ -9,33 +9,6 @@
#define CPSTORE_DERANDOMIZATION 1
/* This macro disables current vfork implementation and aliases it to fork.
*
* Rationale:
* Current vfork() implementation is broken and works only in simple cases.
* The implementation creates a new thread in the same process and runs it
* in place of the previous (parent) thread which called vfork(). When the
* "pseudo-process" new thread reaches execve(), it silently dies and
* switches execution back to the suspended parent thread (as per vfork
* semantics). Because execve() emulation creates a new host-OS process,
* this vfork implementation works in simple benign cases.
*
* However, this co-existence of the "pseudo-process" thread with threads
* of the parent process leads to bugs elsewhere in Graphene. In general,
* the rest of Graphene is not aware of such situation when two processes
* co-exist in the same Graphene instance and share memory. If the new
* "pseudo-process" thread makes syscalls in-between vfork() and execve()
* or abnormally dies or receives a signal, Graphene may hang or segfault
* or end up with inconsistent internal state.
*
* Therefore, instead of trying to support Linux semantics for vfork() --
* which requires adding corner-cases in signal handling and syscalls --
* we simply redirect vfork() as fork(). We assume that performance hit is
* negligible (Graphene has to migrate internal state anyway which is slow)
* and apps do not rely on insane Linux-specific semantics of vfork().
* */
#define ALIAS_VFORK_AS_FORK 1
#define DEFAULT_HEAP_MIN_SIZE (256 * 1024 * 1024) /* 256MB */
#define DEFAULT_MEM_MAX_NPAGES (1024 * 1024) /* 4GB */
#define DEFAULT_BRK_MAX_SIZE (256 * 1024) /* 256KB */
-8
View File
@@ -53,10 +53,6 @@ struct shim_thread {
struct shim_thread * parent;
/* thread leader */
struct shim_thread * leader;
#ifndef ALIAS_VFORK_AS_FORK
/* dummy thread: stores blocked parent thread for vfork */
struct shim_thread * dummy;
#endif
/* child handles; protected by thread->lock */
LISTP_TYPE(shim_thread) children;
/* nodes in child handles; protected by the parent's lock */
@@ -286,10 +282,6 @@ void del_thread (struct shim_thread * thread);
void cleanup_thread(IDTYPE caller, void* thread);
int check_last_thread(struct shim_thread* self);
#ifndef ALIAS_VFORK_AS_FORK
void switch_dummy_thread (struct shim_thread * thread);
#endif
int walk_thread_list (int (*callback) (struct shim_thread *, void *, bool *),
void * arg);
-1
View File
@@ -99,7 +99,6 @@ objs = \
sys/shim_stat.o \
sys/shim_time.o \
sys/shim_uname.o \
sys/shim_vfork.o \
sys/shim_wait.o \
sys/shim_wrappers.o \
utils/md5.o \
-39
View File
@@ -554,42 +554,6 @@ out:
return ret;
}
#ifndef ALIAS_VFORK_AS_FORK
void switch_dummy_thread (struct shim_thread * thread)
{
struct shim_thread * real_thread = thread->dummy;
IDTYPE child = thread->tid;
assert(thread->frameptr);
assert(real_thread->stack);
assert(real_thread->stack_top > real_thread->stack);
memcpy(thread->frameptr, real_thread->stack,
real_thread->stack_top - real_thread->stack);
real_thread->stack = thread->stack;
real_thread->stack_top = thread->stack_top;
real_thread->frameptr = thread->frameptr;
DkSegmentRegister(PAL_SEGMENT_FS, real_thread->tcb);
set_cur_thread(real_thread);
debug("set tcb to %p\n", real_thread->tcb);
debug("jump to the stack %p\n", real_thread->frameptr);
debug("shim_vfork success (returning %d)\n", child);
/* jump onto old stack
we actually pop rbp as rsp, and later we will call 'ret' */
__asm__ volatile("movq %0, %%rbp\r\n"
"leaveq\r\n"
"retq\r\n" :
: "g"(real_thread->frameptr),
"a"(child)
: "memory");
__builtin_unreachable();
}
#endif
BEGIN_CP_FUNC(signal_handles)
{
__UNUSED(size);
@@ -660,9 +624,6 @@ BEGIN_CP_FUNC(thread)
new_thread->in_vm = false;
new_thread->parent = NULL;
#ifndef ALIAS_VFORK_AS_FORK
new_thread->dummy = NULL;
#endif
new_thread->handle_map = NULL;
new_thread->root = NULL;
new_thread->cwd = NULL;
-16
View File
@@ -181,14 +181,6 @@ noreturn int shim_do_exit_group (int error_code)
if (debug_handle)
sysparser_printf("---- shim_exit_group (returning %d)\n", error_code);
#ifndef ALIAS_VFORK_AS_FORK
if (cur_thread->dummy) {
cur_thread->term_signal = 0;
thread_exit(cur_thread, true);
switch_dummy_thread(cur_thread);
}
#endif
debug("now kill other threads in the process\n");
do_kill_proc(cur_thread->tgid, cur_thread->tgid, SIGKILL, false);
while (check_last_thread(cur_thread)) {
@@ -208,13 +200,5 @@ noreturn int shim_do_exit (int error_code)
if (debug_handle)
sysparser_printf("---- shim_exit (returning %d)\n", error_code);
#ifndef ALIAS_VFORK_AS_FORK
if (cur_thread->dummy) {
cur_thread->term_signal = 0;
thread_exit(cur_thread, true);
switch_dummy_thread(cur_thread);
}
#endif
thread_or_process_exit(error_code, 0);
}
+10 -1
View File
@@ -17,7 +17,7 @@
/*
* shim_fork.c
*
* Implementation of system call "fork".
* Implementation of system calls "fork" and "vfork".
*/
#include <errno.h>
@@ -99,3 +99,12 @@ int shim_do_fork(void) {
put_thread(new_thread);
return tid;
}
/* Instead of trying to support Linux semantics for vfork() -- which requires adding corner-cases in
* signal handling and syscalls -- we simply treat vfork() as fork(). We assume that performance hit
* is negligible (Graphene has to migrate internal state anyway which is slow) and apps do not rely
* on insane Linux-specific semantics of vfork(). */
int shim_do_vfork(void) {
debug("vfork() was called by the application, implemented as alias to fork() in Graphene\n");
return shim_do_fork();
}
-109
View File
@@ -1,109 +0,0 @@
/* Copyright (C) 2014 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 Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/*
* shim_vfork.c
*
* Implementation of system call "vfork".
*/
#include <errno.h>
#include <linux/futex.h>
#include <pal.h>
#include <pal_error.h>
#include <shim_checkpoint.h>
#include <shim_internal.h>
#include <shim_table.h>
#include <shim_thread.h>
#include <shim_utils.h>
#include <sys/mman.h>
#include <sys/syscall.h>
int shim_do_vfork(void) {
#ifdef ALIAS_VFORK_AS_FORK
debug("vfork() is an alias to fork() in Graphene, calling fork() now\n");
return shim_do_fork();
#else
/* NOTE: leaving this old implementation for historical reference */
/* DEP 7/7/12 - Why r13?
*
* Chia-che: when libc call vfork, they store the pointer to the
* caller in rdi. (reference: sysdeps/unix/sysv/linux/x86_64/vfork.S.
* Because rdi might be used in SHIM, I cache rdi in r13 (reference:
* syscallas.S).
*/
struct shim_thread* cur_thread = get_cur_thread();
struct shim_thread* new_thread = get_new_thread(0);
/* put the new thread in a new process (thread group) */
__asm__ volatile ("movq %%rbp, %0\r\n" : "=r"(new_thread->frameptr));
size_t stack_size = 4096;
if (new_thread->frameptr <= cur_thread->stack_top && new_thread->frameptr > cur_thread->stack)
stack_size = cur_thread->stack_top - new_thread->frameptr;
void* dummy_stack = system_malloc(stack_size);
if (!dummy_stack) {
debug("creation of stack failed\n");
put_thread(new_thread);
return -PAL_ERRNO;
}
memcpy(dummy_stack, new_thread->frameptr, stack_size);
/* assigned the stack of the thread */
lock(&cur_thread->lock);
new_thread->tgid = new_thread->tid;
new_thread->in_vm = true;
new_thread->is_alive = true;
new_thread->stack = cur_thread->stack;
new_thread->stack_top = cur_thread->stack_top;
cur_thread->stack = dummy_stack;
cur_thread->stack_top = dummy_stack + stack_size;
cur_thread->frameptr = NULL;
unlock(&cur_thread->lock);
/* Now we are good, set this child as ours */
set_as_child(NULL, new_thread);
/* add the child to the global list */
add_thread(new_thread);
new_thread->dummy = cur_thread;
struct shim_handle_map* handle_map = get_cur_handle_map(cur_thread);
/* pop the ref count of current handle map to prevent revocation */
get_handle_map(handle_map);
struct shim_handle_map* new_map = NULL;
/* duplicate handle map intp a new handle map */
dup_handle_map(&new_map, handle_map);
/* set the new handle map to new thread */
set_handle_map(new_thread, new_map);
/* push back the ref count of handle map */
put_handle_map(handle_map);
/* we have the thread handle from PAL, now set it to the child */
new_thread->pal_handle = cur_thread->pal_handle;
/* set the current thread running */
set_cur_thread(new_thread);
put_thread(new_thread);
/* here we return immediately, no letting the hooks mes up our stack */
return 0;
#endif
}