Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First iteration of etc passthrough #850

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Documentation/manifest-syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,25 @@ arguments to be provided at runtime from an external (trusted) source.
If none of the above arguments-handling manifest options is specified in the
manifest, the application will get ``argv = [ <libos.entrypoint value> ]``.

Emulate ``/etc``
^^^^^^^^^^^^^^^^

::

libos.emulate_etc_files = [true|false]
(Default: false)

This specifies whether to emulate runtime files under ``/etc``.
In the case of SGX on Linux, this is achieved by taking the host's ``/etc``
files, filter supported options, and sanitize them.
The set of extra runtime files is limited to:

- ``/etc/hostname``

This option takes precedence over ``fs.mounts``.
This means that etc files provided via ``fs.mounts`` will be overridden with
the ones sanitized by LibOS.

Environment variables
^^^^^^^^^^^^^^^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions libos/include/libos_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ extern struct libos_dentry* g_dentry_root;
int init_fs(void);
int init_mount_root(void);
int init_mount(void);
int mount_etcfs(void);

/* file system operations */

Expand Down
3 changes: 3 additions & 0 deletions libos/include/libos_fs_pseudo.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,6 @@ int sys_print_as_ranges(char* buf, size_t buf_size, size_t count,
int sys_print_as_bitmask(char* buf, size_t buf_size, size_t count,
bool (*is_present)(size_t ind, const void* arg),
const void* callback_arg);

/* etcfs */
int init_etcfs(void);
2 changes: 2 additions & 0 deletions libos/include/libos_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ bool handle_signal(PAL_CONTEXT* context);
*/
long pal_to_unix_errno(long err);

int set_hostname(const char* name, size_t len);

void warn_unsupported_syscall(unsigned long sysno);
void debug_print_syscall_before(unsigned long sysno, ...);
void debug_print_syscall_after(unsigned long sysno, ...);
Expand Down
65 changes: 65 additions & 0 deletions libos/src/fs/etc/fs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/* Copyright (C) 2022 Intel Corporation
* Mariusz Zaborski <[email protected]>
*/

/*
* This file contains the implementation of `etc` FS.
* LibOS assumes that contents of all data obtained from host were already sanitized.
*/

#include "libos_checkpoint.h"
#include "libos_fs.h"
#include "libos_fs_pseudo.h"

static int provide_etc_hostname(struct libos_dentry* dent, char** out_data, size_t* out_size) {
__UNUSED(dent);
/* Use the string (without null terminator) as file data */
size_t size = strlen(g_pal_public_state->hostname);
char* data = malloc(size);
if (!data)
return -ENOMEM;
memcpy(data, g_pal_public_state->hostname, size);
*out_data = data;
*out_size = size;
return 0;
}

int init_etcfs(void) {
pseudo_add_str(NULL, "emulate-etc-hostname", &provide_etc_hostname);
return 0;
}

int mount_etcfs(void) {
if (!g_pal_public_state->emulate_etc_files)
return 0;

return mount_fs(&(struct libos_mount_params){
.type = "pseudo",
.path = "/etc/hostname",
.uri = "emulate-etc-hostname",
});
}

BEGIN_CP_FUNC(etc_info) {
__UNUSED(size);
__UNUSED(obj);
__UNUSED(objp);

/* Propagate hostname */
size_t off = ADD_CP_OFFSET(sizeof(g_pal_public_state->hostname));
char* new_hostname = (char*)(base + off);
memcpy(new_hostname, g_pal_public_state->hostname, sizeof(g_pal_public_state->hostname));

ADD_CP_FUNC_ENTRY(off);
}
END_CP_FUNC(etc_info)

BEGIN_RS_FUNC(etc_info) {
__UNUSED(offset);
__UNUSED(rebase);

const char* hostname = (const char*)(base + GET_CP_FUNC_ENTRY());
memcpy(&g_pal_public_state->hostname, hostname, sizeof(g_pal_public_state->hostname));
}
END_RS_FUNC(etc_info)
7 changes: 6 additions & 1 deletion libos/src/fs/libos_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ int init_fs(void) {
if ((ret = init_sysfs()) < 0)
goto err;

if ((ret = init_etcfs()) < 0)
goto err;

return 0;

err:
Expand Down Expand Up @@ -650,7 +653,9 @@ int init_mount(void) {
}
/* Otherwise `cwd` is already initialized. */

return 0;
/* The mount_etcfs takes precedence over user's fs.mounts, and because of that,
* it has to be called at the end. */
return mount_etcfs();
}

struct libos_fs* find_fs(const char* name) {
Expand Down
4 changes: 4 additions & 0 deletions libos/src/libos_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,10 @@ noreturn void libos_init(const char* const* argv, const char* const* envp) {
* communicates with server over a "loopback" IPC connection. */
RUN_INIT(init_sync_client);

/* XXX: this will break uname checkpointing. */
RUN_INIT(set_hostname, g_pal_public_state->hostname,
strlen(g_pal_public_state->hostname));

log_debug("LibOS initialized");

libos_tcb_t* cur_tcb = libos_get_tcb();
Expand Down
1 change: 1 addition & 0 deletions libos/src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ libos_sources = files(
'fs/chroot/fs.c',
'fs/dev/attestation.c',
'fs/dev/fs.c',
'fs/etc/fs.c',
'fs/eventfd/fs.c',
'fs/libos_dcache.c',
'fs/libos_fs.c',
Expand Down
1 change: 1 addition & 0 deletions libos/src/sys/libos_clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ static BEGIN_MIGRATION_DEF(fork, struct libos_process* process_description,
DEFINE_MIGRATE(brk, NULL, 0);
DEFINE_MIGRATE(loaded_elf_objects, NULL, 0);
DEFINE_MIGRATE(topo_info, NULL, 0);
DEFINE_MIGRATE(etc_info, NULL, 0);
#ifdef DEBUG
DEFINE_MIGRATE(gdb_map, NULL, 0);
#endif
Expand Down
14 changes: 11 additions & 3 deletions libos/src/sys/libos_uname.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,24 @@ long libos_syscall_uname(struct new_utsname* buf) {
return 0;
}

int set_hostname(const char* name, size_t len) {
if (len >= sizeof(g_current_uname.nodename))
return -EINVAL;

memcpy(&g_current_uname.nodename, name, len);
memset(&g_current_uname.nodename[len], 0, sizeof(g_current_uname.nodename) - len);

return 0;
}

long libos_syscall_sethostname(char* name, int len) {
if (len < 0 || (size_t)len >= sizeof(g_current_uname.nodename))
return -EINVAL;

if (!is_user_memory_readable(name, len))
return -EFAULT;

memcpy(&g_current_uname.nodename, name, len);
memset(&g_current_uname.nodename[len], 0, sizeof(g_current_uname.nodename) - len);
return 0;
return set_hostname(name, len);
}

long libos_syscall_setdomainname(char* name, int len) {
Expand Down
101 changes: 101 additions & 0 deletions libos/test/regression/hostname.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#define _DEFAULT_SOURCE BSD /* This is required for gethostname */

#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#include "rw_file.h"

static void test_fork(const char* tag, const char* expected_name,
void (*f)(const char*, const char*)) {
int status;

pid_t pid = fork();
if (pid == -1) {
err(1, "Unable to fork %s", tag);
}

if (pid == 0) {
f(tag, expected_name);
exit(0);
}

if (wait(&status) == -1) {
err(1, "Wait failed %s", tag);
}

if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
err(1, "Test failed %s", tag);
}
}

static void test_gethostname(const char* tag, const char* expected_name) {
char buf[512] = {0};

if (gethostname(buf, sizeof(buf)) != 0) {
err(1, "%s gethostname: failed", tag);
}

if (strcmp(buf, expected_name) != 0) {
errx(1, "%s gethostname result doesn't match hostname (expected: %s, got: %s)",
tag, expected_name, buf);
}
}

static void test_etc_hostname(const char* tag, const char* expected_name) {
char buf[512] = {0};
int fd;

fd = open("/etc/hostname", O_RDONLY);

/*
* If the etc expected name was not provided, assume that /etc/hostname shouldn't exist.
*/
if (strcmp(expected_name, "") == 0) {
if (fd != -1 || errno != ENOENT) {
err(1, "The etc file shouldn't exist, but exists");
}
return;
}

if (fd == -1) {
err(1, "Unable to open /etc/hostname in %s", tag);
}

if (posix_fd_read(fd, buf, sizeof(buf)) < 0)
err(1, "Unable to read /etc/hostname in %s", tag);

/*
* Sometimes /etc/hostname might have a trailing '\n', Gramine is removing it,
* do the same in the test.
*/
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') {
buf[len - 1] = '\0';
}

if (strcmp(buf, expected_name) != 0) {
err(1, "%s /etc/hostname don't have a expected value (expected: %s, got: %s)",
tag, expected_name, buf);
}
}

int main(int argc, char** argv) {
if (argc != 3) {
printf("Usage: %s [hostname] [etc_hostname]\n", argv[0]);
return 1;
}

test_gethostname("normal", argv[1]);
test_etc_hostname("normal etc", argv[2]);
test_fork("fork gethostname", argv[1], test_gethostname);
test_fork("fork etc_hostname", argv[2], test_etc_hostname);

printf("TEST OK\n");
return 0;
}
24 changes: 24 additions & 0 deletions libos/test/regression/hostname_emulate_etc.manifest.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
loader.entrypoint = "file:{{ gramine.libos }}"
libos.entrypoint = "hostname"

loader.env.LD_LIBRARY_PATH = "/lib"
loader.insecure__use_cmdline_argv = true

fs.mounts = [
{ path = "/lib", uri = "file:{{ gramine.runtimedir(libc) }}" },
{ path = "/hostname", uri = "file:{{ binary_dir }}/hostname" },
# dummy file to check that `libos.emulate_etc_files` overrides
# /etc/hostname unconditionally
{ path = "/etc/hostname", uri = "file:{{ binary_dir }}/hostname" },
]

sgx.debug = true
sgx.nonpie_binary = true

sgx.trusted_files = [
"file:{{ gramine.libos }}",
"file:{{ gramine.runtimedir(libc) }}/",
"file:{{ binary_dir }}/hostname",
]

libos.emulate_etc_files = true
1 change: 1 addition & 0 deletions libos/test/regression/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ tests = {
'groups': {},
'helloworld': {},
'host_root_fs': {},
'hostname': {},
'init_fail': {},
'keys': {},
'kill_all': {},
Expand Down
31 changes: 6 additions & 25 deletions libos/test/regression/rename_unlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <unistd.h>

#include "common.h"
#include "rw_file.h"

static const char message1[] = "first message\n";
static const size_t message1_len = sizeof(message1) - 1;
Expand All @@ -44,29 +45,6 @@ static int write_all(int fd, const char* str, size_t size) {
return 0;
}

static int read_all(int fd, char* str, size_t size) {
while (size > 0) {
ssize_t n = read(fd, str, size);
/* Treat EINTR as error: we don't expect it because the test doesn't use any signal
* handlers. */
if (n == -1) {
warn("read");
return -1;
}
if (n == 0) {
if (size > 0) {
warnx("read less bytes than expected");
return -1;
}
break;
}
assert(n >= 0 && (size_t)n <= size);
size -= n;
str += n;
}
return 0;
}

static void should_not_exist(const char* path) {
struct stat statbuf;

Expand Down Expand Up @@ -107,8 +85,11 @@ static void should_contain(const char* desc, int fd, const char* str, size_t len
if (lseek(fd, 0, SEEK_SET) == -1)
err(1, "%s: lseek", desc);

if (read_all(fd, buffer, len) == -1)
errx(1, "%s: read_all failed", desc);
ssize_t n = posix_fd_read(fd, buffer, len);
if (n < 0)
errx(1, "%s: read_fd failed", desc);
if ((size_t)n != len)
warnx("%s: read less bytes than expected", desc);
if (memcmp(buffer, str, len) != 0)
errx(1, "%s: wrong content", desc);

Expand Down
Loading