Skip to content
This repository has been archived by the owner on Jan 20, 2022. It is now read-only.

ON HOLD [LibOS] Add optional app input and output redirection #2650

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 55 additions & 25 deletions LibOS/shim/src/bookkeep/shim_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
21 changes: 20 additions & 1 deletion LibOS/shim/test/regression/fork_and_exec.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#define _XOPEN_SOURCE 700
#define _GNU_SOURCE
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
Expand All @@ -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);
Expand Down
42 changes: 42 additions & 0 deletions LibOS/shim/test/regression/fork_and_exec.manifest.template
Original file line number Diff line number Diff line change
@@ -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
26 changes: 21 additions & 5 deletions LibOS/shim/test/regression/test_libos.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down