mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-28 09:38:53 +00:00
Delegate access to entrypoints via SCM rights
This patch eliminates the thread ID portion of the 'Native_capability' type. The access to entrypoints is now exclusively handled by passing socket descripts over Unix domain sockets and by inheriting the socket descriptor of the parent entrypoint at process-creation time. Each entrypoint creates a socket pair. The server-side socket is bound to a unique name defined by the server. The client-side socket is then connected to the same name. Whereas the server-side socket is meant to be exclusively used by the server to wait for incoming requests, the client-side socket can be delegated to other processes as payload of RPC messages (via SCM rights). Anyone who receives a capability over RPC receives the client-side socket of the entrypoint to which the capability refers. Given this socket descriptor, the unique name (as defined by the server) can be requested using 'getpeername'. Using this name, it is possible to compare socket descriptors, which is important to avoid duplicates from polluting the limited socket-descriptor name space. Wheras this patch introduces capability-based delegation of access rights to entrypoints, it does not cover the protection of the integrity of RPC objects. RPC objects are still referenced by a global ID passed as normal message payload.
This commit is contained in:
parent
64efaf249a
commit
f33c7c73bd
base-linux
include/base
src
@ -16,50 +16,23 @@
|
||||
|
||||
#include <base/ipc_generic.h>
|
||||
|
||||
#include <base/snprintf.h>
|
||||
|
||||
extern "C" int raw_write_str(const char *str);
|
||||
extern "C" void wait_for_continue(void);
|
||||
|
||||
#define PRAWW(fmt, ...) \
|
||||
do { \
|
||||
char str[128]; \
|
||||
Genode::snprintf(str, sizeof(str), \
|
||||
ESC_ERR fmt ESC_END "\n", ##__VA_ARGS__); \
|
||||
raw_write_str(str); \
|
||||
} while (0)
|
||||
|
||||
|
||||
inline void Genode::Ipc_ostream::_marshal_capability(Genode::Native_capability const &cap)
|
||||
{
|
||||
|
||||
PRAWW("_marshal_capability called: local_name=%ld, tid=%ld, socket=%d",
|
||||
cap.local_name(), cap.dst().tid, cap.dst().socket);
|
||||
|
||||
_write_to_buf(cap.local_name());
|
||||
_write_to_buf(cap.dst().tid);
|
||||
|
||||
if (cap.valid()) {
|
||||
PRAWW("append_cap");
|
||||
if (cap.valid())
|
||||
_snd_msg->append_cap(cap.dst().socket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void Genode::Ipc_istream::_unmarshal_capability(Genode::Native_capability &cap)
|
||||
{
|
||||
long local_name = 0;
|
||||
long tid = 0;
|
||||
int socket = -1;
|
||||
|
||||
_read_from_buf(local_name);
|
||||
_read_from_buf(tid);
|
||||
|
||||
bool const cap_valid = (tid != 0);
|
||||
if (cap_valid)
|
||||
socket = _rcv_msg->read_cap();
|
||||
|
||||
cap = Native_capability(Cap_dst_policy::Dst(tid, socket), local_name);
|
||||
int const socket = _rcv_msg->read_cap();
|
||||
cap = Native_capability(Cap_dst_policy::Dst(socket), local_name);
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__BASE__IPC_H_ */
|
||||
|
@ -91,10 +91,6 @@ namespace Genode {
|
||||
{
|
||||
return index < _used_caps ? _caps[index] : -1;
|
||||
}
|
||||
|
||||
size_t used_size() const { return _used_size; }
|
||||
|
||||
void used_size(size_t used_size) { _used_size = used_size; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -56,7 +56,7 @@ namespace Genode {
|
||||
static IF *deref(Capability<IF> cap)
|
||||
{
|
||||
/* check if this is a pseudo capability */
|
||||
if (cap.dst().tid != 0 || !cap.local_name())
|
||||
if (cap.dst().socket != -1 || !cap.local_name())
|
||||
throw Non_local_capability();
|
||||
|
||||
/*
|
||||
|
@ -98,19 +98,17 @@ namespace Genode {
|
||||
{
|
||||
struct Dst
|
||||
{
|
||||
long tid; /* XXX to be removed once the transition to SCM rights
|
||||
is completed */
|
||||
int socket;
|
||||
|
||||
/**
|
||||
* Default constructor creates invalid destination
|
||||
*/
|
||||
Dst() : tid(0), socket(-1) { }
|
||||
Dst() : socket(-1) { }
|
||||
|
||||
Dst(long tid, int socket) : tid(tid), socket(socket) { }
|
||||
explicit Dst(int socket) : socket(socket) { }
|
||||
};
|
||||
|
||||
static bool valid(Dst id) { return id.tid != 0; }
|
||||
static bool valid(Dst id) { return id.socket != -1; }
|
||||
static Dst invalid() { return Dst(); }
|
||||
static void copy(void* dst, Native_capability_tpl<Cap_dst_policy>* src);
|
||||
};
|
||||
@ -125,7 +123,15 @@ namespace Genode {
|
||||
/**
|
||||
* The connection state is the socket handle of the RPC entrypoint
|
||||
*/
|
||||
typedef int Native_connection_state;
|
||||
struct Native_connection_state
|
||||
{
|
||||
int server_sd;
|
||||
int client_sd;
|
||||
|
||||
Native_connection_state() : server_sd(-1), client_sd(-1) { }
|
||||
};
|
||||
|
||||
enum { PARENT_SOCKET_HANDLE = 100 };
|
||||
|
||||
struct Native_config
|
||||
{
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <parent/client.h>
|
||||
#include <ram_session/client.h>
|
||||
#include <cpu_session/client.h>
|
||||
#include <pd_session/client.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
@ -323,13 +324,12 @@ namespace Genode {
|
||||
|
||||
Parent_capability _parent_cap()
|
||||
{
|
||||
long tid = _get_env_ulong("parent_tid");
|
||||
long local_name = _get_env_ulong("parent_local_name");
|
||||
|
||||
/* produce typed capability manually */
|
||||
typedef Native_capability::Dst Dst;
|
||||
return reinterpret_cap_cast<Parent>(Native_capability(Dst(tid, -1),
|
||||
local_name));
|
||||
Dst const dst(PARENT_SOCKET_HANDLE);
|
||||
return reinterpret_cap_cast<Parent>(Native_capability(dst, local_name));
|
||||
}
|
||||
|
||||
|
||||
@ -343,6 +343,7 @@ namespace Genode {
|
||||
Cpu_session_capability _cpu_session_cap;
|
||||
Cpu_session_client _cpu_session_client;
|
||||
Rm_session_mmap _rm_session_mmap;
|
||||
Pd_session_client _pd_session_client;
|
||||
Heap _heap;
|
||||
|
||||
public:
|
||||
@ -358,6 +359,7 @@ namespace Genode {
|
||||
_cpu_session_cap(static_cap_cast<Cpu_session>(parent()->session("Env::cpu_session", ""))),
|
||||
_cpu_session_client(_cpu_session_cap),
|
||||
_rm_session_mmap(false),
|
||||
_pd_session_client(static_cap_cast<Pd_session>(parent()->session("Env::pd_session", ""))),
|
||||
_heap(&_ram_session_client, &_rm_session_mmap)
|
||||
{ }
|
||||
|
||||
|
3
base-linux/src/base/env/debug.cc
vendored
3
base-linux/src/base/env/debug.cc
vendored
@ -55,3 +55,6 @@ extern "C" void wait_for_continue(void)
|
||||
lx_syscall(SYS_read, (int)0, buf, sizeof(buf));
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
|
||||
extern "C" int get_pid() { return lx_getpid(); }
|
||||
|
@ -7,7 +7,6 @@
|
||||
* The current request message layout is:
|
||||
*
|
||||
* long server_local_name;
|
||||
* long client_thread_id;
|
||||
* int opcode;
|
||||
* ...payload...
|
||||
*
|
||||
@ -46,6 +45,14 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
|
||||
Genode::Ep_socket_descriptor_registry *Genode::ep_sd_registry()
|
||||
{
|
||||
static Genode::Ep_socket_descriptor_registry registry;
|
||||
return ®istry;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** Ipc_ostream **
|
||||
*****************/
|
||||
@ -94,7 +101,7 @@ void Ipc_istream::_wait()
|
||||
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
|
||||
:
|
||||
Ipc_unmarshaller(rcv_msg->buf, rcv_msg->size()),
|
||||
Native_capability(Dst(lx_gettid(), -1), 0),
|
||||
Native_capability(Dst(-1), 0),
|
||||
_rcv_msg(rcv_msg)
|
||||
{ }
|
||||
|
||||
@ -123,10 +130,9 @@ void Ipc_client::_prepare_next_call()
|
||||
|
||||
void Ipc_client::_call()
|
||||
{
|
||||
if (Ipc_ostream::_dst.valid()) {
|
||||
_snd_msg->used_size(_write_offset);
|
||||
lx_call(Ipc_ostream::_dst.dst().tid, *_snd_msg, *_rcv_msg);
|
||||
}
|
||||
if (Ipc_ostream::_dst.valid())
|
||||
lx_call(Ipc_ostream::_dst.dst().socket, *_snd_msg, _write_offset, *_rcv_msg);
|
||||
|
||||
_prepare_next_call();
|
||||
}
|
||||
|
||||
@ -165,6 +171,15 @@ void Ipc_server::_wait()
|
||||
{
|
||||
_reply_needed = true;
|
||||
|
||||
/*
|
||||
* Block infinitely if called from the main thread. This may happen if the
|
||||
* main thread calls 'sleep_forever()'.
|
||||
*/
|
||||
if (!Thread_base::myself()) {
|
||||
struct timespec ts = { 1000, 0 };
|
||||
for (;;) lx_nanosleep(&ts, 0);
|
||||
}
|
||||
|
||||
try {
|
||||
int const reply_socket = lx_wait(_rcv_cs, *_rcv_msg);
|
||||
|
||||
@ -176,11 +191,8 @@ void Ipc_server::_wait()
|
||||
* object, the 'local_name' is meaningless.
|
||||
*/
|
||||
enum { DUMMY_LOCAL_NAME = -1 };
|
||||
|
||||
typedef Native_capability::Dst Dst;
|
||||
enum { DUMMY_TID = -1 };
|
||||
Dst dst(DUMMY_TID, reply_socket);
|
||||
Ipc_ostream::_dst = Native_capability(dst, DUMMY_LOCAL_NAME);
|
||||
Ipc_ostream::_dst = Native_capability(Dst(reply_socket), DUMMY_LOCAL_NAME);
|
||||
|
||||
_prepare_next_reply_wait();
|
||||
} catch (Blocking_canceled) { }
|
||||
@ -190,9 +202,8 @@ void Ipc_server::_wait()
|
||||
void Ipc_server::_reply()
|
||||
{
|
||||
try {
|
||||
_snd_msg->used_size(_write_offset);
|
||||
lx_reply(Ipc_ostream::_dst.dst().socket, *_snd_msg);
|
||||
} catch (Ipc_error) { }
|
||||
lx_reply(Ipc_ostream::_dst.dst().socket, *_snd_msg, _write_offset); }
|
||||
catch (Ipc_error) { }
|
||||
|
||||
_prepare_next_reply_wait();
|
||||
}
|
||||
@ -201,10 +212,8 @@ void Ipc_server::_reply()
|
||||
void Ipc_server::_reply_wait()
|
||||
{
|
||||
/* when first called, there was no request yet */
|
||||
if (_reply_needed) {
|
||||
_snd_msg->used_size(_write_offset);
|
||||
lx_reply(Ipc_ostream::_dst.dst().socket, *_snd_msg);
|
||||
}
|
||||
if (_reply_needed)
|
||||
lx_reply(Ipc_ostream::_dst.dst().socket, *_snd_msg, _write_offset);
|
||||
|
||||
_wait();
|
||||
}
|
||||
@ -229,19 +238,14 @@ Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg)
|
||||
throw Ipc_server_multiple_instance();
|
||||
}
|
||||
|
||||
_rcv_cs = lx_server_socket(Thread_base::myself());
|
||||
if (_rcv_cs < 0) {
|
||||
PRAW("lx_server_socket failed (error %d)", _rcv_cs);
|
||||
struct Ipc_socket_creation_failed { };
|
||||
throw Ipc_socket_creation_failed();
|
||||
}
|
||||
_rcv_cs = lx_server_socket_pair(Thread_base::myself());
|
||||
|
||||
if (thread)
|
||||
thread->tid().is_ipc_server = true;
|
||||
|
||||
/* override capability initialization performed by 'Ipc_istream' */
|
||||
*static_cast<Native_capability *>(this) =
|
||||
Native_capability(Native_capability::Dst(lx_gettid(), _rcv_cs), 0);
|
||||
Native_capability(Native_capability::Dst(_rcv_cs.client_sd), 0);
|
||||
|
||||
_prepare_next_reply_wait();
|
||||
}
|
||||
|
@ -30,18 +30,22 @@ Dataspace_capability Process::_dynamic_linker_cap;
|
||||
/**
|
||||
* Argument frame for passing 'execve' paremeters through 'clone'
|
||||
*/
|
||||
struct execve_args {
|
||||
const char *filename;
|
||||
char *const*argv;
|
||||
char *const*envp;
|
||||
struct Execve_args
|
||||
{
|
||||
const char *filename;
|
||||
char *const *argv;
|
||||
char *const *envp;
|
||||
int parent_sd;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Startup code of the new child process
|
||||
*/
|
||||
static int _exec_child(struct execve_args *arg)
|
||||
static int _exec_child(Execve_args *arg)
|
||||
{
|
||||
lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE);
|
||||
|
||||
return lx_execve(arg->filename, arg->argv, arg->envp);
|
||||
}
|
||||
|
||||
@ -67,6 +71,7 @@ static const char *get_env(const char *key)
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for dynamic ELF header
|
||||
*/
|
||||
@ -115,8 +120,6 @@ const char *Process::_priv_pd_args(Parent_capability parent_cap,
|
||||
/* pass parent capability as environment variable to the child */
|
||||
enum { ENV_STR_LEN = 256 };
|
||||
static char envbuf[5][ENV_STR_LEN];
|
||||
Genode::snprintf(envbuf[0], ENV_STR_LEN, "parent_tid=%ld",
|
||||
parent_cap.dst().tid);
|
||||
Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu",
|
||||
parent_cap.local_name());
|
||||
Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s",
|
||||
@ -162,16 +165,11 @@ const char *Process::_priv_pd_args(Parent_capability parent_cap,
|
||||
enum { STACK_SIZE = 4096 };
|
||||
static char stack[STACK_SIZE]; /* initial stack used by the child until
|
||||
calling 'execve' */
|
||||
|
||||
/*
|
||||
* Argument frame as passed to 'clone'. Because, we can only pass a single
|
||||
* pointer, all arguments are embedded within the 'execve_args' struct.
|
||||
*/
|
||||
struct execve_args arg = {
|
||||
fname.buf,
|
||||
argv,
|
||||
env
|
||||
};
|
||||
Execve_args arg = { fname.buf, argv, env, parent_cap.dst().socket };
|
||||
|
||||
pid_t pid = lx_create_process((int (*)(void *))_exec_child,
|
||||
stack + STACK_SIZE - sizeof(umword_t), &arg);
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* \brief Linux socket utilities
|
||||
* \author Christian Helmuth
|
||||
* \author Norman Feske
|
||||
* \date 2012-01-17
|
||||
*
|
||||
* We create one socket under lx_rpath() for each 'Ipc_server'. The naming is
|
||||
@ -39,31 +40,154 @@ extern "C" int raw_write_str(const char *str);
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Create socket address for server entrypoint atthread ID
|
||||
/******************************
|
||||
** File-descriptor registry **
|
||||
******************************/
|
||||
|
||||
/*
|
||||
* We use the name of the Unix-domain socket as key to uniquely identify
|
||||
* entrypoints. When receiving a socket descriptor as IPC payload, we first
|
||||
* lookup the corresponding entrypoint ID. If we already possess a socket
|
||||
* descriptor pointing to the same entrypoint, we close the received one and
|
||||
* use the already known descriptor instead.
|
||||
*/
|
||||
static void lx_create_server_addr(sockaddr_un *addr, long thread_id)
|
||||
|
||||
namespace Genode
|
||||
{
|
||||
addr->sun_family = AF_UNIX;
|
||||
Genode::snprintf(addr->sun_path, sizeof(addr->sun_path), "%s/ep-%ld",
|
||||
lx_rpath(), thread_id);
|
||||
template <unsigned MAX_FDS>
|
||||
class Socket_descriptor_registry;
|
||||
|
||||
typedef Socket_descriptor_registry<100> Ep_socket_descriptor_registry;
|
||||
|
||||
/**
|
||||
* Return singleton instance of registry for tracking entrypoint sockets
|
||||
*/
|
||||
Ep_socket_descriptor_registry *ep_sd_registry();
|
||||
}
|
||||
|
||||
|
||||
template <unsigned MAX_FDS>
|
||||
class Genode::Socket_descriptor_registry
|
||||
{
|
||||
public:
|
||||
|
||||
class Limit_reached { };
|
||||
class Aliased_global_id { };
|
||||
|
||||
private:
|
||||
|
||||
struct Entry
|
||||
{
|
||||
int fd;
|
||||
int global_id;
|
||||
|
||||
/**
|
||||
* Default constructor creates empty entry
|
||||
*/
|
||||
Entry() : fd(-1), global_id(-1) { }
|
||||
|
||||
Entry(int fd, int global_id) : fd(fd), global_id(global_id) { }
|
||||
|
||||
bool is_free() const { return fd == -1; }
|
||||
};
|
||||
|
||||
Entry _entries[MAX_FDS];
|
||||
|
||||
Genode::Lock mutable _lock;
|
||||
|
||||
Entry &_find_free_entry()
|
||||
{
|
||||
for (unsigned i = 0; i < MAX_FDS; i++)
|
||||
if (_entries[i].is_free())
|
||||
return _entries[i];
|
||||
|
||||
throw Limit_reached();
|
||||
}
|
||||
|
||||
bool _is_registered(int global_id) const
|
||||
{
|
||||
for (unsigned i = 0; i < MAX_FDS; i++)
|
||||
if (_entries[i].global_id == global_id)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Register association of socket descriptor and its corresponding ID
|
||||
*
|
||||
* \throw Limit_reached
|
||||
* \throw Aliased_global_id if global ID is already registered
|
||||
*/
|
||||
void associate(int sd, int global_id)
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
/* ignore invalid capabilities */
|
||||
if (sd == -1 || global_id == -1)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check for potential aliasing
|
||||
*
|
||||
* We allow any global ID to be present in the registry only once.
|
||||
*/
|
||||
if (_is_registered(global_id)) {
|
||||
PRAW("attempted to register global ID %d twice", global_id);
|
||||
throw Aliased_global_id();
|
||||
}
|
||||
|
||||
Entry &entry = _find_free_entry();
|
||||
entry = Entry(sd, global_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup file descriptor that belongs to specified global ID
|
||||
*
|
||||
* \return file descriptor or -1 if lookup failed
|
||||
*/
|
||||
int lookup_fd_by_global_id(int global_id) const
|
||||
{
|
||||
Genode::Lock::Guard guard(_lock);
|
||||
|
||||
for (unsigned i = 0; i < MAX_FDS; i++)
|
||||
if (_entries[i].global_id == global_id)
|
||||
return _entries[i].fd;
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Return thread ID to which the given socket is bound
|
||||
*
|
||||
* \return -1 if the socket is not bound to an entrypoint
|
||||
* Utility: Create socket address for server entrypoint at thread ID
|
||||
*/
|
||||
static int lookup_tid_by_socket(int sd)
|
||||
struct Uds_addr : sockaddr_un
|
||||
{
|
||||
Uds_addr(long thread_id)
|
||||
{
|
||||
sun_family = AF_UNIX;
|
||||
Genode::snprintf(sun_path, sizeof(sun_path), "%s/ep-%ld",
|
||||
lx_rpath(), thread_id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Return thread ID to which the given socket is directed to
|
||||
*
|
||||
* \return -1 if the socket is pointing to a valid entrypoint
|
||||
*/
|
||||
static int lookup_tid_by_client_socket(int sd)
|
||||
{
|
||||
sockaddr_un name;
|
||||
socklen_t name_len = sizeof(name);
|
||||
int ret = lx_getsockname(sd, (sockaddr *)&name, &name_len);
|
||||
if (ret < 0) {
|
||||
PRAW("Error: lx_getsockname returned %d", ret);
|
||||
int ret = lx_getpeername(sd, (sockaddr *)&name, &name_len);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The socket name has the form '<rpath>/ep-<tid>'. Hence, to determine the
|
||||
@ -115,18 +239,10 @@ namespace {
|
||||
|
||||
public:
|
||||
|
||||
Message(long server_thread_id = -1) : _num_sds(0)
|
||||
Message(void *buffer, size_t buffer_len) : _num_sds(0)
|
||||
{
|
||||
memset(&_msg, 0, sizeof(_msg));
|
||||
|
||||
if (server_thread_id != -1) {
|
||||
/* initialize receiver */
|
||||
lx_create_server_addr(&_addr, server_thread_id);
|
||||
|
||||
_msg.msg_name = &_addr;
|
||||
_msg.msg_namelen = sizeof(_addr);
|
||||
}
|
||||
|
||||
/* initialize control message */
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
@ -138,12 +254,7 @@ namespace {
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
|
||||
}
|
||||
|
||||
msghdr * msg() { return &_msg; }
|
||||
|
||||
void buffer(void *buffer, size_t buffer_len)
|
||||
{
|
||||
/* initialize iovec */
|
||||
_msg.msg_iov = &_iovec;
|
||||
_msg.msg_iovlen = 1;
|
||||
@ -152,6 +263,8 @@ namespace {
|
||||
_iovec.iov_len = buffer_len;
|
||||
}
|
||||
|
||||
msghdr * msg() { return &_msg; }
|
||||
|
||||
void marshal_socket(int sd)
|
||||
{
|
||||
*((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + _num_sds) = sd;
|
||||
@ -170,17 +283,7 @@ namespace {
|
||||
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
|
||||
}
|
||||
|
||||
int unmarshal_socket()
|
||||
{
|
||||
int ret = *((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + _num_sds);
|
||||
|
||||
_num_sds++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XXX only for debugging */
|
||||
int socket_at_index(int index)
|
||||
int socket_at_index(int index) const
|
||||
{
|
||||
return *((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + index);
|
||||
}
|
||||
@ -200,45 +303,107 @@ namespace {
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Get server socket for given thread
|
||||
* Utility: Extract socket desriptors from SCM message into 'Genode::Msgbuf'
|
||||
*/
|
||||
static int lx_server_socket(Genode::Thread_base *thread)
|
||||
static void extract_sds_from_message(unsigned start_index, Message const &msg,
|
||||
Genode::Msgbuf_base &buf)
|
||||
{
|
||||
int sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (sd < 0)
|
||||
return sd;
|
||||
buf.reset_caps();
|
||||
|
||||
/* start at offset 1 to skip the reply channel */
|
||||
for (unsigned i = start_index; i < msg.num_sockets(); i++) {
|
||||
|
||||
int const sd = msg.socket_at_index(i);
|
||||
int const id = lookup_tid_by_client_socket(sd);
|
||||
int const existing_sd = Genode::ep_sd_registry()->lookup_fd_by_global_id(id);
|
||||
|
||||
if (existing_sd >= 0) {
|
||||
lx_close(sd);
|
||||
buf.append_cap(existing_sd);
|
||||
} else {
|
||||
Genode::ep_sd_registry()->associate(sd, id);
|
||||
buf.append_cap(sd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Server_socket_failed { };
|
||||
class Client_socket_failed { };
|
||||
class Bind_failed { };
|
||||
class Connect_failed { };
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Create named socket pair for given thread
|
||||
*
|
||||
* \throw Server_socket_failed
|
||||
* \throw Client_socket_failed
|
||||
* \throw Bind_failed
|
||||
* \throw Connect_failed
|
||||
*/
|
||||
static Genode::Native_connection_state lx_server_socket_pair(Genode::Thread_base *thread)
|
||||
{
|
||||
Genode::Native_connection_state ncs;
|
||||
|
||||
/*
|
||||
* Main thread uses 'Ipc_server' for 'sleep_forever()' only. No need
|
||||
* for binding.
|
||||
* Main thread uses 'Ipc_server' for 'sleep_forever()' only. No need for
|
||||
* binding.
|
||||
*/
|
||||
if (!thread)
|
||||
return sd;
|
||||
return ncs;
|
||||
|
||||
sockaddr_un addr;
|
||||
lx_create_server_addr(&addr, thread->tid().tid);
|
||||
Uds_addr addr(thread->tid().tid);
|
||||
|
||||
/*
|
||||
* Create server-side socket
|
||||
*/
|
||||
ncs.server_sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (ncs.server_sd < 0) {
|
||||
PRAW("Error: Could not create server-side socket (ret=%d)", ncs.server_sd);
|
||||
throw Server_socket_failed();
|
||||
}
|
||||
|
||||
/* make sure bind succeeds */
|
||||
lx_unlink(addr.sun_path);
|
||||
|
||||
int const bind_ret = lx_bind(sd, (sockaddr *)&addr, sizeof(addr));
|
||||
if (bind_ret < 0)
|
||||
return bind_ret;
|
||||
int const bind_ret = lx_bind(ncs.server_sd, (sockaddr *)&addr, sizeof(addr));
|
||||
if (bind_ret < 0) {
|
||||
PRAW("Error: Could not bind server socket (ret=%d)", bind_ret);
|
||||
throw Bind_failed();
|
||||
}
|
||||
|
||||
return sd;
|
||||
/*
|
||||
* Create client-side socket
|
||||
*/
|
||||
ncs.client_sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (ncs.client_sd < 0) {
|
||||
PRAW("Error: Could not create client-side socket (ret=%d)", ncs.client_sd);
|
||||
throw Client_socket_failed();
|
||||
}
|
||||
|
||||
int const conn_ret = lx_connect(ncs.client_sd, (sockaddr *)&addr, sizeof(addr));
|
||||
if (conn_ret < 0) {
|
||||
PRAW("Error: Could not connect client-side socket (ret=%d)", conn_ret);
|
||||
throw Connect_failed();
|
||||
}
|
||||
|
||||
int const tid = lookup_tid_by_client_socket(ncs.client_sd);
|
||||
Genode::ep_sd_registry()->associate(ncs.client_sd, tid);
|
||||
|
||||
return ncs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility: Send request to server and wait for reply
|
||||
*/
|
||||
static void lx_call(long thread_id,
|
||||
Genode::Msgbuf_base &send_msgbuf,
|
||||
static void lx_call(int dst_sd,
|
||||
Genode::Msgbuf_base &send_msgbuf, size_t send_msg_len,
|
||||
Genode::Msgbuf_base &recv_msgbuf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
Message send_msg(thread_id);
|
||||
Message send_msg(send_msgbuf.buf, send_msg_len);
|
||||
|
||||
/* create reply channel */
|
||||
enum { LOCAL_SOCKET = 0, REMOTE_SOCKET = 1 };
|
||||
@ -247,7 +412,6 @@ static void lx_call(long thread_id,
|
||||
ret = lx_socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, reply_channel);
|
||||
if (ret < 0) {
|
||||
PRAW("lx_socketpair failed with %d", ret);
|
||||
/* XXX */ wait_for_continue();
|
||||
throw Genode::Ipc_error();
|
||||
}
|
||||
|
||||
@ -257,54 +421,27 @@ static void lx_call(long thread_id,
|
||||
send_msg.marshal_socket(reply_channel[REMOTE_SOCKET]);
|
||||
|
||||
/* marshal capabilities contained in 'send_msgbuf' */
|
||||
if (send_msgbuf.used_caps() > 0)
|
||||
PRAW("lx_call: marshal %d caps:", send_msgbuf.used_caps());
|
||||
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++)
|
||||
send_msg.marshal_socket(send_msgbuf.cap(i));
|
||||
|
||||
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++) {
|
||||
PRAW(" sd[%d]: %d", i, send_msgbuf.cap(i));
|
||||
send_msg.marshal_socket(lx_dup(send_msgbuf.cap(i)));
|
||||
}
|
||||
|
||||
send_msg.buffer(send_msgbuf.buf, send_msgbuf.used_size());
|
||||
|
||||
/*
|
||||
* The socket argument is not actually used. It just needs to match the
|
||||
* socket type.
|
||||
*/
|
||||
ret = lx_sendmsg(reply_channel[LOCAL_SOCKET], send_msg.msg(), 0);
|
||||
ret = lx_sendmsg(dst_sd, send_msg.msg(), 0);
|
||||
if (ret < 0) {
|
||||
PRAW("lx_sendmsg failed with %d in lx_call()", ret);
|
||||
/* XXX */ wait_for_continue();
|
||||
throw Genode::Ipc_error();
|
||||
}
|
||||
|
||||
Message recv_msg;
|
||||
recv_msg.accept_sockets(Message::MAX_SDS_PER_MSG);
|
||||
/* receive reply */
|
||||
|
||||
recv_msg.buffer(recv_msgbuf.buf, recv_msgbuf.size());
|
||||
Message recv_msg(recv_msgbuf.buf, recv_msgbuf.size());
|
||||
recv_msg.accept_sockets(Message::MAX_SDS_PER_MSG);
|
||||
|
||||
ret = lx_recvmsg(reply_channel[LOCAL_SOCKET], recv_msg.msg(), 0);
|
||||
if (ret < 0) {
|
||||
PRAW("lx_recvmsg failed with %d in lx_call()", ret);
|
||||
/* XXX */ wait_for_continue();
|
||||
throw Genode::Ipc_error();
|
||||
}
|
||||
|
||||
/*
|
||||
* 'lx_recvmsg()' returns the number of bytes received. Remember this value
|
||||
* in 'Genode::Msgbuf_base'
|
||||
*
|
||||
* XXX revisit whether we really need this information
|
||||
*/
|
||||
recv_msgbuf.used_size(ret);
|
||||
|
||||
if (recv_msg.num_sockets() > 0) {
|
||||
PRAW("lx_call: got %d sockets in reply", recv_msg.num_sockets());
|
||||
for (unsigned i = 0; i < recv_msg.num_sockets(); i++) {
|
||||
PRAW(" sd[%d]: %d", i, recv_msg.socket_at_index(i));
|
||||
lx_close(recv_msg.socket_at_index(i));
|
||||
}
|
||||
}
|
||||
extract_sds_from_message(0, recv_msg, recv_msgbuf);
|
||||
|
||||
/* destroy reply channel */
|
||||
lx_close(reply_channel[LOCAL_SOCKET]);
|
||||
@ -320,44 +457,19 @@ static void lx_call(long thread_id,
|
||||
static int lx_wait(Genode::Native_connection_state &cs,
|
||||
Genode::Msgbuf_base &recv_msgbuf)
|
||||
{
|
||||
Message msg;
|
||||
Message msg(recv_msgbuf.buf, recv_msgbuf.size());
|
||||
|
||||
msg.accept_sockets(Message::MAX_SDS_PER_MSG);
|
||||
msg.buffer(recv_msgbuf.buf, recv_msgbuf.size());
|
||||
|
||||
int ret = lx_recvmsg(cs, msg.msg(), 0);
|
||||
int ret = lx_recvmsg(cs.server_sd, msg.msg(), 0);
|
||||
if (ret < 0) {
|
||||
PRAW("lx_recvmsg failed with %d in lx_wait()", ret);
|
||||
/* XXX */ wait_for_continue();
|
||||
PRAW("lx_recvmsg failed with %d in lx_wait(), sd=%d", ret, cs.server_sd);
|
||||
throw Genode::Ipc_error();
|
||||
}
|
||||
|
||||
/*
|
||||
* 'lx_recvmsg()' returned message size, keep it in 'recv_msgbuf'.
|
||||
*
|
||||
* XXX revisit whether this information is actually needed.
|
||||
*/
|
||||
recv_msgbuf.used_size(ret);
|
||||
int const reply_socket = msg.socket_at_index(0);
|
||||
|
||||
|
||||
if (msg.num_sockets() > 1) {
|
||||
PRAW("lx_wait: got %d sockets from wait", msg.num_sockets());
|
||||
for (unsigned i = 0; i < msg.num_sockets(); i++) {
|
||||
|
||||
int tid = (i > 0) ? lookup_tid_by_socket(msg.socket_at_index(i)) : -1;
|
||||
PRAW(" sd[%d]: %d, tid=%d", i, msg.socket_at_index(i), tid);
|
||||
|
||||
/* don't close reply channel */
|
||||
if (i > 0)
|
||||
lx_close(msg.socket_at_index(i));
|
||||
}
|
||||
}
|
||||
|
||||
int reply_socket = msg.unmarshal_socket();
|
||||
|
||||
/*
|
||||
* Copy-out additional sds from msg to recv_msgbuf
|
||||
*/
|
||||
extract_sds_from_message(1, msg, recv_msgbuf);
|
||||
|
||||
return reply_socket;
|
||||
}
|
||||
@ -367,11 +479,16 @@ static int lx_wait(Genode::Native_connection_state &cs,
|
||||
* Utility: Send reply to client
|
||||
*/
|
||||
static void lx_reply(int reply_socket,
|
||||
Genode::Msgbuf_base &send_msgbuf)
|
||||
Genode::Msgbuf_base &send_msgbuf,
|
||||
size_t msg_len)
|
||||
{
|
||||
Message msg;
|
||||
Message msg(send_msgbuf.buf, msg_len);
|
||||
|
||||
msg.buffer(send_msgbuf.buf, send_msgbuf.used_size());
|
||||
/*
|
||||
* Marshall capabilities to be transferred to the client
|
||||
*/
|
||||
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++)
|
||||
msg.marshal_socket(send_msgbuf.cap(i));
|
||||
|
||||
int ret = lx_sendmsg(reply_socket, msg.msg(), 0);
|
||||
if (ret < 0)
|
||||
|
@ -70,6 +70,12 @@ inline int lx_dup(int fd)
|
||||
}
|
||||
|
||||
|
||||
inline int lx_dup2(int fd, int to)
|
||||
{
|
||||
return lx_syscall(SYS_dup2, fd, to);
|
||||
}
|
||||
|
||||
|
||||
inline int lx_unlink(const char *fname)
|
||||
{
|
||||
return lx_syscall(SYS_unlink, fname);
|
||||
@ -113,6 +119,14 @@ inline int lx_bind(int sockfd, const struct sockaddr *addr,
|
||||
}
|
||||
|
||||
|
||||
inline int lx_connect(int sockfd, const struct sockaddr *serv_addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
unsigned long args[3] = { sockfd, (unsigned long)serv_addr, addrlen };
|
||||
return lx_socketcall(SYS_CONNECT, args);
|
||||
}
|
||||
|
||||
|
||||
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
|
||||
{
|
||||
unsigned long args[3] = { sockfd, (unsigned long)msg, flags };
|
||||
@ -127,10 +141,10 @@ inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
|
||||
}
|
||||
|
||||
|
||||
inline int lx_getsockname(int sockfd, struct sockaddr *name, socklen_t *namelen)
|
||||
inline int lx_getpeername(int sockfd, struct sockaddr *name, socklen_t *namelen)
|
||||
{
|
||||
unsigned long args[3] = { sockfd, (unsigned long)name, (unsigned long)namelen };
|
||||
return lx_socketcall(SYS_GETSOCKNAME, args);
|
||||
return lx_socketcall(SYS_GETPEERNAME, args);
|
||||
}
|
||||
|
||||
#else
|
||||
@ -140,9 +154,23 @@ inline int lx_socket(int domain, int type, int protocol)
|
||||
return lx_syscall(SYS_socket, domain, type, protocol);
|
||||
}
|
||||
|
||||
inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
|
||||
inline int lx_bind(int sockfd, const struct sockaddr *addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
return lx_syscall(SYS_getsockname, s, name, namelen);
|
||||
return lx_syscall(SYS_bind, sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
|
||||
inline int lx_connect(int sockfd, const struct sockaddr *serv_addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
return lx_syscall(SYS_connect, sockfd, serv_addr, addrlen);
|
||||
}
|
||||
|
||||
|
||||
inline int lx_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
|
||||
{
|
||||
return lx_syscall(SYS_getpeername, s, name, namelen);
|
||||
}
|
||||
|
||||
/* TODO add missing socket system calls */
|
||||
|
Loading…
Reference in New Issue
Block a user