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 6 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
18 changes: 18 additions & 0 deletions Documentation/manifest-syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ 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> ]``.

``/etc`` passthrough
^^^^^^^^^^^^^^^^^^^^

::

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

This specifies whether to passthrough extra runtime files from host's ``/etc``.
Before the files are available to the application, they will be sanitized.
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 init_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(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
71 changes: 71 additions & 0 deletions libos/src/fs/etc/fs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* 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 etc files 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, "etc-passthrough-hostname", &provide_etc_hostname);
return 0;
}

int init_mount_etcfs(void) {
int ret;

if (!g_pal_public_state->passthrough_etc_files)
return 0;

ret = mount_fs(&(struct libos_mount_params){
.type = "pseudo",
.path = "/etc/hostname",
.uri = "etc-passthrough-hostname",
});
if (ret < 0)
return ret;

return 0;
}

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)
3 changes: 3 additions & 0 deletions 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
6 changes: 6 additions & 0 deletions libos/src/libos_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@ noreturn void libos_init(const char* const* argv, const char* const* envp) {
RUN_INIT(init_threading);
RUN_INIT(init_mount);
RUN_INIT(init_std_handles);
/* The init_mount_etcfs takes precedence over user's fs.mounts, and because of that,
* it has to be called after init_mount. */
RUN_INIT(init_mount_etcfs);

char** expanded_argv = NULL;
RUN_INIT(init_exec_handle, argv, &expanded_argv);
Expand Down Expand Up @@ -487,6 +490,9 @@ 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);

RUN_INIT(set_hostname, g_pal_public_state->hostname,
strlen(g_pal_public_state->hostname) + 1);

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(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
110 changes: 110 additions & 0 deletions libos/test/regression/hostname.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#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>

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);
}

off_t offset = 0;
size_t size = sizeof(buf);
while (size > 0) {
ssize_t ret = read(fd, buf + offset, size);
if (ret < 0 && errno == EINTR)
continue;
if (ret < 0)
err(1, "Unable to read /etc/hostname in %s", tag);
if (ret == 0)
break;
size -= ret;
offset += ret;
}

/*
* 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_pass_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.passthrough_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.passthrough_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
16 changes: 16 additions & 0 deletions libos/test/regression/test_libos.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import shutil
import signal
import socket
import subprocess
import unittest

Expand Down Expand Up @@ -1119,6 +1120,21 @@ def test_060_synthetic(self):
stdout, _ = self.run_binary(['synthetic'])
self.assertIn("TEST OK", stdout)

def test_070_hostname_localhost(self):
# The generic manifest (manifest.template) doesn't contain etc passthrough.
# This means that the /etc/hostname file doesn't exist.
# To test it, we pass the empty second argument.
stdout, _ = self.run_binary(['hostname', 'localhost', ''])
self.assertIn("TEST OK", stdout)

def test_071_hostname_pass_etc(self):
stdout, _ = self.run_binary([
'hostname_pass_etc',
socket.gethostname(),
socket.gethostname(),
])
self.assertIn("TEST OK", stdout)


class TC_50_GDB(RegressionTestCase):
def setUp(self):
Expand Down
2 changes: 2 additions & 0 deletions libos/test/regression/tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ manifests = [
"gettimeofday",
"groups",
"helloworld",
"hostname",
"hostname_pass_etc",
"host_root_fs",
"init_fail",
"keys",
Expand Down
2 changes: 2 additions & 0 deletions libos/test/regression/tests_musl.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ manifests = [
"gettimeofday",
"groups",
"helloworld",
"hostname",
"hostname_pass_etc",
"host_root_fs",
"init_fail",
"keys",
Expand Down
10 changes: 10 additions & 0 deletions pal/include/host/linux-common/etc_host_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/* Copyright (C) 2022 Intel Corporation
* Mariusz Zaborski <[email protected]>
*/

#pragma once

#include "pal.h"

int get_hostname(char* hostname, size_t size);
Loading