From 6894ced63b40003764e8a9a2ad7d316b968563a5 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 20 Aug 2019 12:18:54 +0200 Subject: [PATCH] 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 --- repos/libports/include/libc-plugin/fd_alloc.h | 16 +- repos/libports/include/libc-plugin/plugin.h | 10 +- .../include/libc-plugin/plugin_registry.h | 2 - repos/libports/lib/mk/libc.mk | 2 +- repos/libports/run/execve.run | 32 +++ repos/libports/src/lib/libc/execve.cc | 203 ++++++++++++++++++ repos/libports/src/lib/libc/fd_alloc.cc | 55 +++-- .../libports/src/lib/libc/file_operations.cc | 23 +- repos/libports/src/lib/libc/libc_init.h | 19 +- repos/libports/src/lib/libc/malloc.cc | 8 + repos/libports/src/lib/libc/plugin.cc | 8 - .../libports/src/lib/libc/plugin_registry.cc | 4 - repos/libports/src/lib/libc/task.cc | 108 +++++++++- repos/libports/src/test/execve/target.mk | 5 + repos/libports/src/test/execve/test.cc | 37 ++++ repos/ports/src/lib/libc_noux/plugin.cc | 141 ++++++------ 16 files changed, 527 insertions(+), 146 deletions(-) create mode 100644 repos/libports/run/execve.run create mode 100644 repos/libports/src/lib/libc/execve.cc create mode 100644 repos/libports/src/test/execve/target.mk create mode 100644 repos/libports/src/test/execve/test.cc diff --git a/repos/libports/include/libc-plugin/fd_alloc.h b/repos/libports/include/libc-plugin/fd_alloc.h index b768c0666..743c9df13 100644 --- a/repos/libports/include/libc-plugin/fd_alloc.h +++ b/repos/libports/include/libc-plugin/fd_alloc.h @@ -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); }; diff --git a/repos/libports/include/libc-plugin/plugin.h b/repos/libports/include/libc-plugin/plugin.h index 12b0cf6ef..561729b07 100644 --- a/repos/libports/include/libc-plugin/plugin.h +++ b/repos/libports/include/libc-plugin/plugin.h @@ -15,6 +15,7 @@ #define _LIBC_PLUGIN__PLUGIN_H_ #include +#include #include #include @@ -32,9 +33,12 @@ namespace Libc { class File_descriptor; - typedef Genode::Path Absolute_path; + class Symlink_resolve_error : Genode::Exception { }; + + void resolve_symlinks(char const *path, Absolute_path &resolved_path); + class Plugin : public List::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); diff --git a/repos/libports/include/libc-plugin/plugin_registry.h b/repos/libports/include/libc-plugin/plugin_registry.h index e9302f633..931e65b53 100644 --- a/repos/libports/include/libc-plugin/plugin_registry.h +++ b/repos/libports/include/libc-plugin/plugin_registry.h @@ -30,8 +30,6 @@ namespace Libc { struct Libc::Plugin_registry : List { 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(); diff --git a/repos/libports/lib/mk/libc.mk b/repos/libports/lib/mk/libc.mk index e4a064adb..e9596baf6 100644 --- a/repos/libports/lib/mk/libc.mk +++ b/repos/libports/lib/mk/libc.mk @@ -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 diff --git a/repos/libports/run/execve.run b/repos/libports/run/execve.run new file mode 100644 index 000000000..424c32dab --- /dev/null +++ b/repos/libports/run/execve.run @@ -0,0 +1,32 @@ +build { core init test/execve } + +create_boot_directory + +install_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 diff --git a/repos/libports/src/lib/libc/execve.cc b/repos/libports/src/lib/libc/execve.cc new file mode 100644 index 000000000..6ad1a0c84 --- /dev/null +++ b/repos/libports/src/lib/libc/execve.cc @@ -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 +#include + +/* libc includes */ +#include +#include +#include + +/* libc-internal includes */ +#include +#include +#include +#include +#include + +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 { }; + + 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(*_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"))); diff --git a/repos/libports/src/lib/libc/fd_alloc.cc b/repos/libports/src/lib/libc/fd_alloc.cc index 480486d35..dffe77904 100644 --- a/repos/libports/src/lib/libc/fd_alloc.cc +++ b/repos/libports/src/lib/libc/fd_alloc.cc @@ -16,7 +16,6 @@ #include #include #include -#include /* libc plugin interface */ #include @@ -25,26 +24,31 @@ #include #include +/* libc-internal includes */ +#include +#include + 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(*_alloc_ptr); - if (!constructed) { - Genode::construct_at(placeholder, md_alloc); - constructed = true; - } - - return reinterpret_cast(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 ** ********************/ diff --git a/repos/libports/src/lib/libc/file_operations.cc b/repos/libports/src/lib/libc/file_operations.cc index 2ebb6eaba..a45a62e63 100644 --- a/repos/libports/src/lib/libc/file_operations.cc +++ b/repos/libports/src/lib/libc/file_operations.cc @@ -92,11 +92,7 @@ typedef Genode::Token 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"); diff --git a/repos/libports/src/lib/libc/libc_init.h b/repos/libports/src/lib/libc/libc_init.h index e0fe830dc..0588fce35 100644 --- a/repos/libports/src/lib/libc/libc_init.h +++ b/repos/libports/src/lib/libc/libc_init.h @@ -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_ */ diff --git a/repos/libports/src/lib/libc/malloc.cc b/repos/libports/src/lib/libc/malloc.cc index 864181bc4..43116c7bd 100644 --- a/repos/libports/src/lib/libc/malloc.cc +++ b/repos/libports/src/lib/libc/malloc.cc @@ -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(&malloc, heap); +} diff --git a/repos/libports/src/lib/libc/plugin.cc b/repos/libports/src/lib/libc/plugin.cc index 4f3a84b5d..dfdd77951 100644 --- a/repos/libports/src/lib/libc/plugin.cc +++ b/repos/libports/src/lib/libc/plugin.cc @@ -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)); diff --git a/repos/libports/src/lib/libc/plugin_registry.cc b/repos/libports/src/lib/libc/plugin_registry.cc index a041fe4b3..22e2103dd 100644 --- a/repos/libports/src/lib/libc/plugin_registry.cc +++ b/repos/libports/src/lib/libc/plugin_registry.cc @@ -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) } diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index b39982f36..c854dc245 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -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 > _dataspaces { }; + + void _release(Genode::Registered &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 &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(_dataspaces, cap); + return cap; + } + + void free(Ram_dataspace_capability ds_cap) override + { + _dataspaces.for_each([&] (Registered &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 { _heap, _env.ram() }; + Genode::Constructible _malloc_heap { }; Genode::Registry > _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 &r) { + destroy(_heap, &r); }); + + Heap &raw_malloc_heap = *_malloc_heap; + Genode::construct_at(&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(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); diff --git a/repos/libports/src/test/execve/target.mk b/repos/libports/src/test/execve/target.mk new file mode 100644 index 000000000..193f2cf83 --- /dev/null +++ b/repos/libports/src/test/execve/target.mk @@ -0,0 +1,5 @@ +TARGET = test-execve +SRC_CC = test.cc +LIBS = posix + +CC_CXX_WARN_STRICT = diff --git a/repos/libports/src/test/execve/test.cc b/repos/libports/src/test/execve/test.cc new file mode 100644 index 000000000..7184659ed --- /dev/null +++ b/repos/libports/src/test/execve/test.cc @@ -0,0 +1,37 @@ +/* + * \brief Simple execve test + * \author Norman Feske + * \date 2019-08-20 + */ + +#include +#include +#include + +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; +} diff --git a/repos/ports/src/lib/libc_noux/plugin.cc b/repos/ports/src/lib/libc_noux/plugin.cc index 15407a08a..658f268e6 100644 --- a/repos/ports/src/lib/libc_noux/plugin.cc +++ b/repos/ports/src/lib/libc_noux/plugin.cc @@ -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)