mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 03:45:24 +00:00
noux: Keep track of how dataspaces are used
This patch eliminates the "no attachment at..." warnings, which were caused by a use-after-free problem of dataspaces. When a dataspace was destroyed, the users of the dataspace were not informed and therefore could not revert possible attachments to RM sessions. The fix introduces a callback mechanism that allows dataspace users (i.e., RM regions) to register for the event that a dataspace vanishes. The following types of dataspaces are handled: * RAM dataspaces * ROM dataspaces * The process binary * The binary of the dynamic linker * Args dataspace * Sysio dataspace * Env dataspace * managed RM dataspaces The handling of ROM dataspaces is still not complete. When forking, the ROM dataspace of the parent process gets just reused without creating proper meta data ('Dataspace_info') for the forked process. Similar issues might arise from other special dataspaces (e.g., args, env, sysio). This patch removes all "no attachment at..." warnings except for one (an attachment at 0). Issue #485
This commit is contained in:
parent
959282403c
commit
956cab5fdb
@ -35,6 +35,7 @@
|
||||
|
||||
#include <local_cpu_service.h>
|
||||
#include <local_ram_service.h>
|
||||
#include <local_rom_service.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
@ -82,6 +83,11 @@ namespace Noux {
|
||||
*/
|
||||
Io_receptor_registry *io_receptor_registry();
|
||||
|
||||
/**
|
||||
* Return ELF binary of dynamic linker
|
||||
*/
|
||||
Dataspace_capability ldso_ds_cap();
|
||||
|
||||
class Child;
|
||||
|
||||
bool is_init_process(Child *child);
|
||||
@ -181,9 +187,17 @@ namespace Noux {
|
||||
Local_ram_service _local_ram_service;
|
||||
Local_cpu_service _local_cpu_service;
|
||||
Local_rm_service _local_rm_service;
|
||||
Local_rom_service _local_rom_service;
|
||||
Service_registry &_parent_services;
|
||||
|
||||
Static_dataspace_info _binary_ds_info;
|
||||
Static_dataspace_info _sysio_ds_info;
|
||||
Static_dataspace_info _ldso_ds_info;
|
||||
Static_dataspace_info _args_ds_info;
|
||||
Static_dataspace_info _env_ds_info;
|
||||
|
||||
Child_policy _child_policy;
|
||||
|
||||
Genode::Child _child;
|
||||
|
||||
/**
|
||||
@ -270,8 +284,7 @@ namespace Noux {
|
||||
_args(ARGS_DS_SIZE, args),
|
||||
_env(env),
|
||||
_root_dir(root_dir),
|
||||
_binary_ds(forked ? Dataspace_capability()
|
||||
: root_dir->dataspace(name)),
|
||||
_binary_ds(root_dir->dataspace(name)),
|
||||
_sysio_ds(Genode::env()->ram_session(), SYSIO_DS_SIZE),
|
||||
_sysio(_sysio_ds.local_addr<Sysio>()),
|
||||
_noux_session_cap(Session_capability(_entrypoint.manage(this))),
|
||||
@ -279,12 +292,20 @@ namespace Noux {
|
||||
_local_ram_service(_entrypoint),
|
||||
_local_cpu_service(_entrypoint, _resources.cpu.cpu_cap()),
|
||||
_local_rm_service(_entrypoint, _resources.ds_registry),
|
||||
_local_rom_service(_entrypoint, _resources.ds_registry),
|
||||
_parent_services(parent_services),
|
||||
_binary_ds_info(_resources.ds_registry, _binary_ds),
|
||||
_sysio_ds_info(_resources.ds_registry, _sysio_ds.cap()),
|
||||
_ldso_ds_info(_resources.ds_registry, ldso_ds_cap()),
|
||||
_args_ds_info(_resources.ds_registry, _args.cap()),
|
||||
_env_ds_info(_resources.ds_registry, _env.cap()),
|
||||
_child_policy(name, _binary_ds, _args.cap(), _env.cap(),
|
||||
_entrypoint, _local_noux_service,
|
||||
_local_rm_service, _parent_services,
|
||||
_local_rm_service, _local_rom_service,
|
||||
_parent_services,
|
||||
*this, *this, _destruct_context_cap, _resources.ram),
|
||||
_child(_binary_ds, _resources.ram.cap(), _resources.cpu.cap(),
|
||||
_child(forked ? Dataspace_capability() : _binary_ds,
|
||||
_resources.ram.cap(), _resources.cpu.cap(),
|
||||
_resources.rm.cap(), &_entrypoint, &_child_policy,
|
||||
/**
|
||||
* Override the implicit assignment to _parent_service
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <file_descriptor_registry.h>
|
||||
#include <local_noux_service.h>
|
||||
#include <local_rm_service.h>
|
||||
#include <local_rom_service.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
@ -38,6 +39,7 @@ namespace Noux {
|
||||
Init::Child_policy_provide_rom_file _env_policy;
|
||||
Local_noux_service &_local_noux_service;
|
||||
Local_rm_service &_local_rm_service;
|
||||
Local_rom_service &_local_rom_service;
|
||||
Service_registry &_parent_services;
|
||||
Family_member &_family_member;
|
||||
File_descriptor_registry &_file_descriptor_registry;
|
||||
@ -53,6 +55,7 @@ namespace Noux {
|
||||
Rpc_entrypoint &entrypoint,
|
||||
Local_noux_service &local_noux_service,
|
||||
Local_rm_service &local_rm_service,
|
||||
Local_rom_service &local_rom_service,
|
||||
Service_registry &parent_services,
|
||||
Family_member &family_member,
|
||||
File_descriptor_registry &file_descriptor_registry,
|
||||
@ -66,6 +69,7 @@ namespace Noux {
|
||||
_env_policy( "env", env_ds, &entrypoint),
|
||||
_local_noux_service(local_noux_service),
|
||||
_local_rm_service(local_rm_service),
|
||||
_local_rom_service(local_rom_service),
|
||||
_parent_services(parent_services),
|
||||
_family_member(family_member),
|
||||
_file_descriptor_registry(file_descriptor_registry),
|
||||
@ -98,6 +102,12 @@ namespace Noux {
|
||||
if (strcmp(service_name, Rm_session::service_name()) == 0)
|
||||
return &_local_rm_service;
|
||||
|
||||
/*
|
||||
* Check for local ROM service
|
||||
*/
|
||||
if (strcmp(service_name, Rom_session::service_name()) == 0)
|
||||
return &_local_rom_service;
|
||||
|
||||
return _parent_services.find(service_name);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,13 @@
|
||||
namespace Noux {
|
||||
|
||||
class Dataspace_registry;
|
||||
class Dataspace_info;
|
||||
|
||||
|
||||
struct Dataspace_user : List<Dataspace_user>::Element
|
||||
{
|
||||
virtual void dissolve(Dataspace_info &ds) = 0;
|
||||
};
|
||||
|
||||
|
||||
class Dataspace_info : public Object_pool<Dataspace_info>::Entry
|
||||
@ -28,6 +35,8 @@ namespace Noux {
|
||||
|
||||
size_t _size;
|
||||
Dataspace_capability _ds_cap;
|
||||
Lock _users_lock;
|
||||
List<Dataspace_user> _users;
|
||||
|
||||
public:
|
||||
|
||||
@ -43,6 +52,32 @@ namespace Noux {
|
||||
size_t size() const { return _size; }
|
||||
Dataspace_capability ds_cap() const { return _ds_cap; }
|
||||
|
||||
void register_user(Dataspace_user &user)
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
_users.insert(&user);
|
||||
}
|
||||
|
||||
void unregister_user(Dataspace_user &user)
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
_users.remove(&user);
|
||||
}
|
||||
|
||||
void dissolve_users()
|
||||
{
|
||||
for (;;) {
|
||||
Dataspace_user *user = 0;
|
||||
{
|
||||
Lock::Guard guard(_users_lock);
|
||||
user = _users.first();
|
||||
if (!user)
|
||||
break;
|
||||
}
|
||||
user->dissolve(*this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shadow copy of dataspace
|
||||
*
|
||||
@ -112,6 +147,45 @@ namespace Noux {
|
||||
return _pool.lookup_and_lock(ds_cap);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Static_dataspace_info : Dataspace_info
|
||||
{
|
||||
Dataspace_registry &_ds_registry;
|
||||
|
||||
Static_dataspace_info(Dataspace_registry &ds_registry,
|
||||
Dataspace_capability ds)
|
||||
: Dataspace_info(ds), _ds_registry(ds_registry)
|
||||
{
|
||||
_ds_registry.insert(this);
|
||||
}
|
||||
|
||||
~Static_dataspace_info()
|
||||
{
|
||||
Static_dataspace_info *info =
|
||||
dynamic_cast<Static_dataspace_info *>(_ds_registry.lookup_info(ds_cap()));
|
||||
|
||||
if (!info) {
|
||||
PERR("lookup of binary ds info failed");
|
||||
return;
|
||||
}
|
||||
|
||||
info->dissolve_users();
|
||||
_ds_registry.remove(info);
|
||||
}
|
||||
|
||||
Dataspace_capability fork(Ram_session_capability,
|
||||
Dataspace_registry &,
|
||||
Rpc_entrypoint &)
|
||||
{
|
||||
return ds_cap();
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
{
|
||||
PERR("Attempt to poke onto a static dataspace");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__DATASPACE_REGISTRY_H_ */
|
||||
|
@ -26,7 +26,7 @@ namespace Noux {
|
||||
{
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
Rpc_entrypoint &_ep;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -133,6 +133,7 @@ namespace Noux {
|
||||
/* release dataspace info */
|
||||
Dataspace_info *info = _ds_registry.lookup_info(ds_cap);
|
||||
if (info) {
|
||||
info->dissolve_users();
|
||||
_ds_registry.remove(info);
|
||||
destroy(env()->heap(), info);
|
||||
} else {
|
||||
|
78
ports/src/noux/local_rom_service.h
Normal file
78
ports/src/noux/local_rom_service.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* \brief ROM service provided to Noux processes
|
||||
* \author Norman Feske
|
||||
* \date 2013-07-18
|
||||
*
|
||||
* The local ROM service has the sole purpose of tracking ROM dataspaces
|
||||
* so that they are properly detached from RM sessions when the corresponding
|
||||
* ROM sessions are closed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__LOCAL_ROM_SERVICE_H_
|
||||
#define _NOUX__LOCAL_ROM_SERVICE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/service.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <dataspace_registry.h>
|
||||
#include <rom_session_component.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Local_rom_service : public Service
|
||||
{
|
||||
private:
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
Dataspace_registry &_ds_registry;
|
||||
|
||||
public:
|
||||
|
||||
Local_rom_service(Rpc_entrypoint &ep, Dataspace_registry &ds_registry)
|
||||
:
|
||||
Service(Rom_session::service_name()), _ep(ep),
|
||||
_ds_registry(ds_registry)
|
||||
{ }
|
||||
|
||||
Genode::Session_capability session(const char *args)
|
||||
{
|
||||
enum { NAME_MAX_LEN = 128 };
|
||||
char name[NAME_MAX_LEN];
|
||||
Arg_string::find_arg(args, "filename").string(name, sizeof(name), "<noname>");
|
||||
|
||||
Rom_session_component *rom = new (env()->heap())
|
||||
Rom_session_component(_ds_registry, name);
|
||||
|
||||
return _ep.manage(rom);
|
||||
}
|
||||
|
||||
void upgrade(Genode::Session_capability, const char *args) { }
|
||||
|
||||
void close(Genode::Session_capability session)
|
||||
{
|
||||
/* acquire locked session object */
|
||||
Rom_session_component *rom_session =
|
||||
dynamic_cast<Rom_session_component *>(_ep.lookup_and_lock(session));
|
||||
|
||||
if (!rom_session) {
|
||||
PWRN("Unexpected call of close with non-ROM-session argument");
|
||||
return;
|
||||
}
|
||||
|
||||
_ep.dissolve(rom_session);
|
||||
|
||||
destroy(env()->heap(), rom_session);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__LOCAL_ROM_SERVICE_H_ */
|
@ -819,6 +819,18 @@ Terminal::Connection *Noux::terminal()
|
||||
}
|
||||
|
||||
|
||||
Genode::Dataspace_capability Noux::ldso_ds_cap()
|
||||
{
|
||||
try {
|
||||
static Genode::Rom_connection rom("ld.lib.so");
|
||||
static Genode::Dataspace_capability ldso_ds = rom.dataspace();
|
||||
return ldso_ds;
|
||||
} catch (...) { }
|
||||
|
||||
return Genode::Dataspace_capability();
|
||||
}
|
||||
|
||||
|
||||
void *operator new (Genode::size_t size) {
|
||||
return Genode::env()->heap()->alloc(size); }
|
||||
|
||||
@ -828,11 +840,8 @@ int main(int argc, char **argv)
|
||||
using namespace Noux;
|
||||
PINF("--- noux started ---");
|
||||
|
||||
/* look for dynamic linker */
|
||||
try {
|
||||
static Genode::Rom_connection rom("ld.lib.so");
|
||||
Genode::Process::dynamic_linker(rom.dataspace());
|
||||
} catch (...) { }
|
||||
/* register dynamic linker */
|
||||
Genode::Process::dynamic_linker(ldso_ds_cap());
|
||||
|
||||
/* whitelist of service requests to be routed to the parent */
|
||||
static Genode::Service_registry parent_services;
|
||||
|
@ -160,7 +160,9 @@ namespace Noux {
|
||||
PERR("RAM free: dataspace lookup failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ds_info->dissolve_users();
|
||||
|
||||
_registry.remove(ds_info);
|
||||
_list.remove(ds_info);
|
||||
_used_quota -= ds_info->size();
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
namespace Noux {
|
||||
|
||||
static bool verbose_attach = false;
|
||||
|
||||
class Rm_session_component : public Rpc_object<Rm_session>
|
||||
{
|
||||
private:
|
||||
@ -31,17 +33,20 @@ namespace Noux {
|
||||
/**
|
||||
* Record of an attached dataspace
|
||||
*/
|
||||
struct Region : List<Region>::Element
|
||||
struct Region : List<Region>::Element, Dataspace_user
|
||||
{
|
||||
Dataspace_capability ds;
|
||||
size_t size;
|
||||
off_t offset;
|
||||
addr_t local_addr;
|
||||
Rm_session_component &rm;
|
||||
Dataspace_capability ds;
|
||||
size_t size;
|
||||
off_t offset;
|
||||
addr_t local_addr;
|
||||
|
||||
Region(Dataspace_capability ds, size_t size,
|
||||
Region(Rm_session_component &rm,
|
||||
Dataspace_capability ds, size_t size,
|
||||
off_t offset, addr_t local_addr)
|
||||
:
|
||||
ds(ds), size(size), offset(offset), local_addr(local_addr)
|
||||
rm(rm), ds(ds), size(size), offset(offset),
|
||||
local_addr(local_addr)
|
||||
{ }
|
||||
|
||||
/**
|
||||
@ -52,6 +57,13 @@ namespace Noux {
|
||||
return (addr >= local_addr)
|
||||
&& (addr < local_addr + size);
|
||||
}
|
||||
|
||||
Region *next_region()
|
||||
{
|
||||
return List<Region>::Element::next();
|
||||
}
|
||||
|
||||
inline void dissolve(Dataspace_info &ds);
|
||||
};
|
||||
|
||||
List<Region> _regions;
|
||||
@ -59,7 +71,7 @@ namespace Noux {
|
||||
Region *_lookup_region_by_addr(addr_t local_addr)
|
||||
{
|
||||
Region *curr = _regions.first();
|
||||
for (; curr; curr = curr->next()) {
|
||||
for (; curr; curr = curr->next_region()) {
|
||||
if (curr->contains(local_addr))
|
||||
return curr;
|
||||
}
|
||||
@ -103,7 +115,7 @@ namespace Noux {
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &ep)
|
||||
{
|
||||
for (Region *curr = _regions.first(); curr; curr = curr->next()) {
|
||||
for (Region *curr = _regions.first(); curr; curr = curr->next_region()) {
|
||||
|
||||
Dataspace_capability ds;
|
||||
|
||||
@ -121,6 +133,9 @@ namespace Noux {
|
||||
|
||||
} else {
|
||||
|
||||
PERR("replay: missing ds_info for dataspace at addr 0x%lx",
|
||||
curr->local_addr);
|
||||
|
||||
/*
|
||||
* If the dataspace is not a RAM dataspace, assume that
|
||||
* it's a ROM dataspace.
|
||||
@ -195,21 +210,35 @@ namespace Noux {
|
||||
if (size == 0)
|
||||
size = Dataspace_client(ds).size() - offset;
|
||||
|
||||
/*
|
||||
* XXX look if we can identify the specified dataspace.
|
||||
* Is it a dataspace allocated via 'Local_ram_session'?
|
||||
*/
|
||||
|
||||
local_addr = _rm.attach(ds, size, offset,
|
||||
use_local_addr, local_addr,
|
||||
executable);
|
||||
|
||||
Region *region = new (env()->heap())
|
||||
Region(*this, ds, size, offset, local_addr);
|
||||
|
||||
/* register region as user of RAM dataspaces */
|
||||
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(ds));
|
||||
|
||||
|
||||
if (info) {
|
||||
info->register_user(*region);
|
||||
} else {
|
||||
if (verbose_attach) {
|
||||
PWRN("Trying to attach unknown dataspace type");
|
||||
PWRN(" ds_info@%p at 0x%lx size=%zd offset=0x%lx",
|
||||
info.object(), (long)local_addr,
|
||||
Dataspace_client(ds).size(), (long)offset);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Record attachment for later replay (needed during
|
||||
* fork)
|
||||
*/
|
||||
_regions.insert(new (env()->heap())
|
||||
Region(ds, size, offset, local_addr));
|
||||
_regions.insert(region);
|
||||
|
||||
|
||||
return local_addr;
|
||||
}
|
||||
|
||||
@ -225,6 +254,11 @@ namespace Noux {
|
||||
}
|
||||
|
||||
_regions.remove(region);
|
||||
|
||||
Object_pool<Dataspace_info>::Guard info(_ds_registry.lookup_info(region->ds));
|
||||
if (info)
|
||||
info->unregister_user(*region);
|
||||
|
||||
destroy(env()->heap(), region);
|
||||
}
|
||||
|
||||
@ -253,6 +287,20 @@ namespace Noux {
|
||||
return _rm.dataspace();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline void Rm_session_component::Region::dissolve(Dataspace_info &ds)
|
||||
{
|
||||
/*
|
||||
* When this function is called, the 'ds' is already locked by the
|
||||
* caller. To prevent 'detach' from taking the lock twice
|
||||
* ('_ds_registry.lookup_info'), the temporarily release and re-acquire
|
||||
* the lock.
|
||||
*/
|
||||
ds.release();
|
||||
rm.detach(local_addr);
|
||||
ds.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _NOUX__RM_SESSION_COMPONENT_H_ */
|
||||
|
97
ports/src/noux/rom_session_component.h
Normal file
97
ports/src/noux/rom_session_component.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* \brief ROM session implementation used by Noux processes
|
||||
* \author Norman Feske
|
||||
* \date 2013-07-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__ROM_SESSION_COMPONENT_H_
|
||||
#define _NOUX__ROM_SESSION_COMPONENT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <rom_session/connection.h>
|
||||
#include <base/rpc_server.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
struct Rom_dataspace_info : Dataspace_info
|
||||
{
|
||||
Rom_dataspace_info(Dataspace_capability ds) : Dataspace_info(ds) { }
|
||||
|
||||
~Rom_dataspace_info() { }
|
||||
|
||||
Dataspace_capability fork(Ram_session_capability,
|
||||
Dataspace_registry &ds_registry,
|
||||
Rpc_entrypoint &)
|
||||
{
|
||||
return ds_cap();
|
||||
}
|
||||
|
||||
void poke(addr_t dst_offset, void const *src, size_t len)
|
||||
{
|
||||
PERR("Attempt to poke onto a ROM dataspace");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Rom_session_component : public Rpc_object<Rom_session>
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Wrapped ROM session at core
|
||||
*/
|
||||
Rom_connection _rom;
|
||||
|
||||
Dataspace_registry &_ds_registry;
|
||||
|
||||
Rom_dataspace_info _ds_info;
|
||||
|
||||
public:
|
||||
|
||||
Rom_session_component(Dataspace_registry &ds_registry,
|
||||
char const *name)
|
||||
:
|
||||
_rom(name), _ds_registry(ds_registry), _ds_info(_rom.dataspace())
|
||||
{
|
||||
_ds_registry.insert(&_ds_info);
|
||||
}
|
||||
|
||||
~Rom_session_component()
|
||||
{
|
||||
/*
|
||||
* Lookup and lock ds info instead of directly acccessing
|
||||
* the '_ds_info' member.
|
||||
*/
|
||||
Object_pool<Dataspace_info>::Guard
|
||||
info(_ds_registry.lookup_info(_ds_info.ds_cap()));
|
||||
|
||||
if (info)
|
||||
info->dissolve_users();
|
||||
else
|
||||
PERR("~Rom_session_component: unexpected !info");
|
||||
|
||||
_ds_registry.remove(&_ds_info);
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** ROM session interface **
|
||||
***************************/
|
||||
|
||||
Rom_dataspace_capability dataspace()
|
||||
{
|
||||
return static_cap_cast<Rom_dataspace>(_ds_info.ds_cap());
|
||||
}
|
||||
|
||||
void sigh(Signal_context_capability) { }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__ROM_SESSION_COMPONENT_H_ */
|
Loading…
x
Reference in New Issue
Block a user