mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 11:55:24 +00:00
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:
parent
2a3cebdd6e
commit
6894ced63b
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
32
repos/libports/run/execve.run
Normal file
32
repos/libports/run/execve.run
Normal 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
|
203
repos/libports/src/lib/libc/execve.cc
Normal file
203
repos/libports/src/lib/libc/execve.cc
Normal 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")));
|
@ -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 **
|
||||
********************/
|
||||
|
@ -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");
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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) }
|
||||
|
@ -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);
|
||||
|
5
repos/libports/src/test/execve/target.mk
Normal file
5
repos/libports/src/test/execve/target.mk
Normal file
@ -0,0 +1,5 @@
|
||||
TARGET = test-execve
|
||||
SRC_CC = test.cc
|
||||
LIBS = posix
|
||||
|
||||
CC_CXX_WARN_STRICT =
|
37
repos/libports/src/test/execve/test.cc
Normal file
37
repos/libports/src/test/execve/test.cc
Normal 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;
|
||||
}
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user