Merge of initial SCM rights code

This patch, which was originally created by Christian Helmuth,
represents the first step towards using SCM rights as capability
mechanism on Linux. It employs the SCM rights mechanism for transmitting
a reply capability to the server as argument of each IPC call. The
server will then send its respond to this reply file descriptor. This
way, the reply channel does not need to be globally visible anymore.
This commit is contained in:
Norman Feske 2012-07-18 12:34:11 +02:00
parent 76b60566b7
commit c09cd2d1a7
4 changed files with 255 additions and 189 deletions

View File

@ -64,6 +64,7 @@ namespace Genode {
/**
* Native thread contains more thread-local data than just the ID
*
* FIXME doc
* A thread needs two sockets as it may be a server that depends on another
* service during request processing. If the server socket would be used for
* the client call, the server thread may be unblocked by further requests
@ -73,8 +74,7 @@ namespace Genode {
*/
struct Native_thread : Native_thread_id
{
int client; /* socket used as IPC client */
int server; /* socket used as IPC server */
int socket; /* server-entrypoint socket */
/**
* Opaque pointer to additional thread-specific meta data
@ -85,7 +85,7 @@ namespace Genode {
*/
Thread_meta_data *meta_data;
Native_thread() : client(-1), server(-1), meta_data(0) { }
Native_thread() : socket(-1), meta_data(0) { }
};
inline bool operator == (Native_thread_id t1, Native_thread_id t2) {
@ -107,7 +107,46 @@ namespace Genode {
typedef struct { } Native_utcb;
typedef Native_capability_tpl<Cap_dst_policy> Native_capability;
typedef int Native_connection_state; /* socket descriptor */
class Native_connection_state
{
public:
struct Reply_socket_error { };
private:
int _socket; /* server-entrypoint socket */
int _reply_socket; /* reply socket */
public:
Native_connection_state() : _socket(-1), _reply_socket(-1) { }
void socket(int socket) { _socket = socket; }
int socket() const { return _socket; }
/*
* FIXME Check for unsupported usage pattern: Reply sockets should
* only be set once and read once.
*/
void reply_socket(int socket)
{
if (_reply_socket != -1) throw Reply_socket_error();
_reply_socket = socket;
}
int reply_socket()
{
if (_reply_socket == -1) throw Reply_socket_error();
int s = _reply_socket;
_reply_socket = -1;
return s;
}
};
struct Native_config
{
@ -125,7 +164,6 @@ namespace Genode {
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
};
}
#endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */

View File

@ -35,6 +35,7 @@
/* Linux includes */
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <errno.h>
@ -93,7 +94,7 @@ void Ipc_istream::_wait()
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
: Ipc_unmarshaller(rcv_msg->buf, rcv_msg->size()),
Native_capability(lx_gettid(), 0),
_rcv_msg(rcv_msg), _rcv_cs(-1)
_rcv_msg(rcv_msg)
{ }
@ -122,10 +123,9 @@ void Ipc_client::_prepare_next_call()
void Ipc_client::_call()
{
if (Ipc_ostream::_dst.valid()) {
lx_send_to(_rcv_cs, Ipc_ostream::_dst.dst(), "server",
_snd_msg->buf, _write_offset);
lx_recv_from(_rcv_cs, _rcv_msg->buf, _rcv_msg->size());
lx_call(Ipc_ostream::_dst.dst(),
_snd_msg->buf, _write_offset,
_rcv_msg->buf, _rcv_msg->size());
}
_prepare_next_call();
}
@ -135,8 +135,6 @@ Ipc_client::Ipc_client(Native_capability const &srv,
Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg)
: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0)
{
_rcv_cs = lx_client_socket(Thread_base::myself());
_prepare_next_call();
}
@ -171,7 +169,7 @@ void Ipc_server::_wait()
{
/* wait for new server request */
try {
lx_recv_from(_rcv_cs, _rcv_msg->buf, _rcv_msg->size());
lx_wait(_rcv_cs, _rcv_msg->buf, _rcv_msg->size());
} catch (Blocking_canceled) { }
/* now we have a request to reply, determine reply destination */
@ -183,8 +181,7 @@ void Ipc_server::_wait()
void Ipc_server::_reply()
{
try {
lx_send_to(_rcv_cs, Ipc_ostream::_dst.dst(), "client",
_snd_msg->buf, _write_offset);
lx_reply(_rcv_cs, _snd_msg->buf, _write_offset);
} catch (Ipc_error) { }
_prepare_next_reply_wait();
@ -195,8 +192,7 @@ void Ipc_server::_reply_wait()
{
/* when first called, there was no request yet */
if (_reply_needed)
lx_send_to(_rcv_cs, Ipc_ostream::_dst.dst(), "client",
_snd_msg->buf, _write_offset);
lx_reply(_rcv_cs, _snd_msg->buf, _write_offset);
_wait();
}
@ -206,7 +202,7 @@ Ipc_server::Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg)
: Ipc_istream(rcv_msg),
Ipc_ostream(Native_capability(), snd_msg), _reply_needed(false)
{
_rcv_cs = lx_server_socket(Thread_base::myself());
_rcv_cs.socket(lx_server_socket(Thread_base::myself()));
_prepare_next_reply_wait();
}

View File

@ -45,63 +45,100 @@ extern "C" int raw_write_str(const char *str);
/**
* Utility: Create socket address for thread ID and role (client/server)
* Utility: Create socket address for server entrypoint atthread ID
*/
static void lx_create_sockaddr(sockaddr_un *addr, long thread_id, char const *role)
static void lx_create_server_addr(sockaddr_un *addr, long thread_id)
{
addr->sun_family = AF_UNIX;
Genode::snprintf(addr->sun_path, sizeof(addr->sun_path), "%s/ep-%ld-%s",
lx_rpath(), thread_id, role);
Genode::snprintf(addr->sun_path, sizeof(addr->sun_path), "%s/ep-%ld",
lx_rpath(), thread_id);
}
/**
* Utility: Create a socket descriptor and file for given thread and role
*/
static int lx_create_socket(long thread_id, char const *role)
{
int sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sd < 0) return -1;
namespace {
sockaddr_un addr;
lx_create_sockaddr(&addr, thread_id, role);
/**
* Message object encapsulating data for sendmsg/recvmsg
*/
struct Message
{
private:
/* make sure bind succeeds */
lx_unlink(addr.sun_path);
msghdr _msg;
sockaddr_un _addr;
iovec _iovec;
char _cmsg_buf[CMSG_SPACE(sizeof(int))];
if (lx_bind(sd, (sockaddr *)&addr, sizeof(addr)) < 0)
return -2;
public:
return sd;
}
Message(long server_thread_id = -1)
{
memset(&_msg, 0, sizeof(_msg));
if (server_thread_id != -1) {
/* initialize receiver */
lx_create_server_addr(&_addr, server_thread_id);
/**
* Utility: Unlink socket file and close descriptor
*
* XXX Currently, socket destruction is missing. The client socket could be
* used from multiple Ipc_client objects. A safe destruction would need
* reference counting.
*/
//static void lx_destroy_socket(int sd, long thread_id, char const *role)
//{
// sockaddr_un addr;
// lx_create_sockaddr(&addr, thread_id, role);
//
// lx_unlink(addr.sun_path);
// lx_close(sd);
//}
_msg.msg_name = &_addr;
_msg.msg_namelen = sizeof(_addr);
}
}
msghdr * msg() { return &_msg; }
/**
* Get client-socket descriptor for main thread
*/
static int lx_main_client_socket()
{
static int sd = lx_create_socket(lx_gettid(), "client");
void buffer(void *buffer, size_t buffer_len)
{
/* initialize iovec */
_msg.msg_iov = &_iovec;
_msg.msg_iovlen = 1;
return sd;
}
_iovec.iov_base = buffer;
_iovec.iov_len = buffer_len;
}
/**
* Prepare slot for socket sending/reception
*
* Note, if this function is not called sockets are not accepted on
* 'recvmsg' and, therefore, do not occupy local file descriptors.
*/
void prepare_reply_socket_slot()
{
/* initialize control message */
struct cmsghdr *cmsg;
_msg.msg_control = _cmsg_buf;
_msg.msg_controllen = sizeof(_cmsg_buf); /* buffer space available */
_msg.msg_flags |= MSG_CMSG_CLOEXEC;
cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
}
void reply_socket(int sd)
{
if (!_msg.msg_control) {
PRAW("reply-socket slot not prepared");
throw Genode::Ipc_error();
}
*(int *)CMSG_DATA((cmsghdr *)_cmsg_buf) = sd;
}
int reply_socket() const
{
if (!_msg.msg_control) {
PRAW("reply-socket slot not prepared");
throw Genode::Ipc_error();
}
return *(int *)CMSG_DATA((cmsghdr *)_cmsg_buf);
}
};
} /* unnamed namespace */
/**
@ -115,59 +152,117 @@ static int lx_server_socket(Genode::Thread_base *thread)
if (!thread)
return lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (thread->tid().server == -1)
thread->tid().server = lx_create_socket(thread->tid().tid, "server");
return thread->tid().server;
if (thread->tid().socket == -1) {
int sd = lx_socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sd < 0) return -1;
sockaddr_un addr;
lx_create_server_addr(&addr, thread->tid().tid);
/* make sure bind succeeds */
lx_unlink(addr.sun_path);
if (lx_bind(sd, (sockaddr *)&addr, sizeof(addr)) < 0)
return -2;
thread->tid().socket = sd;
}
return thread->tid().socket;
}
/**
* Utility: Get client socket for given thread
* Utility: Send request to server and wait for reply
*/
static int lx_client_socket(Genode::Thread_base *thread)
static void lx_call(long thread_id,
void *send_buf, Genode::size_t send_buf_len,
void *recv_buf, Genode::size_t recv_buf_len)
{
if (!thread) return lx_main_client_socket();
int ret;
if (thread->tid().client == -1)
thread->tid().client = lx_create_socket(thread->tid().tid, "client");
return thread->tid().client;
}
Message send_msg(thread_id);
/* create reply channel */
enum { LOCAL_SOCKET = 0, REMOTE_SOCKET = 1 };
int reply_channel[2];
/**
* Utility: Send message to thread via given socket descriptor
*/
static void lx_send_to(int sd, long thread_id, char const *target_role,
void *msg, Genode::size_t msg_len)
{
sockaddr_un addr;
lx_create_sockaddr(&addr, thread_id, target_role);
int res = lx_sendto(sd, msg, msg_len, 0, (sockaddr *)&addr, sizeof(addr));
if (res < 0) {
PRAW("Send error: %d with %s in %d", res, addr.sun_path, lx_gettid());
wait_for_continue();
ret = lx_socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, reply_channel);
if (ret < 0) {
PRAW("lx_socketpair failed with %d", ret);
throw Genode::Ipc_error();
}
send_msg.prepare_reply_socket_slot();
send_msg.reply_socket(reply_channel[REMOTE_SOCKET]);
send_msg.buffer(send_buf, send_buf_len);
ret = lx_sendmsg(reply_channel[LOCAL_SOCKET], send_msg.msg(), 0);
if (ret < 0) {
PRAW("lx_sendmsg failed with %d in call()", ret);
throw Genode::Ipc_error();
}
Message recv_msg;
recv_msg.buffer(recv_buf, recv_buf_len);
ret = lx_recvmsg(reply_channel[LOCAL_SOCKET], recv_msg.msg(), 0);
if (ret < 0) {
PRAW("lx_recvmsg failed with %d in call()", ret);
throw Genode::Ipc_error();
}
/* destroy reply channel */
lx_close(reply_channel[LOCAL_SOCKET]);
lx_close(reply_channel[REMOTE_SOCKET]);
}
/**
* Utility: Receive message via given socket descriptor
* Utility: Wait for request from client
*/
static void lx_recv_from(int sd, void *buf, Genode::size_t buf_len)
static void lx_wait(Genode::Native_connection_state &cs,
void *buf, Genode::size_t buf_len)
{
socklen_t fromlen;
int res = lx_recvfrom(sd, buf, buf_len, 0, 0, &fromlen);
if (res < 0) {
if ((-res) == EINTR)
throw Genode::Blocking_canceled();
else {
PRAW("Recv error: %d in %d", res, lx_gettid());
wait_for_continue();
throw Genode::Ipc_error();
}
int ret;
Message msg;
msg.prepare_reply_socket_slot();
msg.buffer(buf, buf_len);
ret = lx_recvmsg(cs.socket(), msg.msg(), 0);
if (ret < 0) {
PRAW("lx_recvmsg failed with %d in wait()", ret);
throw Genode::Ipc_error();
}
/* remember socket descriptor for reply */
cs.reply_socket(msg.reply_socket());
}
/**
* Utility: Send reply to client
*/
static void lx_reply(Genode::Native_connection_state &cs,
void *buf, Genode::size_t buf_len)
{
int ret;
int sd = cs.reply_socket();
Message msg;
msg.buffer(buf, buf_len);
ret = lx_sendmsg(sd, msg.msg(), 0);
if (ret < 0) {
PRAW("lx_sendmsg failed with %d in reply()", ret);
throw Genode::Ipc_error();
}
lx_close(sd);
}
#endif /* _PLATFORM__LINUX_SOCKET_H_ */

View File

@ -43,41 +43,38 @@
#include <util/string.h>
/*****************************************
** Functions used by the IPC framework **
*****************************************/
#include <linux/net.h>
extern "C" long lx_syscall(int number, ...);
extern "C" int lx_clone(int (*fn)(void *), void *child_stack,
int flags, void *arg);
inline Genode::uint16_t lx_bswap16(Genode::uint16_t x)
/*****************************************
** General syscalls used by base-linux **
*****************************************/
inline int lx_write(int fd, const void *buf, Genode::size_t count)
{
char v[2] = {
(char)((x & 0xff00) >> 8),
(char)((x & 0x00ff) >> 0),
};
return *(Genode::uint16_t *)v;
return lx_syscall(SYS_write, fd, buf, count);
}
inline Genode::uint32_t lx_bswap32(Genode::uint32_t x)
inline int lx_close(int fd)
{
char v[4] = {
(char)((x & 0xff000000) >> 24),
(char)((x & 0x00ff0000) >> 16),
(char)((x & 0x0000ff00) >> 8),
(char)((x & 0x000000ff) >> 0),
};
return *(Genode::uint32_t *)v;
return lx_syscall(SYS_close, fd);
}
#define lx_htonl(x) lx_bswap32(x)
#define lx_htons(x) lx_bswap16(x)
#define lx_ntohs(x) lx_bswap16(x)
inline int lx_unlink(const char *fname)
{
return lx_syscall(SYS_unlink, fname);
}
/*****************************************
** Functions used by the IPC framework **
*****************************************/
#include <linux/net.h>
#ifdef SYS_socketcall
@ -95,11 +92,10 @@ inline int lx_socket(int domain, int type, int protocol)
}
inline int lx_connect(int sockfd, const struct sockaddr *serv_addr,
socklen_t addrlen)
inline int lx_socketpair(int domain, int type, int protocol, int sd[2])
{
unsigned long args[3] = { sockfd, (unsigned long)serv_addr, addrlen };
return lx_socketcall(SYS_CONNECT, args);
unsigned long args[4] = { domain, type, protocol, (unsigned long)sd };
return lx_socketcall(SYS_SOCKETPAIR, args);
}
@ -111,28 +107,17 @@ inline int lx_bind(int sockfd, const struct sockaddr *addr,
}
inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
inline int lx_sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
unsigned long args[3] = { s, (unsigned long)name, (unsigned long)namelen };
return lx_socketcall(SYS_GETSOCKNAME, args);
unsigned long args[3] = { sockfd, (unsigned long)msg, flags };
return lx_socketcall(SYS_SENDMSG, args);
}
inline ssize_t lx_recvfrom(int s, void *buf, Genode::size_t len, int flags,
struct sockaddr *from, socklen_t *from_len)
inline int lx_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
unsigned long args[6] = { s, (unsigned long)buf, len, flags,
(unsigned long)from, (unsigned long)from_len };
return lx_socketcall(SYS_RECVFROM, args);
}
inline ssize_t lx_sendto(int s, void *buf, Genode::size_t len, int flags,
struct sockaddr *to, socklen_t to_len)
{
unsigned long args[6] = { s, (unsigned long)buf, len, flags,
(unsigned long)to, (unsigned long)to_len };
return lx_socketcall(SYS_SENDTO, args);
unsigned long args[3] = { sockfd, (unsigned long)msg, flags };
return lx_socketcall(SYS_RECVMSG, args);
}
#else
@ -142,55 +127,11 @@ inline int lx_socket(int domain, int type, int protocol)
return lx_syscall(SYS_socket, domain, type, protocol);
}
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_bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen)
{
return lx_syscall(SYS_bind, sockfd, addr, addrlen);
}
inline int lx_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
{
return lx_syscall(SYS_getsockname, s, name, namelen);
}
inline ssize_t lx_recvfrom(int s, void *buf, Genode::size_t len, int flags,
struct sockaddr *from, socklen_t *from_len)
{
return lx_syscall(SYS_recvfrom, s, buf, len, flags, from, from_len);
}
inline ssize_t lx_sendto(int s, void *buf, Genode::size_t len, int flags,
struct sockaddr *to, socklen_t to_len)
{
return lx_syscall(SYS_sendto, s, buf, len, flags, to, to_len);
}
/* TODO add missing socket system calls */
#endif /* SYS_socketcall */
inline int lx_write(int fd, const void *buf, Genode::size_t count)
{
return lx_syscall(SYS_write, fd, buf, count);
}
inline int lx_close(int fd)
{
return lx_syscall(SYS_close, fd);
}
/*******************************************
** Functions used by the process library **
*******************************************/
@ -290,11 +231,6 @@ inline int lx_ftruncate(int fd, unsigned long length)
}
inline int lx_unlink(const char *fname)
{
return lx_syscall(SYS_unlink, fname);
}
/*******************************************************
** Functions used by core's rom-session support code **
*******************************************************/
@ -308,6 +244,7 @@ inline int lx_stat(const char *path, struct stat64 *buf)
#endif
}
/***********************************************************************
** Functions used by thread lib and core's cancel-blocking mechanism **
***********************************************************************/