From be01f9e6c71ba242d0d3d3d84b98ea1abe138c8f Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Wed, 27 May 2020 01:48:22 +0000 Subject: [PATCH] [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. --- LibOS/shim/include/shim_defs.h | 27 ------- LibOS/shim/include/shim_thread.h | 8 -- LibOS/shim/src/Makefile | 1 - LibOS/shim/src/bookkeep/shim_thread.c | 39 --------- LibOS/shim/src/sys/shim_exit.c | 16 ---- LibOS/shim/src/sys/shim_fork.c | 11 ++- LibOS/shim/src/sys/shim_vfork.c | 109 -------------------------- 7 files changed, 10 insertions(+), 201 deletions(-) delete mode 100644 LibOS/shim/src/sys/shim_vfork.c diff --git a/LibOS/shim/include/shim_defs.h b/LibOS/shim/include/shim_defs.h index a1a88492..0f5d9b69 100644 --- a/LibOS/shim/include/shim_defs.h +++ b/LibOS/shim/include/shim_defs.h @@ -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 */ diff --git a/LibOS/shim/include/shim_thread.h b/LibOS/shim/include/shim_thread.h index 6210c301..06d5d877 100644 --- a/LibOS/shim/include/shim_thread.h +++ b/LibOS/shim/include/shim_thread.h @@ -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); diff --git a/LibOS/shim/src/Makefile b/LibOS/shim/src/Makefile index 0f6501d3..5211372a 100644 --- a/LibOS/shim/src/Makefile +++ b/LibOS/shim/src/Makefile @@ -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 \ diff --git a/LibOS/shim/src/bookkeep/shim_thread.c b/LibOS/shim/src/bookkeep/shim_thread.c index d04c8750..98029a2a 100644 --- a/LibOS/shim/src/bookkeep/shim_thread.c +++ b/LibOS/shim/src/bookkeep/shim_thread.c @@ -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; diff --git a/LibOS/shim/src/sys/shim_exit.c b/LibOS/shim/src/sys/shim_exit.c index d81f1866..87fad732 100644 --- a/LibOS/shim/src/sys/shim_exit.c +++ b/LibOS/shim/src/sys/shim_exit.c @@ -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); } diff --git a/LibOS/shim/src/sys/shim_fork.c b/LibOS/shim/src/sys/shim_fork.c index 71e7b70d..4f2280fc 100644 --- a/LibOS/shim/src/sys/shim_fork.c +++ b/LibOS/shim/src/sys/shim_fork.c @@ -17,7 +17,7 @@ /* * shim_fork.c * - * Implementation of system call "fork". + * Implementation of system calls "fork" and "vfork". */ #include @@ -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(); +} diff --git a/LibOS/shim/src/sys/shim_vfork.c b/LibOS/shim/src/sys/shim_vfork.c deleted file mode 100644 index 36eb2c29..00000000 --- a/LibOS/shim/src/sys/shim_vfork.c +++ /dev/null @@ -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 . */ - -/* - * shim_vfork.c - * - * Implementation of system call "vfork". - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 -}