libc: execve

This patch implements 'execve' in Genode's libc.

The mechanism relies on the dynamic linker's ability to replace the
loaded binary while keeping crucial libraries - in particular the libc -
intact. The state outside the libc is wiped. For this reason, all libc
internal state needed beyond the 'execve' call must be allocated on a
heap separate from the application-owned malloc heap. E.g.,
libc-internal file-descriptor objects must not be allocated or refer to
any memory object allocated from the malloc heap.

Issue #3481
This commit is contained in:
Norman Feske 2019-08-20 12:18:54 +02:00 committed by Christian Helmuth
parent 2a3cebdd6e
commit 6894ced63b
16 changed files with 527 additions and 146 deletions

View File

@ -63,21 +63,7 @@ namespace Libc {
Id_space::Id id)
: _elem(*this, id_space, id), plugin(&plugin), context(&context) { }
void path(char const *newpath)
{
if (fd_path) { Genode::warning("may leak former FD path memory"); }
if (newpath) {
Genode::size_t const path_size = ::strlen(newpath) + 1;
char *buf = (char*)malloc(path_size);
if (!buf) {
Genode::error("could not allocate path buffer for libc_fd ", libc_fd);
return;
}
::memcpy(buf, newpath, path_size);
fd_path = buf;
} else
fd_path = 0;
}
void path(char const *newpath);
};

View File

@ -15,6 +15,7 @@
#define _LIBC_PLUGIN__PLUGIN_H_
#include <os/path.h>
#include <base/exception.h>
#include <util/list.h>
#include <netdb.h>
@ -32,9 +33,12 @@ namespace Libc {
class File_descriptor;
typedef Genode::Path<PATH_MAX> Absolute_path;
class Symlink_resolve_error : Genode::Exception { };
void resolve_symlinks(char const *path, Absolute_path &resolved_path);
class Plugin : public List<Plugin>::Element
{
protected:
@ -54,8 +58,6 @@ namespace Libc {
virtual int priority();
virtual bool supports_access(char const *path, int amode);
virtual bool supports_execve(char const *filename, char *const argv[],
char *const envp[]);
virtual bool supports_mkdir(const char *path, mode_t mode);
virtual bool supports_open(const char *pathname, int flags);
virtual bool supports_pipe();
@ -92,8 +94,6 @@ namespace Libc {
socklen_t addrlen);
virtual File_descriptor *dup(File_descriptor*);
virtual int dup2(File_descriptor *, File_descriptor *new_fd);
virtual int execve(char const *filename, char *const argv[],
char *const envp[]);
virtual int fstatfs(File_descriptor *, struct statfs *buf);
virtual int fcntl(File_descriptor *, int cmd, long arg);
virtual int fstat(File_descriptor *, struct stat *buf);

View File

@ -30,8 +30,6 @@ namespace Libc {
struct Libc::Plugin_registry : List<Plugin>
{
Plugin *get_plugin_for_access(char const *pathname, int amode);
Plugin *get_plugin_for_execve(char const *filename, char *const argv[],
char *const envp[]);
Plugin *get_plugin_for_mkdir(const char *path, mode_t mode);
Plugin *get_plugin_for_open(const char *pathname, int flags);
Plugin *get_plugin_for_pipe();

View File

@ -18,7 +18,7 @@ SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc \
pread_pwrite.cc readv_writev.cc poll.cc \
vfs_plugin.cc dynamic_linker.cc signal.cc \
socket_operations.cc task.cc socket_fs_plugin.cc syscall.cc \
getpwent.cc getrandom.cc fork.cc
getpwent.cc getrandom.cc fork.cc execve.cc
#
# Pthreads

View File

@ -0,0 +1,32 @@
build { core init test/execve }
create_boot_directory
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="LOG"/>
<service name="CPU"/>
<service name="PD"/>
</parent-provides>
<start name="test-execve" caps="300">
<resource name="RAM" quantum="1G"/>
<config ld_verbose="yes">
<arg value="name_of_executeable"/>
<arg value="100"/>
<libc stdin="/null" stdout="/log" stderr="/log"/>
<vfs> <null/> <log/> </vfs>
</config>
<route> <any-service> <parent/> </any-service> </route>
</start>
</config>
}
build_boot_image {
core init ld.lib.so libc.lib.so vfs.lib.so libm.lib.so posix.lib.so test-execve
}
append qemu_args " -nographic "
run_genode_until "child.*exited.*value 0.*\n" 15

View File

@ -0,0 +1,203 @@
/*
* \brief Libc execve mechanism
* \author Norman Feske
* \date 2019-08-20
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/shared_object.h>
#include <base/log.h>
/* libc includes */
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
/* libc-internal includes */
#include <libc/allocator.h>
#include <internal/call_func.h>
#include <libc_init.h>
#include <libc_errno.h>
#include <task.h>
using namespace Genode;
typedef int (*main_fn_ptr) (int, char **, char **);
namespace Libc { struct String_array; }
/**
* Utility to capture the state of argv or envp string arrays
*/
struct Libc::String_array : Noncopyable
{
typedef Genode::Allocator Allocator;
Allocator &_alloc;
static unsigned _num_entries(char const * const * const array)
{
unsigned i = 0;
for (; array && array[i]; i++);
return i;
}
unsigned const count;
size_t const _array_bytes = sizeof(char *)*(count + 1);
char ** const array = (char **)_alloc.alloc(_array_bytes);
struct Buffer
{
Allocator &_alloc;
size_t const _size;
char * const _base = (char *)_alloc.alloc(_size);
unsigned _pos = 0;
Buffer(Allocator &alloc, size_t size)
: _alloc(alloc), _size(size) { }
~Buffer() { _alloc.free(_base, _size); }
bool try_append(char const *s)
{
size_t const len = ::strlen(s) + 1;
if (_pos + len > _size)
return false;
Genode::strncpy(_base + _pos, s, len);
_pos += len;
return true;
}
char *pos_ptr() { return _base + _pos; }
};
Constructible<Buffer> _buffer { };
String_array(Allocator &alloc, char const * const * const src_array)
:
_alloc(alloc), count(_num_entries(src_array))
{
/* marshal array strings to buffer */
size_t size = 1024;
for (;;) {
_buffer.construct(alloc, size);
unsigned i = 0;
for (i = 0; i < count; i++) {
array[i] = _buffer->pos_ptr();
if (!_buffer->try_append(src_array[i]))
break;
}
bool const done = (i == count);
if (done) {
array[i] = nullptr;
break;
}
warning("env buffer ", size, " too small");
size += 1024;
}
}
~String_array() { _alloc.free(array, _array_bytes); }
};
/* pointer to environment, provided by libc */
extern char **environ;
static Env *_env_ptr;
static Allocator *_alloc_ptr;
static void *_user_stack_ptr;
static main_fn_ptr _main_ptr;
static Libc::String_array *_env_vars_ptr;
static Libc::String_array *_args_ptr;
static Libc::Reset_malloc_heap *_reset_malloc_heap_ptr;
void Libc::init_execve(Env &env, Genode::Allocator &alloc, void *user_stack_ptr,
Reset_malloc_heap &reset_malloc_heap)
{
_env_ptr = &env;
_alloc_ptr = &alloc;
_user_stack_ptr = user_stack_ptr;
_reset_malloc_heap_ptr = &reset_malloc_heap;
Dynamic_linker::keep(env, "libc.lib.so");
Dynamic_linker::keep(env, "libm.lib.so");
Dynamic_linker::keep(env, "posix.lib.so");
Dynamic_linker::keep(env, "vfs.lib.so");
}
static void user_entry(void *)
{
_env_ptr->exec_static_constructors();
exit((*_main_ptr)(_args_ptr->count, _args_ptr->array, _env_vars_ptr->array));
}
extern "C" int execve(char const *, char *const[], char *const[]) __attribute__((weak));
extern "C" int execve(char const *filename,
char *const argv[], char *const envp[])
{
if (!_env_ptr || !_alloc_ptr) {
error("missing call of 'init_execve'");
return Libc::Errno(EACCES);
}
/* capture environment variables and args to libc-internal heap */
Libc::String_array *saved_env_vars =
new (*_alloc_ptr) Libc::String_array(*_alloc_ptr, envp);
Libc::String_array *saved_args =
new (*_alloc_ptr) Libc::String_array(*_alloc_ptr, argv);
try {
_main_ptr = Dynamic_linker::respawn<main_fn_ptr>(*_env_ptr, filename, "main");
}
catch (Dynamic_linker::Invalid_symbol) {
error("Dynamic_linker::respawn could not obtain binary entry point");
return Libc::Errno(EACCES);
}
catch (Dynamic_linker::Invalid_rom_module) {
error("Dynamic_linker::respawn could not access binary ROM");
return Libc::Errno(EACCES);
}
/*
* Reconstruct malloc heap for application-owned data
*/
_reset_malloc_heap_ptr->reset_malloc_heap();
Libc::Allocator app_heap { };
_env_vars_ptr = new (app_heap) Libc::String_array(app_heap, saved_env_vars->array);
_args_ptr = new (app_heap) Libc::String_array(app_heap, saved_args->array);
/* register list of environment variables at libc 'environ' pointer */
environ = _env_vars_ptr->array;
destroy(_alloc_ptr, saved_env_vars);
destroy(_alloc_ptr, saved_args);
call_func(_user_stack_ptr, (void *)user_entry, nullptr);
}
extern "C" int _execve(char const *, char *const[], char *const[]) __attribute__((weak, alias("execve")));

View File

@ -16,7 +16,6 @@
#include <util/construct_at.h>
#include <base/env.h>
#include <base/log.h>
#include <libc/allocator.h>
/* libc plugin interface */
#include <libc-plugin/fd_alloc.h>
@ -25,26 +24,31 @@
#include <fcntl.h>
#include <unistd.h>
/* libc-internal includes */
#include <libc_init.h>
#include <base/internal/unmanaged_singleton.h>
using namespace Libc;
using namespace Genode;
static Allocator *_alloc_ptr;
void Libc::init_fd_alloc(Allocator &alloc) { _alloc_ptr = &alloc; }
File_descriptor_allocator *Libc::file_descriptor_allocator()
{
static bool constructed = false;
static char placeholder[sizeof(File_descriptor_allocator)];
static Libc::Allocator md_alloc;
if (_alloc_ptr)
return unmanaged_singleton<File_descriptor_allocator>(*_alloc_ptr);
if (!constructed) {
Genode::construct_at<File_descriptor_allocator>(placeholder, md_alloc);
constructed = true;
}
return reinterpret_cast<File_descriptor_allocator *>(placeholder);
error("missing call of 'init_fd_alloc'");
return nullptr;
}
File_descriptor_allocator::File_descriptor_allocator(Genode::Allocator &alloc)
File_descriptor_allocator::File_descriptor_allocator(Allocator &alloc)
: _alloc(alloc)
{ }
@ -68,9 +72,10 @@ void File_descriptor_allocator::free(File_descriptor *fdo)
{
Lock::Guard guard(_lock);
::free((void *)fdo->fd_path);
if (fdo->fd_path)
_alloc.free((void *)fdo->fd_path, ::strlen(fdo->fd_path) + 1);
Genode::destroy(_alloc, fdo);
destroy(_alloc, fdo);
}
@ -131,6 +136,30 @@ void File_descriptor_allocator::generate_info(Xml_generator &xml)
}
void File_descriptor::path(char const *newpath)
{
if (fd_path)
warning("may leak former FD path memory");
if (!_alloc_ptr) {
error("missing call of 'init_fd_alloc'");
return;
}
if (newpath) {
Genode::size_t const path_size = ::strlen(newpath) + 1;
char *buf = (char*)_alloc_ptr->alloc(path_size);
if (!buf) {
error("could not allocate path buffer for libc_fd ", libc_fd);
return;
}
::memcpy(buf, newpath, path_size);
fd_path = buf;
} else
fd_path = 0;
}
/********************
** Libc functions **
********************/

View File

@ -92,11 +92,7 @@ typedef Genode::Token<Scanner_policy_path_element> Path_element_token;
/**
* Resolve symbolic links in a given absolute path
*/
/* exception */
class Symlink_resolve_error { };
static void resolve_symlinks(char const *path, Absolute_path &resolved_path)
void Libc::resolve_symlinks(char const *path, Absolute_path &resolved_path)
{
char path_element[PATH_MAX];
char symlink_target[PATH_MAX];
@ -283,23 +279,6 @@ extern "C" __attribute__((alias("dup2")))
int _dup2(int libc_fd, int new_libc_fd);
extern "C" int execve(char const *filename, char *const argv[],
char *const envp[])
{
try {
Absolute_path resolved_path;
resolve_symlinks(filename, resolved_path);
FNAME_FUNC_WRAPPER(execve, resolved_path.base(), argv, envp);
} catch (Symlink_resolve_error) {
return -1;
}
}
extern "C" __attribute__((alias("execve")))
int _execve(char const *, char *const [], char *const []);
extern "C" int fchdir(int libc_fd)
{
File_descriptor *fd = libc_fd_to_fd(libc_fd, "fchdir");

View File

@ -29,6 +29,11 @@ namespace Libc {
*/
void init_dl(Genode::Env &env);
/**
* File-descriptor allocator
*/
void init_fd_alloc(Genode::Allocator &);
/**
* Global memory allocator
*/
@ -49,8 +54,9 @@ namespace Libc {
/**
* Malloc allocator
*/
void init_malloc(Genode::Allocator &heap);
void init_malloc(Genode::Allocator &);
void init_malloc_cloned(Clone_connection &);
void reinit_malloc(Genode::Allocator &);
/**
* Allow thread.cc to access the 'Genode::Env' (needed for the
@ -68,6 +74,17 @@ namespace Libc {
*/
void init_fork(Genode::Env &, Config_accessor const &,
Genode::Allocator &heap, Genode::Heap &malloc_heap, int pid);
struct Reset_malloc_heap : Genode::Interface
{
virtual void reset_malloc_heap() = 0;
};
/**
* Execve mechanism
*/
void init_execve(Genode::Env &, Genode::Allocator &, void *user_stack,
Reset_malloc_heap &);
}
#endif /* _LIBC_INIT_H_ */

View File

@ -284,3 +284,11 @@ void Libc::init_malloc_cloned(Clone_connection &clone_connection)
mallocator = constructible_malloc().operator->();
}
void Libc::reinit_malloc(Genode::Allocator &heap)
{
Libc::Malloc &malloc = *constructible_malloc();
Genode::construct_at<Libc::Malloc>(&malloc, heap);
}

View File

@ -57,13 +57,6 @@ bool Plugin::supports_access(char const *path, int amode)
}
bool Plugin::supports_execve(char const *filename, char *const argv[],
char *const envp[])
{
return false;
}
bool Plugin::supports_mkdir(const char *, mode_t)
{
return false;
@ -197,7 +190,6 @@ DUMMY(ssize_t, -1, write, (File_descriptor *, const void *, ::size_t));
* Misc
*/
DUMMY(int, -1, access, (char const *, int));
DUMMY(int, -1, execve, (char const *, char *const[], char *const[]));
DUMMY(int, -1, mkdir, (const char*, mode_t));
DUMMY(void *, (void *)(-1), mmap, (void *addr, ::size_t length, int prot, int flags,
File_descriptor *, ::off_t offset));

View File

@ -40,10 +40,6 @@ using namespace Libc;
Plugin *Plugin_registry::get_plugin_for_access(char const *path, int amode) {
GET_PLUGIN_FOR(access, path, amode) }
Plugin *Plugin_registry::get_plugin_for_execve(char const *filename, char *const argv[],
char *const envp[]) {
GET_PLUGIN_FOR(execve, filename, argv, envp) }
Plugin *Plugin_registry::get_plugin_for_mkdir(const char *path, mode_t mode) {
GET_PLUGIN_FOR(mkdir, path, mode) }

View File

@ -41,6 +41,7 @@ extern char **environ;
namespace Libc {
class Env_implementation;
class Cloned_malloc_heap_range;
class Malloc_ram_allocator;
class Kernel;
class Pthreads;
class Timer;
@ -371,6 +372,59 @@ struct Libc::Cloned_malloc_heap_range
};
/*
* Utility for tracking the allocation of dataspaces by the malloc heap
*/
struct Libc::Malloc_ram_allocator : Genode::Ram_allocator
{
Genode::Allocator &_md_alloc;
Genode::Ram_allocator &_ram;
struct Dataspace
{
Genode::Ram_dataspace_capability cap;
Dataspace(Genode::Ram_dataspace_capability cap) : cap(cap) { }
virtual ~Dataspace() { }
};
Genode::Registry<Genode::Registered<Dataspace> > _dataspaces { };
void _release(Genode::Registered<Dataspace> &ds)
{
_ram.free(ds.cap);
destroy(_md_alloc, &ds);
}
Malloc_ram_allocator(Allocator &md_alloc, Ram_allocator &ram)
: _md_alloc(md_alloc), _ram(ram) { }
~Malloc_ram_allocator()
{
_dataspaces.for_each([&] (Registered<Dataspace> &ds) {
_release(ds); });
}
Ram_dataspace_capability alloc(size_t size, Cache_attribute cached) override
{
Ram_dataspace_capability cap = _ram.alloc(size, cached);
new (_md_alloc) Registered<Dataspace>(_dataspaces, cap);
return cap;
}
void free(Ram_dataspace_capability ds_cap) override
{
_dataspaces.for_each([&] (Registered<Dataspace> &ds) {
if (ds_cap == ds.cap)
_release(ds); });
}
size_t dataspace_size(Ram_dataspace_capability ds_cap) const override
{
return _ram.dataspace_size(ds_cap);
}
};
/**
* Libc "kernel"
*
@ -382,17 +436,36 @@ struct Libc::Cloned_malloc_heap_range
* setjmp/longjmp.
*/
struct Libc::Kernel final : Vfs::Io_response_handler,
Genode::Entrypoint::Io_progress_handler
Genode::Entrypoint::Io_progress_handler,
Reset_malloc_heap
{
private:
Genode::Env &_env;
Genode::Env &_env;
/**
* Allocator for libc-internal data
*
* Not mirrored to forked processes. Preserved across 'execve' calls.
*/
Genode::Allocator &_heap;
/**
* Allocator for application-owned data
*
* Mirrored to forked processes. Not preserved across 'execve' calls.
*/
Genode::Reconstructible<Malloc_ram_allocator> _malloc_ram { _heap, _env.ram() };
Genode::Constructible<Heap> _malloc_heap { };
Genode::Registry<Registered<Cloned_malloc_heap_range> > _cloned_heap_ranges { };
/**
* Reset_malloc_heap interface used by execve
*/
void reset_malloc_heap() override;
Env_implementation _libc_env { _env, _heap };
Vfs_plugin _vfs { _libc_env, _heap, *this };
@ -638,11 +711,12 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
_clone_state_from_parent();
} else {
_malloc_heap.construct(_env.ram(), _env.rm());
_malloc_heap.construct(*_malloc_ram, _env.rm());
init_malloc(*_malloc_heap);
}
Libc::init_fork(_env, _libc_env, _heap, *_malloc_heap, _pid);
Libc::init_execve(_env, _heap, _user_stack, *this);
_init_file_descriptors();
}
@ -920,6 +994,20 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
};
void Libc::Kernel::reset_malloc_heap()
{
_malloc_ram.construct(_heap, _env.ram());
_cloned_heap_ranges.for_each([&] (Registered<Cloned_malloc_heap_range> &r) {
destroy(_heap, &r); });
Heap &raw_malloc_heap = *_malloc_heap;
Genode::construct_at<Heap>(&raw_malloc_heap, *_malloc_ram, _env.rm());
reinit_malloc(raw_malloc_heap);
}
void Libc::Kernel::_init_file_descriptors()
{
auto init_fd = [&] (Genode::Xml_node const &node, char const *attr,
@ -947,12 +1035,15 @@ void Libc::Kernel::_init_file_descriptors()
* We need to manually register the path. Normally this is done
* by '_open'. But we call the local 'open' function directly
* because we want to explicitly specify the libc fd ID.
*
* We have to allocate the path from the libc (done via 'strdup')
* such that the path can be freed when an stdio fd is closed.
*/
if (fd->fd_path) { Genode::warning("may leak former FD path memory"); }
fd->fd_path = strdup(path.string());
if (fd->fd_path)
Genode::warning("may leak former FD path memory");
{
char *dst = (char *)_heap.alloc(path.length());
Genode::strncpy(dst, path.string(), path.length());
fd->fd_path = dst;
}
::off_t const seek = node.attribute_value("seek", 0ULL);
if (seek)
@ -1215,6 +1306,7 @@ void Component::construct(Genode::Env &env)
*unmanaged_singleton<Genode::Heap>(env.ram(), env.rm());
/* pass Genode::Env to libc subsystems that depend on it */
Libc::init_fd_alloc(heap);
Libc::init_mem_alloc(env);
Libc::init_dl(env);
Libc::sysctl_init(env);

View File

@ -0,0 +1,5 @@
TARGET = test-execve
SRC_CC = test.cc
LIBS = posix
CC_CXX_WARN_STRICT =

View File

@ -0,0 +1,37 @@
/*
* \brief Simple execve test
* \author Norman Feske
* \date 2019-08-20
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc <= 1)
return -1;
int const count = atoi(argv[1]);
printf("count %d\n", count);
if (count <= 0)
return 0;
{
char argv0[20];
char argv1[20];
snprintf(argv0, sizeof(argv0), "test-execve");
snprintf(argv1, sizeof(argv1), "%d", count - 1);
char *argv[] { argv0, argv1, NULL };
execve("test-execve", argv, NULL);
}
printf("This code should never be reached.\n");
return -1;
}

View File

@ -712,6 +712,80 @@ extern "C" pid_t waitpid(pid_t pid, int *istat, int options)
}
extern "C" int execve(char const *filename, char *const argv[],
char *const envp[])
{
if (verbose) {
log(__func__, ": filename=", filename);
for (int i = 0; argv[i]; i++)
log(__func__, "argv[", i, "]='", Genode::Cstring(argv[i]), "'");
for (int i = 0; envp[i]; i++)
log(__func__, "envp[", i, "]='", Genode::Cstring(envp[i]), "'");
}
Libc::Absolute_path resolved_path;
try {
Libc::resolve_symlinks(filename, resolved_path);
} catch (Libc::Symlink_resolve_error) {
return -1;
}
Genode::strncpy(sysio()->execve_in.filename, resolved_path.string(),
sizeof(sysio()->execve_in.filename));
if (!serialize_string_array(argv, sysio()->execve_in.args,
sizeof(sysio()->execve_in.args))) {
error("execve: argument buffer exceeded");
errno = E2BIG;
return -1;
}
/* communicate the current working directory as environment variable */
size_t noux_cwd_len = Genode::snprintf(sysio()->execve_in.env,
sizeof(sysio()->execve_in.env),
"NOUX_CWD=");
if (!getcwd(&(sysio()->execve_in.env[noux_cwd_len]),
sizeof(sysio()->execve_in.env) - noux_cwd_len)) {
error("execve: environment buffer exceeded");
errno = E2BIG;
return -1;
}
noux_cwd_len = strlen(sysio()->execve_in.env) + 1;
if (!serialize_string_array(envp, &(sysio()->execve_in.env[noux_cwd_len]),
sizeof(sysio()->execve_in.env) - noux_cwd_len)) {
error("execve: environment buffer exceeded");
errno = E2BIG;
return -1;
}
if (!noux_syscall(Noux::Session::SYSCALL_EXECVE)) {
warning("exec syscall failed for path \"", filename, "\"");
switch (sysio()->error.execve) {
case Noux::Sysio::EXECVE_ERR_NO_ENTRY: errno = ENOENT; break;
case Noux::Sysio::EXECVE_ERR_NO_MEMORY: errno = ENOMEM; break;
case Noux::Sysio::EXECVE_ERR_NO_EXEC: errno = ENOEXEC; break;
case Noux::Sysio::EXECVE_ERR_ACCESS: errno = EACCES; break;
}
return -1;
}
/*
* In the success case, we never return from execve, the execution is
* resumed in the new program.
*/
Genode::sleep_forever();
return 0;
}
extern "C" int _execve(char const *, char *const[], char *const[]) __attribute__((alias("execve")));
int getrusage(int who, struct rusage *usage)
{
if (verbose)
@ -983,8 +1057,6 @@ namespace {
void init(Genode::Env &);
bool supports_access(const char *, int) { return true; }
bool supports_execve(char const *, char *const[],
char *const[]) { return true; }
bool supports_open(char const *, int) { return true; }
bool supports_stat(char const *) { return true; }
bool supports_symlink(char const *, char const*) { return true; }
@ -1003,8 +1075,6 @@ namespace {
int close(Libc::File_descriptor *);
Libc::File_descriptor *dup(Libc::File_descriptor*);
int dup2(Libc::File_descriptor *, Libc::File_descriptor *);
int execve(char const *filename, char *const argv[],
char *const envp[]);
int fstat(Libc::File_descriptor *, struct stat *);
int fsync(Libc::File_descriptor *);
int fstatfs(Libc::File_descriptor *, struct statfs *);
@ -1067,69 +1137,6 @@ namespace {
}
int Plugin::execve(char const *filename, char *const argv[],
char *const envp[])
{
if (verbose) {
log(__func__, ": filename=", filename);
for (int i = 0; argv[i]; i++)
log(__func__, "argv[", i, "]='", Genode::Cstring(argv[i]), "'");
for (int i = 0; envp[i]; i++)
log(__func__, "envp[", i, "]='", Genode::Cstring(envp[i]), "'");
}
Genode::strncpy(sysio()->execve_in.filename, filename, sizeof(sysio()->execve_in.filename));
if (!serialize_string_array(argv, sysio()->execve_in.args,
sizeof(sysio()->execve_in.args))) {
error("execve: argument buffer exceeded");
errno = E2BIG;
return -1;
}
/* communicate the current working directory as environment variable */
size_t noux_cwd_len = Genode::snprintf(sysio()->execve_in.env,
sizeof(sysio()->execve_in.env),
"NOUX_CWD=");
if (!getcwd(&(sysio()->execve_in.env[noux_cwd_len]),
sizeof(sysio()->execve_in.env) - noux_cwd_len)) {
error("execve: environment buffer exceeded");
errno = E2BIG;
return -1;
}
noux_cwd_len = strlen(sysio()->execve_in.env) + 1;
if (!serialize_string_array(envp, &(sysio()->execve_in.env[noux_cwd_len]),
sizeof(sysio()->execve_in.env) - noux_cwd_len)) {
error("execve: environment buffer exceeded");
errno = E2BIG;
return -1;
}
if (!noux_syscall(Noux::Session::SYSCALL_EXECVE)) {
warning("exec syscall failed for path \"", filename, "\"");
switch (sysio()->error.execve) {
case Noux::Sysio::EXECVE_ERR_NO_ENTRY: errno = ENOENT; break;
case Noux::Sysio::EXECVE_ERR_NO_MEMORY: errno = ENOMEM; break;
case Noux::Sysio::EXECVE_ERR_NO_EXEC: errno = ENOEXEC; break;
case Noux::Sysio::EXECVE_ERR_ACCESS: errno = EACCES; break;
}
return -1;
}
/*
* In the success case, we never return from execve, the execution is
* resumed in the new program.
*/
Genode::sleep_forever();
return 0;
}
int Plugin::stat(char const *path, struct stat *buf)
{
if (verbose)