mirror of
https://github.com/mmueller41/genode.git
synced 2026-01-22 04:52:56 +01:00
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:
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user