From db0e7cb18ece52b03abab877a07f79743448a01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borys=20Pop=C5=82awski?= Date: Mon, 16 Aug 2021 17:28:17 +0200 Subject: [PATCH 1/3] [LibOS] Add optional app input and output redirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New manifest keys are added: "libos.redirect.stdin", "libos.redirect.stdout" and "libos.redirect.stderr", which enable redirecting stdin, stdout and stderr (respectively). Values associated with those keys should be in-Graphene fs paths. Signed-off-by: Borys Popławski --- LibOS/shim/src/bookkeep/shim_handle.c | 80 +++++++++++++------ LibOS/shim/test/regression/fork_and_exec.c | 21 ++++- .../fork_and_exec.manifest.template | 42 ++++++++++ LibOS/shim/test/regression/test_libos.py | 26 ++++-- 4 files changed, 138 insertions(+), 31 deletions(-) create mode 100644 LibOS/shim/test/regression/fork_and_exec.manifest.template diff --git a/LibOS/shim/src/bookkeep/shim_handle.c b/LibOS/shim/src/bookkeep/shim_handle.c index ef6eb2d9e4..6662b4fc1b 100644 --- a/LibOS/shim/src/bookkeep/shim_handle.c +++ b/LibOS/shim/src/bookkeep/shim_handle.c @@ -165,6 +165,49 @@ int init_handle(void) { return 0; } +static int init_default_handle(struct shim_handle_map* handle_map, int fd, const char* manifest_key, + bool write) { + assert(locked(&handle_map->lock)); + + struct shim_handle* hdl = get_new_handle(); + if (!hdl) { + return -ENOMEM; + } + + char* path = NULL; + int ret = toml_string_in(g_manifest_root, manifest_key, &path); + if (ret < 0) { + log_error("Cannot parse '%s'", manifest_key); + put_handle(hdl); + return -EINVAL; + } + if (path) { + if (strstartswith(path, URI_PREFIX_FILE)) { + log_error("'%s' must be an in-Graphene fs path", manifest_key); + put_handle(hdl); + free(path); + return -EINVAL; + } + int flags = write ? O_WRONLY | O_CREAT : O_RDONLY; + ret = open_namei(hdl, /*start=*/NULL, path, flags, /*mode=*/0600, /*found=*/NULL); + free(path); + if (ret < 0) { + log_error("%s: cannot open '%s': %d", __func__, path, ret); + put_handle(hdl); + return ret; + } + } else { + if ((ret = init_tty_handle(hdl, write)) < 0) { + put_handle(hdl); + return ret; + } + } + + __init_handle(&handle_map->map[fd], /*fd=*/fd, hdl, /*flags=*/0); + put_handle(hdl); + return 0; +} + int init_important_handles(void) { int ret; struct shim_thread* thread = get_cur_thread(); @@ -195,46 +238,33 @@ int init_important_handles(void) { } } + assert(g_manifest_root); + /* initialize stdin */ if (!HANDLE_ALLOCATED(handle_map->map[0])) { - struct shim_handle* stdin_hdl = get_new_handle(); - if (!stdin_hdl) { - unlock(&handle_map->lock); - return -ENOMEM; - } - - if ((ret = init_tty_handle(stdin_hdl, /*write=*/false)) < 0) { + ret = init_default_handle(handle_map, /*fd=*/0, "libos.redirect.stdin", /*write=*/false); + if (ret < 0) { unlock(&handle_map->lock); - put_handle(stdin_hdl); return ret; } - - __init_handle(&handle_map->map[0], /*fd=*/0, stdin_hdl, /*flags=*/0); - put_handle(stdin_hdl); } /* initialize stdout */ if (!HANDLE_ALLOCATED(handle_map->map[1])) { - struct shim_handle* stdout_hdl = get_new_handle(); - if (!stdout_hdl) { - unlock(&handle_map->lock); - return -ENOMEM; - } - - if ((ret = init_tty_handle(stdout_hdl, /*write=*/true)) < 0) { + ret = init_default_handle(handle_map, /*fd=*/1, "libos.redirect.stdout", /*write=*/true); + if (ret < 0) { unlock(&handle_map->lock); - put_handle(stdout_hdl); return ret; } - - __init_handle(&handle_map->map[1], /*fd=*/1, stdout_hdl, /*flags=*/0); - put_handle(stdout_hdl); } - /* initialize stderr as duplicate of stdout */ + /* initialize stderr */ if (!HANDLE_ALLOCATED(handle_map->map[2])) { - struct shim_handle* stdout_hdl = handle_map->map[1]->handle; - __init_handle(&handle_map->map[2], /*fd=*/2, stdout_hdl, /*flags=*/0); + ret = init_default_handle(handle_map, /*fd=*/2, "libos.redirect.stderr", /*write=*/true); + if (ret < 0) { + unlock(&handle_map->lock); + return ret; + } } if (handle_map->fd_top == FD_NULL || handle_map->fd_top < 2) diff --git a/LibOS/shim/test/regression/fork_and_exec.c b/LibOS/shim/test/regression/fork_and_exec.c index 7b10237175..36742b8191 100644 --- a/LibOS/shim/test/regression/fork_and_exec.c +++ b/LibOS/shim/test/regression/fork_and_exec.c @@ -1,4 +1,6 @@ -#define _XOPEN_SOURCE 700 +#define _GNU_SOURCE +#include +#include #include #include #include @@ -9,6 +11,23 @@ int main(int argc, const char** argv, const char** envp) { pid_t child_pid; + char c = 0; + ssize_t x = read(0, &c, 1); + if (x < 0) { + err(1, "stdin read failed"); + } else if (x != 1) { + assert(x == 0); + errx(1, "unexpected eof on stdin"); + } + assert(c == 'a'); + x = read(0, &c, 1); + if (x < 0) { + err(1, "stdin 2nd read failed"); + } + if (x != 0) { + errx(1, "stdin read succeeded unexpectedly"); + } + /* duplicate STDOUT into newfd and pass it as exec_victim argument * (it will be inherited by exec_victim) */ int newfd = dup(1); diff --git a/LibOS/shim/test/regression/fork_and_exec.manifest.template b/LibOS/shim/test/regression/fork_and_exec.manifest.template new file mode 100644 index 0000000000..d2dc84c701 --- /dev/null +++ b/LibOS/shim/test/regression/fork_and_exec.manifest.template @@ -0,0 +1,42 @@ +loader.preload = "file:{{ graphene.libos }}" +libos.entrypoint = "fork_and_exec" +loader.argv0_override = "fork_and_exec" +loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" + +fs.mount.graphene_lib.type = "chroot" +fs.mount.graphene_lib.path = "/lib" +fs.mount.graphene_lib.uri = "file:{{ graphene.runtimedir() }}" + +fs.mount.host_lib.type = "chroot" +fs.mount.host_lib.path = "{{ arch_libdir }}" +fs.mount.host_lib.uri = "file:{{ arch_libdir }}" + +fs.mount.host_usr_lib.type = "chroot" +fs.mount.host_usr_lib.path = "/usr/{{ arch_libdir }}" +fs.mount.host_usr_lib.uri = "file:/usr/{{ arch_libdir }}" + +fs.mount.bin.type = "chroot" +fs.mount.bin.path = "/bin" +fs.mount.bin.uri = "file:/bin" + +fs.mount.stdin.type = "chroot" +fs.mount.stdin.path = "fork_and_exec.stdin" +fs.mount.stdin.uri = "file:fork_and_exec.stdin" + +libos.redirect.stdin = "fork_and_exec.stdin" +libos.redirect.stdout = "fork_and_exec.stdout" +libos.redirect.stderr = "fork_and_exec.stderr" + +sgx.trusted_files.runtime = "file:{{ graphene.runtimedir() }}/" +sgx.trusted_files.libgcc_s = "file:{{ arch_libdir }}/libgcc_s.so.1" +sgx.trusted_files.libstdcxx = "file:/usr{{ arch_libdir }}/libstdc++.so.6" + +sgx.trusted_files.entrypoint = "file:{{ entrypoint }}" +sgx.trusted_files.exec_victim = "file:exec_victim" + +sgx.allowed_files.stdin = "file:fork_and_exec.stdin" +sgx.allowed_files.stdout = "file:fork_and_exec.stdout" +sgx.allowed_files.stderr = "file:fork_and_exec.stderr" + +sgx.thread_num = 8 +sgx.nonpie_binary = true diff --git a/LibOS/shim/test/regression/test_libos.py b/LibOS/shim/test/regression/test_libos.py index 3fdf07b856..bc83dcb4fe 100644 --- a/LibOS/shim/test/regression/test_libos.py +++ b/LibOS/shim/test/regression/test_libos.py @@ -124,11 +124,27 @@ def test_201_exec_same(self): self.assertIn(arg + '\n', stdout) def test_202_fork_and_exec(self): - stdout, _ = self.run_binary(['fork_and_exec'], timeout=60) - - # fork and exec 2 page child binary - self.assertIn('child exited with status: 0', stdout) - self.assertIn('test completed successfully', stdout) + paths = ['fork_and_exec.stdin', 'fork_and_exec.stdout', 'fork_and_exec.stderr'] + for path in paths: + if os.path.exists(path): + os.remove(path) + with open(paths[0], 'wb') as f: + f.write(b'a') + + _, _ = self.run_binary(['fork_and_exec'], timeout=60) + + with open(paths[1], 'rb') as f: + file_stdout = f.read() + with open(paths[2], 'rb') as f: + file_stderr = f.read() + + for path in paths: + if os.path.exists(path): + os.remove(path) + + self.assertIn(b'child exited with status: 0', file_stdout) + self.assertIn(b'test completed successfully', file_stdout) + self.assertEqual(b'', file_stderr) def test_203_vfork_and_exec(self): stdout, _ = self.run_binary(['vfork_and_exec'], timeout=60) From e482fcc5e553ef37af0d260660717ad8d28d68d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borys=20Pop=C5=82awski?= Date: Mon, 16 Aug 2021 18:39:15 +0200 Subject: [PATCH 2/3] fixup! [LibOS] Add optional app input and output redirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Borys Popławski --- LibOS/shim/test/regression/test_libos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LibOS/shim/test/regression/test_libos.py b/LibOS/shim/test/regression/test_libos.py index bc83dcb4fe..70bf9ac283 100644 --- a/LibOS/shim/test/regression/test_libos.py +++ b/LibOS/shim/test/regression/test_libos.py @@ -127,7 +127,7 @@ def test_202_fork_and_exec(self): paths = ['fork_and_exec.stdin', 'fork_and_exec.stdout', 'fork_and_exec.stderr'] for path in paths: if os.path.exists(path): - os.remove(path) + os.remove(path) with open(paths[0], 'wb') as f: f.write(b'a') @@ -140,7 +140,7 @@ def test_202_fork_and_exec(self): for path in paths: if os.path.exists(path): - os.remove(path) + os.remove(path) self.assertIn(b'child exited with status: 0', file_stdout) self.assertIn(b'test completed successfully', file_stdout) From b1fbb05122a6ad71898672c9e608922b7d12c0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borys=20Pop=C5=82awski?= Date: Tue, 17 Aug 2021 16:18:50 +0200 Subject: [PATCH 3/3] fixup! [LibOS] Add optional app input and output redirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Borys Popławski --- LibOS/shim/src/bookkeep/shim_handle.c | 21 +++++++------------ LibOS/shim/test/regression/exec_victim.c | 19 ----------------- .../fork_and_exec.manifest.template | 10 +++------ LibOS/shim/test/regression/test_libos.py | 10 ++++----- 4 files changed, 14 insertions(+), 46 deletions(-) diff --git a/LibOS/shim/src/bookkeep/shim_handle.c b/LibOS/shim/src/bookkeep/shim_handle.c index 6662b4fc1b..113e66bc54 100644 --- a/LibOS/shim/src/bookkeep/shim_handle.c +++ b/LibOS/shim/src/bookkeep/shim_handle.c @@ -240,35 +240,28 @@ int init_important_handles(void) { assert(g_manifest_root); - /* initialize stdin */ - if (!HANDLE_ALLOCATED(handle_map->map[0])) { - ret = init_default_handle(handle_map, /*fd=*/0, "libos.redirect.stdin", /*write=*/false); + if (!g_pal_control->parent_process) { + ret = init_default_handle(handle_map, /*fd=*/0, "libos.redirect_fd.stdin", /*write=*/false); if (ret < 0) { unlock(&handle_map->lock); return ret; } - } - /* initialize stdout */ - if (!HANDLE_ALLOCATED(handle_map->map[1])) { - ret = init_default_handle(handle_map, /*fd=*/1, "libos.redirect.stdout", /*write=*/true); + ret = init_default_handle(handle_map, /*fd=*/1, "libos.redirect_fd.stdout", /*write=*/true); if (ret < 0) { unlock(&handle_map->lock); return ret; } - } - /* initialize stderr */ - if (!HANDLE_ALLOCATED(handle_map->map[2])) { - ret = init_default_handle(handle_map, /*fd=*/2, "libos.redirect.stderr", /*write=*/true); + ret = init_default_handle(handle_map, /*fd=*/2, "libos.redirect_fd.stderr", /*write=*/true); if (ret < 0) { unlock(&handle_map->lock); return ret; } - } - if (handle_map->fd_top == FD_NULL || handle_map->fd_top < 2) - handle_map->fd_top = 2; + if (handle_map->fd_top == FD_NULL || handle_map->fd_top < 2) + handle_map->fd_top = 2; + } unlock(&handle_map->lock); diff --git a/LibOS/shim/test/regression/exec_victim.c b/LibOS/shim/test/regression/exec_victim.c index 773556e605..6894eaa345 100644 --- a/LibOS/shim/test/regression/exec_victim.c +++ b/LibOS/shim/test/regression/exec_victim.c @@ -17,24 +17,5 @@ int main(int argc, char** argv, const char** envp) { fprintf(out, "Hello World (%s)!\n", argv[0]); fprintf(out, "envp[\'IN_EXECVE\'] = %s\n", getenv("IN_EXECVE")); - fprintf( - out, - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ -\n"); return 0; } diff --git a/LibOS/shim/test/regression/fork_and_exec.manifest.template b/LibOS/shim/test/regression/fork_and_exec.manifest.template index d2dc84c701..ff82eac7ee 100644 --- a/LibOS/shim/test/regression/fork_and_exec.manifest.template +++ b/LibOS/shim/test/regression/fork_and_exec.manifest.template @@ -19,13 +19,9 @@ fs.mount.bin.type = "chroot" fs.mount.bin.path = "/bin" fs.mount.bin.uri = "file:/bin" -fs.mount.stdin.type = "chroot" -fs.mount.stdin.path = "fork_and_exec.stdin" -fs.mount.stdin.uri = "file:fork_and_exec.stdin" - -libos.redirect.stdin = "fork_and_exec.stdin" -libos.redirect.stdout = "fork_and_exec.stdout" -libos.redirect.stderr = "fork_and_exec.stderr" +libos.redirect_fd.stdin = "fork_and_exec.stdin" +libos.redirect_fd.stdout = "fork_and_exec.stdout" +libos.redirect_fd.stderr = "fork_and_exec.stderr" sgx.trusted_files.runtime = "file:{{ graphene.runtimedir() }}/" sgx.trusted_files.libgcc_s = "file:{{ arch_libdir }}/libgcc_s.so.1" diff --git a/LibOS/shim/test/regression/test_libos.py b/LibOS/shim/test/regression/test_libos.py index 70bf9ac283..1c34d3085d 100644 --- a/LibOS/shim/test/regression/test_libos.py +++ b/LibOS/shim/test/regression/test_libos.py @@ -111,11 +111,7 @@ def test_110_basic_bootstrapping_cpp(self): def test_200_exec(self): stdout, _ = self.run_binary(['exec']) - # 2 page child binary - self.assertIn( - '0' * 89 + ' ' + - ('0' * 93 + ' ') * 15, - stdout) + self.assertIn('Hello World (./exec_victim)!', stdout) def test_201_exec_same(self): args = ['arg_#%d' % i for i in range(50)] @@ -131,7 +127,7 @@ def test_202_fork_and_exec(self): with open(paths[0], 'wb') as f: f.write(b'a') - _, _ = self.run_binary(['fork_and_exec'], timeout=60) + stdout, _ = self.run_binary(['fork_and_exec'], timeout=60) with open(paths[1], 'rb') as f: file_stdout = f.read() @@ -142,9 +138,11 @@ def test_202_fork_and_exec(self): if os.path.exists(path): os.remove(path) + self.assertIn(b'Hello World (./exec_victim)!', file_stdout) self.assertIn(b'child exited with status: 0', file_stdout) self.assertIn(b'test completed successfully', file_stdout) self.assertEqual(b'', file_stderr) + self.assertEqual(b'', stdout) def test_203_vfork_and_exec(self): stdout, _ = self.run_binary(['vfork_and_exec'], timeout=60)