libc: fork/execve improvements

- Close FDs marked with the close-on-execve flag
  (needed for 'make', which sets the flag for the pipe-in
  FD of forked children)
- Update binary name on execve to use as ROM for subsequent fork
- Enable vfork as an alias for fork (needed by make)
- Purge line buffers for output streams during execve because they
  may be allocated at the allocation heap, which does not survive
  the execve call.
- Consider short-lived processes that may exit while the parent still
  blocks in the fork call.

With these changes, the website generator of genodians.org works without
the need for the Noux runtime.

Issue #3578
This commit is contained in:
Norman Feske 2019-12-03 18:38:07 +01:00 committed by Christian Helmuth
parent d1cf216384
commit b7fbe65ff2
10 changed files with 137 additions and 70 deletions

View File

@ -104,6 +104,14 @@ namespace Libc {
File_descriptor *find_by_libc_fd(int libc_fd);
/**
* Return any file descriptor with close-on-execve flag set
*
* \return pointer to file descriptor, or
* nullptr is no such file descriptor exists
*/
File_descriptor *any_cloexec_libc_fd();
void generate_info(Genode::Xml_generator &);
};

View File

@ -97,7 +97,6 @@ DUMMY(char *, 0, crypt, (const char *, const char *))
DUMMY(DB * , 0, dbopen, (const char *, int, int, DBTYPE, const void *))
DUMMY(u_int32_t, 0, __default_hash, (const void *, size_t));
DUMMY_SILENT(long , -1, _fpathconf, (int, int))
DUMMY(pid_t , -1, vfork, (void))
DUMMY(long , -1, fpathconf, (int, int))
DUMMY(int , -1, freebsd7___semctl, (void))
DUMMY(int , -1, getcontext, (ucontext_t *))

View File

@ -21,7 +21,9 @@
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <libc/allocator.h>
#include <libc-plugin/fd_alloc.h>
/* libc-internal includes */
#include <internal/call_func.h>
@ -171,7 +173,7 @@ struct Libc::Interpreter
_rom(env, filename), num_args(_count_args() + 2 /* argv0 + filename */)
{
if (script()) {
args = (char **)malloc(sizeof(char *)*num_args);
args = (char **)calloc(num_args + 1 /* null termination */, sizeof(char *));
unsigned i = 0;
@ -183,8 +185,6 @@ struct Libc::Interpreter
/* supply script file name as last argument */
args[i++] = strdup(filename);
}
}
@ -277,9 +277,10 @@ struct Libc::String_array : Noncopyable
size_t const n = _num_entries(src_array);
for (unsigned i = skip; i < n; i++) {
array[dst_i++] = _buffer->pos_ptr();
array[dst_i] = _buffer->pos_ptr();
if (!_buffer->try_append(src_array[i]))
break;
dst_i++;
}
};
@ -291,8 +292,6 @@ struct Libc::String_array : Noncopyable
array[dst_i] = nullptr;
break;
}
warning("string-array buffer ", size, " exceeded");
size += 1024;
}
}
@ -304,22 +303,27 @@ struct Libc::String_array : Noncopyable
/* 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;
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;
static Libc::Binary_name *_binary_name_ptr;
static Libc::File_descriptor_allocator *_fd_alloc_ptr;
void Libc::init_execve(Env &env, Genode::Allocator &alloc, void *user_stack_ptr,
Reset_malloc_heap &reset_malloc_heap)
Reset_malloc_heap &reset_malloc_heap, Binary_name &binary_name,
File_descriptor_allocator &fd_alloc)
{
_env_ptr = &env;
_alloc_ptr = &alloc;
_user_stack_ptr = user_stack_ptr;
_reset_malloc_heap_ptr = &reset_malloc_heap;
_binary_name_ptr = &binary_name;
_fd_alloc_ptr = &fd_alloc;
Dynamic_linker::keep(env, "libc.lib.so");
Dynamic_linker::keep(env, "libm.lib.so");
@ -346,6 +350,10 @@ extern "C" int execve(char const *filename,
return Libc::Errno(EACCES);
}
/* close all file descriptors with the close-on-execve flag enabled */
while (Libc::File_descriptor *fd = _fd_alloc_ptr->any_cloexec_libc_fd())
close(fd->libc_fd);
/* capture environment variables and args to libc-internal heap */
Libc::String_array *saved_env_vars =
new (*_alloc_ptr) Libc::String_array(*_alloc_ptr, envp);
@ -367,7 +375,7 @@ extern "C" int execve(char const *filename,
try {
Libc::resolve_symlinks(path.string(), resolved_path); }
catch (Libc::Symlink_resolve_error) {
warning("execve: executable binary does not exist");
warning("execve: executable binary '", filename, "' does not exist");
return Libc::Errno(ENOENT);
}
@ -411,6 +419,10 @@ extern "C" int execve(char const *filename,
return Libc::Errno(EACCES);
}
/* purge line buffers, which may be allocated at the application heap */
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
/*
* Reconstruct malloc heap for application-owned data
*/
@ -424,6 +436,9 @@ extern "C" int execve(char const *filename,
/* register list of environment variables at libc 'environ' pointer */
environ = _env_vars_ptr->array;
/* remember name of new ROM module, to be used by next call of fork */
*_binary_name_ptr = Libc::Binary_name(resolved_path.string());
destroy(_alloc_ptr, saved_env_vars);
destroy(_alloc_ptr, saved_args);

View File

@ -111,6 +111,20 @@ File_descriptor *File_descriptor_allocator::find_by_libc_fd(int libc_fd)
}
File_descriptor *File_descriptor_allocator::any_cloexec_libc_fd()
{
Lock::Guard guard(_lock);
File_descriptor *result = nullptr;
_id_space.for_each<File_descriptor>([&] (File_descriptor &fd) {
if (!result && fd.cloexec)
result = &fd; });
return result;
}
void File_descriptor_allocator::generate_info(Xml_generator &xml)
{
Lock::Guard guard(_lock);

View File

@ -40,6 +40,23 @@
#include <internal/resume.h>
#include <internal/signal.h>
namespace Libc {
struct Child_config;
struct Parent_services;
struct Local_rom_service;
struct Local_rom_services;
struct Local_clone_service;
struct Forked_child_policy;
struct Forked_child;
struct Child_ready : Interface
{
virtual void child_ready() = 0;
};
typedef Registry<Registered<Forked_child> > Forked_children;
}
using namespace Libc;
@ -56,40 +73,9 @@ static void *_user_stack_base_ptr;
static size_t _user_stack_size;
static int _pid;
static int _pid_cnt;
static Config_accessor const *_config_accessor_ptr;
void Libc::init_fork(Env &env, Config_accessor const &config_accessor,
Allocator &alloc, Heap &malloc_heap, pid_t pid,
Suspend &suspend, Resume &resume, Signal &signal,
Kernel_routine_scheduler &kernel_routine_scheduler)
{
_env_ptr = &env;
_alloc_ptr = &alloc;
_suspend_ptr = &suspend;
_resume_ptr = &resume;
_signal_ptr = &signal;
_kernel_routine_scheduler_ptr = &kernel_routine_scheduler;
_malloc_heap_ptr = &malloc_heap;
_config_accessor_ptr = &config_accessor;
_pid = pid;
}
namespace Libc {
struct Child_config;
struct Parent_services;
struct Local_rom_service;
struct Local_rom_services;
struct Local_clone_service;
struct Forked_child_policy;
struct Forked_child;
struct Child_ready : Interface
{
virtual void child_ready() = 0;
};
}
static Config_accessor const *_config_accessor_ptr;
static Binary_name const *_binary_name_ptr;
static Forked_children *_forked_children_ptr;
struct Libc::Child_config
@ -406,6 +392,8 @@ struct Libc::Forked_child : Child_policy, Child_ready
{
Env &_env;
Binary_name const _binary_name;
Resume &_resume;
Signal &_signal;
@ -447,7 +435,7 @@ struct Libc::Forked_child : Child_policy, Child_ready
void execute_in_kernel() override
{
/* keep executing this kernel routine until child is running */
if (!child.running())
if (!child.running() && !child.exited())
_kernel_routine_scheduler_ptr->register_kernel_routine(*this);
}
} wait_fork_ready { *this };
@ -465,7 +453,17 @@ struct Libc::Forked_child : Child_policy, Child_ready
** Child_ready interface **
***************************/
void child_ready() override { _state = State::RUNNING; }
void child_ready() override
{
/*
* Don't modify the state if the child already exited.
* This can happen for short-lived children where the asynchronous
* notification for '_handle_exit' arrives before '_handle_child_ready'
* (while the parent is still blocking in the fork call).
*/
if (_state == State::STARTING_UP)
_state = State::RUNNING;
}
/****************************
@ -474,7 +472,7 @@ struct Libc::Forked_child : Child_policy, Child_ready
Name name() const override { return _name; }
Binary_name binary_name() const override { return "binary"; }
Binary_name binary_name() const override { return _binary_name; }
Pd_session &ref_pd() override { return _env.pd(); }
Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); }
@ -549,6 +547,7 @@ struct Libc::Forked_child : Child_policy, Child_ready
Forked_child(Env &env,
Entrypoint &fork_ep,
Allocator &alloc,
Binary_name const &binary_name,
Resume &resume,
Signal &signal,
pid_t pid,
@ -556,7 +555,8 @@ struct Libc::Forked_child : Child_policy, Child_ready
Parent_services &parent_services,
Local_rom_services &local_rom_services)
:
_env(env), _resume(resume), _signal(signal), _pid(pid),
_env(env), _binary_name(binary_name),
_resume(resume), _signal(signal), _pid(pid),
_child_config(env, config_accessor, pid),
_parent_services(parent_services),
_local_rom_services(local_rom_services),
@ -569,11 +569,6 @@ struct Libc::Forked_child : Child_policy, Child_ready
};
/* initialized on first call of 'fork_kernel_routine' */
typedef Registry<Registered<Libc::Forked_child> > Forked_children;
static Forked_children *_forked_children_ptr;
static void fork_kernel_routine()
{
fork_result = 0;
@ -597,11 +592,9 @@ static void fork_kernel_routine()
static Local_rom_services local_rom_services(env, fork_ep, alloc);
static Forked_children forked_children { };
_forked_children_ptr = &forked_children;
Registered<Forked_child> &child = *new (alloc)
Registered<Forked_child>(forked_children, env, fork_ep, alloc, resume,
Registered<Forked_child>(*_forked_children_ptr, env, fork_ep, alloc,
*_binary_name_ptr, resume,
signal, child_pid, *_config_accessor_ptr,
parent_services, local_rom_services);
@ -652,7 +645,9 @@ extern "C" pid_t __sys_fork(void)
return fork_result;
}
pid_t fork(void) __attribute__((weak, alias("__sys_fork")));
pid_t fork(void) __attribute__((weak, alias("__sys_fork")));
pid_t vfork(void) __attribute__((weak, alias("__sys_fork")));
/************
@ -758,3 +753,24 @@ extern "C" pid_t __sys_wait4(pid_t pid, int *status, int options, rusage *rusage
extern "C" pid_t wait4(pid_t, int *, int, rusage *) __attribute__((weak, alias("__sys_wait4")));
void Libc::init_fork(Env &env, Config_accessor const &config_accessor,
Allocator &alloc, Heap &malloc_heap, pid_t pid,
Suspend &suspend, Resume &resume, Signal &signal,
Kernel_routine_scheduler &kernel_routine_scheduler,
Binary_name const &binary_name)
{
_env_ptr = &env;
_alloc_ptr = &alloc;
_suspend_ptr = &suspend;
_resume_ptr = &resume;
_signal_ptr = &signal;
_kernel_routine_scheduler_ptr = &kernel_routine_scheduler;
_malloc_heap_ptr = &malloc_heap;
_config_accessor_ptr = &config_accessor;
_pid = pid;
_binary_name_ptr = &binary_name;
static Forked_children forked_children { };
_forked_children_ptr = &forked_children;
}

View File

@ -19,6 +19,7 @@
/* libc includes */
#include <limits.h> /* for 'PATH_MAX' */
#include <libc-plugin/plugin.h>
/* libc-internal includes */
#include <internal/types.h>
@ -27,8 +28,6 @@ namespace Libc {
typedef Genode::Path<PATH_MAX> Absolute_path;
class Symlink_resolve_error : Exception { };
void resolve_symlinks(char const *path, Absolute_path &resolved_path);
}

View File

@ -36,6 +36,7 @@ namespace Libc {
struct Kernel_routine_scheduler;
struct Watch;
struct Signal;
struct File_descriptor_allocator;
/**
* Support for shared libraries
@ -118,7 +119,8 @@ namespace Libc {
*/
void init_fork(Genode::Env &, Config_accessor const &,
Genode::Allocator &heap, Heap &malloc_heap, int pid,
Suspend &, Resume &, Signal &, Kernel_routine_scheduler &);
Suspend &, Resume &, Signal &, Kernel_routine_scheduler &,
Binary_name const &);
struct Reset_malloc_heap : Interface
{
@ -129,7 +131,8 @@ namespace Libc {
* Execve mechanism
*/
void init_execve(Genode::Env &, Genode::Allocator &, void *user_stack,
Reset_malloc_heap &);
Reset_malloc_heap &, Binary_name &,
File_descriptor_allocator &);
/**
* Signal handling

View File

@ -79,6 +79,13 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
*/
Allocator &_heap;
/**
* Name of the current binary's ROM module
*
* Used by fork, modified by execve.
*/
Binary_name _binary_name { "binary" };
/**
* Allocator for application-owned data
*

View File

@ -15,12 +15,14 @@
#define _LIBC__INTERNAL__TYPES_H_
#include <base/log.h>
#include <util/string.h>
namespace Libc {
using namespace Genode;
typedef Genode::uint64_t uint64_t;
typedef String<64> Binary_name;
}
#endif /* _LIBC__INTERNAL__TYPES_H_ */

View File

@ -119,6 +119,8 @@ void Libc::Kernel::_init_file_descriptors()
return;
}
fd->cloexec = node.attribute_value("cloexec", false);
/*
* We need to manually register the path. Normally this is done
* by '_open'. But we call the local 'open' function directly
@ -373,8 +375,10 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap)
init_malloc(*_malloc_heap);
}
init_fork(_env, _libc_env, _heap, *_malloc_heap, _pid, *this, *this, _signal, *this);
init_execve(_env, _heap, _user_stack, *this);
init_fork(_env, _libc_env, _heap, *_malloc_heap, _pid, *this, *this, _signal,
*this, _binary_name);
init_execve(_env, _heap, _user_stack, *this, _binary_name,
*file_descriptor_allocator());
init_plugin(*this);
init_sleep(*this);
init_vfs_plugin(*this);