From e14bf7950fdb7e80ebb3c1f4bd38eb7b26e61caa Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Wed, 27 Nov 2019 19:32:55 -0800 Subject: [PATCH] [LibOS] Allow Graphene-SGX to occupy the same process on execve() The execve() syscall starts a new executable in the *same* process. Previously, Graphene followed this convention *only* for non-SGX PALs. If PAL was Linux-SGX, Graphene silently terminated the process and created a new one. This deviation from standard execve() behavior resulted in the host shell becoming detached from the Graphene-SGX process. In turn, this led to our Bash example (on Ubuntu 18.04, bash version 4.4.19) "terminating" early from the point of view of Jenkins, and SGX-18.04 pipeline failed. This commit allows Graphene-SGX to execve() in the same process, but only if it is the same executable (as in the Bash example). It is still impossible to execve() in the same process for a different executable since this requires a new SGX enclave measurement and thus demands a new process. --- LibOS/shim/include/shim_defs.h | 2 - LibOS/shim/src/sys/shim_exec.c | 48 ++++++++++++++++++++---- LibOS/shim/test/regression/exec_same.c | 16 ++++++++ LibOS/shim/test/regression/test_libos.py | 8 +++- 4 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 LibOS/shim/test/regression/exec_same.c diff --git a/LibOS/shim/include/shim_defs.h b/LibOS/shim/include/shim_defs.h index 4d11cff7..664c6254 100644 --- a/LibOS/shim/include/shim_defs.h +++ b/LibOS/shim/include/shim_defs.h @@ -47,8 +47,6 @@ #define CP_INIT_VMA_SIZE (64 * 1024 * 1024) /* 64MB */ -#define EXECVE_RTLD 1 - #define ENABLE_ASLR 1 /* debug message printout */ diff --git a/LibOS/shim/src/sys/shim_exec.c b/LibOS/shim/src/sys/shim_exec.c index 290f28ce..67b5571e 100644 --- a/LibOS/shim/src/sys/shim_exec.c +++ b/LibOS/shim/src/sys/shim_exec.c @@ -38,6 +38,31 @@ #include #include +/* returns 0 if normalized URIs are the same; assumes file URIs */ +static int normalize_and_cmp_uris(const char* uri1, const char* uri2) { + char norm1[STR_SIZE]; + char norm2[STR_SIZE]; + size_t len; + int ret; + + if (!strstartswith_static(uri1, "file:") || !strstartswith_static(uri2, "file:")) + return -1; + + uri1 += static_strlen("file:"); + len = sizeof(norm1); + ret = get_norm_path(uri1, norm1, &len); + if (ret < 0) + return ret; + + uri2 += static_strlen("file:"); + len = sizeof(norm2); + ret = get_norm_path(uri2, norm2, &len); + if (ret < 0) + return ret; + + return memcmp(norm1, norm2, len + 1); +} + static int close_on_exec (struct shim_fd_handle * fd_hdl, struct shim_handle_map * map) { @@ -451,16 +476,23 @@ err: SAVE_PROFILE_INTERVAL(open_file_for_exec); -#if EXECVE_RTLD == 1 - if (strcmp_static(PAL_CB(host_type), "Linux-SGX")) { - int is_last = check_last_thread(cur_thread) == 0; - if (is_last) { - debug("execve() in the same process\n"); - return shim_do_execve_rtld(exec, argv, envp); + bool use_same_process = check_last_thread(cur_thread) == 0; + if (use_same_process && !strcmp_static(PAL_CB(host_type), "Linux-SGX")) { + /* for SGX PALs, can use same process only if it is the same executable (because a + * different executable has a different measurement and thus requires a new enclave); + * this special case is to correctly handle e.g. Bash process replacing itself */ + assert(cur_thread->exec); + if (normalize_and_cmp_uris(qstrgetstr(&cur_thread->exec->uri), qstrgetstr(&exec->uri))) { + /* it is not the same executable, definitely cannot use same process */ + use_same_process = false; } - debug("execve() in a new process\n"); } -#endif + + if (use_same_process) { + debug("execve() in the same process\n"); + return shim_do_execve_rtld(exec, argv, envp); + } + debug("execve() in a new process\n"); INC_PROFILE_OCCURENCE(syscall_use_ipc); diff --git a/LibOS/shim/test/regression/exec_same.c b/LibOS/shim/test/regression/exec_same.c new file mode 100644 index 00000000..ee40bfab --- /dev/null +++ b/LibOS/shim/test/regression/exec_same.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +int main(int argc, char** argv, char** envp) { + if (argc > 1) { + puts(argv[1]); + return 0; + } + char* const new_argv[] = {argv[0], "hello from execv process", NULL}; + execv(new_argv[0], new_argv); + + /* must never reach this */ + return 1; +} diff --git a/LibOS/shim/test/regression/test_libos.py b/LibOS/shim/test/regression/test_libos.py index 31b0bb63..b80acdc7 100644 --- a/LibOS/shim/test/regression/test_libos.py +++ b/LibOS/shim/test/regression/test_libos.py @@ -85,14 +85,18 @@ class TC_01_Bootstrap(RegressionTestCase): '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ', stdout) - def test_201_fork_and_exec(self): + def test_201_exec_same(self): + stdout, stderr = self.run_binary(['exec_same']) + self.assertIn('hello from execv process', stdout) + + def test_202_fork_and_exec(self): stdout, stderr = self.run_binary(['fork_and_exec']) # fork and exec 2 page child binary self.assertIn('child exited with status: 0', stdout) self.assertIn('test completed successfully', stdout) - def test_202_vfork_and_exec(self): + def test_203_vfork_and_exec(self): stdout, stderr = self.run_binary(['vfork_and_exec']) # vfork and exec 2 page child binary